From 7c5b0fc9bdf98dfe78585952606f5644bfa12501 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Mon, 27 Feb 2023 13:55:41 -0800 Subject: [PATCH] Fix view jumping up in the password disappear... animation. The problem is that the edittext is not statically anchored to the bottom, so when we start the disappear animation, the height of the view increases to basically the full vertical size of the screen, causing the jump. The solution here is to prevent the bottom padding to be set in onApplyWindowInsets. This means the viewfliper view will remain the same size for the duration of the disapper animation and we use a y translation animation and apply it to the security container for a smooth transition. Fixes: 268221346 Test: start disappear animation in password. Switch security method to see that those work as well. Test: Test animation with bouncer user switcher as well. Change-Id: Id036bb10df79e4d357c00265507b43632d763a6b --- .../keyguard/KeyguardPasswordView.java | 27 ++++++++++-- .../keyguard/KeyguardSecurityContainer.java | 30 +++++++++++-- .../KeyguardSecurityContainerTest.java | 43 +++++++++++++++++++ 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index c1fae9e44bd32..33bea027cd202 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -69,6 +69,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { private Interpolator mLinearOutSlowInInterpolator; private Interpolator mFastOutLinearInInterpolator; + private DisappearAnimationListener mDisappearAnimationListener; public KeyguardPasswordView(Context context) { this(context, null); @@ -186,9 +187,13 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { return; } Insets shownInsets = controller.getShownStateInsets(); - Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0, - (int) (-shownInsets.bottom / 4 - * anim.getAnimatedFraction()))); + int dist = (int) (-shownInsets.bottom / 4 + * anim.getAnimatedFraction()); + Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0, dist)); + if (mDisappearAnimationListener != null) { + mDisappearAnimationListener.setTranslationY(-dist); + } + controller.setInsetsAndAlpha(insets, (float) animation.getAnimatedValue(), anim.getAnimatedFraction()); @@ -209,6 +214,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { controller.finish(false); runOnFinishImeAnimationRunnable(); finishRunnable.run(); + mDisappearAnimationListener = null; Trace.endSection(); }); } @@ -286,4 +292,19 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { } }); } + + /** + * Listens to the progress of the disappear animation and handles it. + */ + interface DisappearAnimationListener { + void setTranslationY(int transY); + } + + /** + * Set an instance of the disappear animation listener to this class. This will be + * removed when the animation completes. + */ + public void setDisappearAnimationListener(DisappearAnimationListener listener) { + mDisappearAnimationListener = listener; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index c6f0eeed108fc..66d5d097ab04f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -173,6 +173,17 @@ public class KeyguardSecurityContainer extends ConstraintLayout { private @Mode int mCurrentMode = MODE_UNINITIALIZED; private int mWidth = -1; + /** + * This callback is used to animate KeyguardSecurityContainer and its child views based on + * the interaction with the ime. After + * {@link WindowInsetsAnimation.Callback#onPrepare(WindowInsetsAnimation)}, + * {@link #onApplyWindowInsets} is called where we + * set the bottom padding to be the height of the keyboard. We use this padding to determine + * the delta of vertical distance for y-translation animations. + * Note that bottom padding is not set when the disappear animation is started because + * we are deferring the y translation logic to the animator in + * {@link KeyguardPasswordView#startDisappearAnimation(Runnable)} + */ private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -213,7 +224,6 @@ public class KeyguardSecurityContainer extends ConstraintLayout { continue; } interpolatedFraction = animation.getInterpolatedFraction(); - final int paddingBottom = (int) MathUtils.lerp( start, end, interpolatedFraction); @@ -568,13 +578,21 @@ public class KeyguardSecurityContainer extends ConstraintLayout { */ public void startDisappearAnimation(SecurityMode securitySelection) { mDisappearAnimRunning = true; - mViewMode.startDisappearAnimation(securitySelection); + if (securitySelection == SecurityMode.Password + && mSecurityViewFlipper.getSecurityView() instanceof KeyguardPasswordView) { + ((KeyguardPasswordView) mSecurityViewFlipper.getSecurityView()) + .setDisappearAnimationListener(this::setTranslationY); + } else { + mViewMode.startDisappearAnimation(securitySelection); + } } /** * This will run when the bouncer shows in all cases except when the user drags the bouncer up. */ public void startAppearAnimation(SecurityMode securityMode) { + setTranslationY(0f); + setAlpha(1f); updateChildren(0 /* translationY */, 1f /* alpha */); mViewMode.startAppearAnimation(securityMode); } @@ -623,7 +641,13 @@ public class KeyguardSecurityContainer extends ConstraintLayout { int inset = max(bottomInset, imeInset); int paddingBottom = max(inset, getContext().getResources() .getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin)); - setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom); + // If security mode is password, we rely on the animation value of defined in + // KeyguardPasswordView to determine the y translation animation. + // This means that we will prevent the WindowInsetsAnimationCallback from setting any y + // translation values by preventing the setting of the padding here. + if (!mDisappearAnimRunning) { + setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom); + } return insets.inset(0, 0, 0, inset); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 1bbc19931c216..531006da8210a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -37,6 +37,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -159,6 +160,29 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isEqualTo(systemBarInsetAmount); } + @Test + public void testOnApplyWindowInsets_disappearAnimation_paddingNotSet() { + int paddingBottom = getContext().getResources() + .getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin); + int imeInsetAmount = paddingBottom + 1; + int systemBarInsetAmount = 0; + initMode(MODE_DEFAULT); + + Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount); + Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount); + + WindowInsets insets = new WindowInsets.Builder() + .setInsets(ime(), imeInset) + .setInsetsIgnoringVisibility(systemBars(), systemBarInset) + .build(); + + ensureViewFlipperIsMocked(); + mKeyguardSecurityContainer.startDisappearAnimation( + KeyguardSecurityModel.SecurityMode.Password); + mKeyguardSecurityContainer.onApplyWindowInsets(insets); + assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isNotEqualTo(imeInsetAmount); + } + @Test public void testDefaultViewMode() { initMode(MODE_ONE_HANDED); @@ -376,6 +400,17 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { assertThat(mKeyguardSecurityContainer.getScaleY()).isEqualTo(1); } + @Test + public void testDisappearAnimationPassword() { + ensureViewFlipperIsMocked(); + KeyguardPasswordView keyguardPasswordView = mock(KeyguardPasswordView.class); + when(mSecurityViewFlipper.getSecurityView()).thenReturn(keyguardPasswordView); + + mKeyguardSecurityContainer + .startDisappearAnimation(KeyguardSecurityModel.SecurityMode.Password); + verify(keyguardPasswordView).setDisappearAnimationListener(any()); + } + private BackEvent createBackEvent(float touchX, float progress) { return new BackEvent(0, 0, progress, BackEvent.EDGE_LEFT); } @@ -446,4 +481,12 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { mUserSwitcherController, () -> { }, mFalsingA11yDelegate); } + + private void ensureViewFlipperIsMocked() { + mSecurityViewFlipper = mock(KeyguardSecurityViewFlipper.class); + KeyguardPasswordView keyguardPasswordView = mock(KeyguardPasswordView.class); + when(mSecurityViewFlipper.getSecurityView()).thenReturn(keyguardPasswordView); + mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper; + } + }