Merge "Use PropertyAnimator for CUJ_SCREEN_OFF_SHOW_AOD" into udc-dev

This commit is contained in:
Peter Kalauskas
2023-04-11 22:19:57 +00:00
committed by Android (Google) Code Review
7 changed files with 110 additions and 91 deletions

View File

@@ -19,10 +19,12 @@ package com.android.keyguard;
import static java.util.Collections.emptySet;
import android.content.Context;
import android.os.Build;
import android.os.Trace;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.widget.GridLayout;
import com.android.systemui.R;
@@ -117,6 +119,16 @@ public class KeyguardStatusView extends GridLayout {
}
}
@Override
public ViewPropertyAnimator animate() {
if (Build.IS_DEBUGGABLE) {
throw new IllegalArgumentException(
"KeyguardStatusView does not support ViewPropertyAnimator. "
+ "Use PropertyAnimator instead.");
}
return super.animate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Trace.beginSection("KeyguardStatusView#onMeasure");

View File

@@ -18,8 +18,8 @@ package com.android.keyguard;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import android.util.Property;
import android.view.View;
import android.view.ViewPropertyAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.log.LogBuffer;
@@ -34,6 +34,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.google.errorprone.annotations.CompileTimeConstant;
import java.util.function.Consumer;
/**
* Helper class for updating visibility of keyguard views based on keyguard and status bar state.
* This logic is shared by both the keyguard status view and the keyguard user switcher.
@@ -83,47 +85,49 @@ public class KeyguardVisibilityHelper {
boolean keyguardFadingAway,
boolean goingToFullShade,
int oldStatusBarState) {
mView.animate().cancel();
PropertyAnimator.cancelAnimation(mView, AnimatableProperty.ALPHA);
boolean isOccluded = mKeyguardStateController.isOccluded();
mKeyguardViewVisibilityAnimating = false;
if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
&& statusBarState != KEYGUARD) || goingToFullShade) {
mKeyguardViewVisibilityAnimating = true;
mView.animate()
.alpha(0f)
.setStartDelay(0)
.setDuration(160)
.setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(
mAnimateKeyguardStatusViewGoneEndRunnable);
AnimationProperties animProps = new AnimationProperties()
.setCustomInterpolator(View.ALPHA, Interpolators.ALPHA_OUT)
.setAnimationEndAction(mSetGoneEndAction);
if (keyguardFadingAway) {
mView.animate()
.setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
.setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
.start();
animProps
.setDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
.setDuration(mKeyguardStateController.getShortenedFadingAwayDuration());
log("goingToFullShade && keyguardFadingAway");
} else {
animProps.setDelay(0).setDuration(160);
log("goingToFullShade && !keyguardFadingAway");
}
PropertyAnimator.setProperty(
mView, AnimatableProperty.ALPHA, 0f, animProps, true /* animate */);
} else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
mView.setVisibility(View.VISIBLE);
mKeyguardViewVisibilityAnimating = true;
mView.setAlpha(0f);
mView.animate()
.alpha(1f)
.setStartDelay(0)
.setDuration(320)
.setInterpolator(Interpolators.ALPHA_IN)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
PropertyAnimator.setProperty(
mView, AnimatableProperty.ALPHA, 1f,
new AnimationProperties()
.setDelay(0)
.setDuration(320)
.setCustomInterpolator(View.ALPHA, Interpolators.ALPHA_IN)
.setAnimationEndAction(
property -> mSetVisibleEndRunnable.run()),
true /* animate */);
log("keyguardFadingAway transition w/ Y Aniamtion");
} else if (statusBarState == KEYGUARD) {
if (keyguardFadingAway) {
mKeyguardViewVisibilityAnimating = true;
ViewPropertyAnimator animator = mView.animate()
.alpha(0)
.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
.withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable);
AnimationProperties animProps = new AnimationProperties()
.setDelay(0)
.setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_LINEAR_IN)
.setAnimationEndAction(mSetInvisibleEndAction);
if (mAnimateYPos) {
float target = mView.getY() - mView.getHeight() * 0.05f;
int delay = 0;
@@ -135,13 +139,16 @@ public class KeyguardVisibilityHelper {
PropertyAnimator.setProperty(mView, AnimatableProperty.Y, target,
mAnimationProperties,
true /* animate */);
animator.setDuration(duration)
.setStartDelay(delay);
animProps.setDuration(duration)
.setDelay(delay);
log("keyguardFadingAway transition w/ Y Aniamtion");
} else {
log("keyguardFadingAway transition w/o Y Animation");
}
animator.start();
PropertyAnimator.setProperty(
mView, AnimatableProperty.ALPHA, 0f,
animProps,
true /* animate */);
} else if (mScreenOffAnimationController.shouldAnimateInKeyguard()) {
log("ScreenOff transition");
mKeyguardViewVisibilityAnimating = true;
@@ -149,7 +156,7 @@ public class KeyguardVisibilityHelper {
// Ask the screen off animation controller to animate the keyguard visibility for us
// since it may need to be cancelled due to keyguard lifecycle events.
mScreenOffAnimationController.animateInKeyguard(
mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
mView, mSetVisibleEndRunnable);
} else {
log("Direct set Visibility to VISIBLE");
mView.setVisibility(View.VISIBLE);
@@ -163,19 +170,25 @@ public class KeyguardVisibilityHelper {
mLastOccludedState = isOccluded;
}
private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
mKeyguardViewVisibilityAnimating = false;
mView.setVisibility(View.INVISIBLE);
log("Callback Set Visibility to INVISIBLE");
private final Consumer<Property> mSetInvisibleEndAction = new Consumer<>() {
@Override
public void accept(Property property) {
mKeyguardViewVisibilityAnimating = false;
mView.setVisibility(View.INVISIBLE);
log("Callback Set Visibility to INVISIBLE");
}
};
private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
mKeyguardViewVisibilityAnimating = false;
mView.setVisibility(View.GONE);
log("CallbackSet Visibility to GONE");
private final Consumer<Property> mSetGoneEndAction = new Consumer<>() {
@Override
public void accept(Property property) {
mKeyguardViewVisibilityAnimating = false;
mView.setVisibility(View.GONE);
log("CallbackSet Visibility to GONE");
}
};
private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
private final Runnable mSetVisibleEndRunnable = () -> {
mKeyguardViewVisibilityAnimating = false;
mView.setVisibility(View.VISIBLE);
log("Callback Set Visibility to VISIBLE");

View File

@@ -48,6 +48,10 @@ public abstract class AnimatableProperty {
View.SCALE_Y, R.id.scale_y_animator_tag, R.id.scale_y_animator_start_value_tag,
R.id.scale_y_animator_end_value_tag);
public static final AnimatableProperty ALPHA = AnimatableProperty.from(
View.ALPHA, R.id.alpha_animator_tag, R.id.alpha_animator_start_value_tag,
R.id.alpha_animator_end_value_tag);
/**
* Similar to X, however this doesn't allow for any other modifications other than from this
* property. When using X, it's possible that the view is laid out during the animation,

View File

@@ -32,6 +32,7 @@ public class AnimationProperties {
public long duration;
public long delay;
private ArrayMap<Property, Interpolator> mInterpolatorMap;
private Consumer<Property> mAnimationCancelAction;
private Consumer<Property> mAnimationEndAction;
/**
@@ -50,27 +51,43 @@ public class AnimationProperties {
* @return a listener that will be added for a given property during its animation.
*/
public AnimatorListenerAdapter getAnimationFinishListener(Property property) {
if (mAnimationEndAction == null) {
if (mAnimationEndAction == null && mAnimationCancelAction == null) {
return null;
}
Consumer<Property> cancelAction = mAnimationCancelAction;
Consumer<Property> endAction = mAnimationEndAction;
return new AnimatorListenerAdapter() {
private boolean mCancelled;
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
mCancelled = true;
if (cancelAction != null) {
cancelAction.accept(property);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (!mCancelled) {
if (!mCancelled && endAction != null) {
endAction.accept(property);
}
}
};
}
/**
* Add a callback for animation cancellation.
*/
public AnimationProperties setAnimationCancelAction(Consumer<Property> listener) {
mAnimationCancelAction = listener;
return this;
}
/**
* Add a callback for animation ending successfully. The callback will not be called when the
* animations is cancelled.
*/
public AnimationProperties setAnimationEndAction(Consumer<Property> listener) {
mAnimationEndAction = listener;
return this;

View File

@@ -175,15 +175,19 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// We animate the Y properly separately using the PropertyAnimator, as the panel
// view also needs to update the end position.
PropertyAnimator.cancelAnimation(keyguardView, AnimatableProperty.Y)
PropertyAnimator.setProperty<View>(keyguardView, AnimatableProperty.Y, currentY,
PropertyAnimator.setProperty(keyguardView, AnimatableProperty.Y, currentY,
AnimationProperties().setDuration(duration.toLong()),
true /* animate */)
keyguardView.animate()
// Cancel any existing CUJs before starting the animation
interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
PropertyAnimator.setProperty(
keyguardView, AnimatableProperty.ALPHA, 1f,
AnimationProperties()
.setDelay(0)
.setDuration(duration.toLong())
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1f)
.withEndAction {
.setAnimationEndAction {
aodUiAnimationPlaying = false
// Lock the keyguard if it was waiting for the screen off animation to end.
@@ -199,30 +203,23 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// Done going to sleep, reset this flag.
decidedToAnimateGoingToSleep = null
// We need to unset the listener. These are persistent for future animators
keyguardView.animate().setListener(null)
interactionJankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
}
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation: Animator?) {
// If we're cancelled, reset state flags/listeners. The end action above
// will not be called, which is what we want since that will finish the
// screen off animation and show the lockscreen, which we don't want if we
// were cancelled.
aodUiAnimationPlaying = false
decidedToAnimateGoingToSleep = null
keyguardView.animate().setListener(null)
interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
}
override fun onAnimationStart(animation: Animator?) {
interactionJankMonitor.begin(
mCentralSurfaces.notificationShadeWindowView,
CUJ_SCREEN_OFF_SHOW_AOD)
}
})
.start()
.setAnimationCancelAction {
// If we're cancelled, reset state flags/listeners. The end action above
// will not be called, which is what we want since that will finish the
// screen off animation and show the lockscreen, which we don't want if we
// were cancelled.
aodUiAnimationPlaying = false
decidedToAnimateGoingToSleep = null
interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
}
.setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_SLOW_IN),
true /* animate */)
interactionJankMonitor.begin(
mCentralSurfaces.notificationShadeWindowView,
CUJ_SCREEN_OFF_SHOW_AOD
)
}
override fun onStartedWakingUp() {

View File

@@ -61,6 +61,7 @@ import javax.inject.Inject;
* Manages the user switcher on the Keyguard.
*/
@KeyguardUserSwitcherScope
@Deprecated
public class KeyguardUserSwitcherController extends ViewController<KeyguardUserSwitcherView> {
private static final String TAG = "KeyguardUserSwitcherController";

View File

@@ -16,12 +16,10 @@
package com.android.systemui.statusbar.phone
import android.animation.Animator
import android.os.Handler
import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
@@ -39,10 +37,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
@@ -111,27 +107,6 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
controller.onStartedWakingUp()
}
@Test
fun testAnimClearsEndListener() {
val keyguardView = View(context)
val animator = spy(keyguardView.animate())
val keyguardSpy = spy(keyguardView)
Mockito.`when`(keyguardSpy.animate()).thenReturn(animator)
val listener = ArgumentCaptor.forClass(Animator.AnimatorListener::class.java)
val endAction = ArgumentCaptor.forClass(Runnable::class.java)
controller.animateInKeyguard(keyguardSpy, Runnable {})
Mockito.verify(animator).setListener(listener.capture())
Mockito.verify(animator).withEndAction(endAction.capture())
// Verify that the listener is cleared if we cancel it.
listener.value.onAnimationCancel(null)
Mockito.verify(animator).setListener(null)
// Verify that the listener is also cleared if the end action is triggered.
endAction.value.run()
verify(animator, times(2)).setListener(null)
}
/**
* The AOD UI is shown during the screen off animation, after a delay to allow the light reveal
* animation to start. If the device is woken up during the screen off, we should *never* do