diff options
author | Jorim Jaggi <jjaggi@google.com> | 2020-01-20 17:24:51 +0100 |
---|---|---|
committer | Jorim Jaggi <jjaggi@google.com> | 2020-01-23 00:36:28 +0100 |
commit | dd3304ec3bb7ca651b79a2878149b47b95e1e521 (patch) | |
tree | 277241400e4127f0d3fa277899949ca514ca54ad | |
parent | 0da8fd165fed21f26b2b10ddcf4bd1a89d9981c3 (diff) |
Fix fraction, and other cleanup
Fraction is now from animation start to end state. This makes it
more in line with other animation fraction meaning. Also, the app
can figure out the animation direction on its own by inspecting
the visibility state when the animation gets prepared.
Also fix some other stuff in WindowInsetsAnimationCallback.
Test: CTS coming soon
Bug: 118118435
Change-Id: I81d36159817719a9b0ee25bb78cfbfddc2029270
8 files changed, 135 insertions, 75 deletions
diff --git a/api/current.txt b/api/current.txt index 2132fc535dab..b8ec7f7649d5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -54119,7 +54119,7 @@ package android.view { method public float getInterpolatedFraction(); method @Nullable public android.view.animation.Interpolator getInterpolator(); method public int getTypeMask(); - method public void setDuration(long); + method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float); method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float); } @@ -54140,7 +54140,7 @@ package android.view { } public interface WindowInsetsController { - method public default void controlInputMethodAnimation(long, @NonNull android.view.WindowInsetsAnimationControlListener); + method public default void controlInputMethodAnimation(long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener); method public int getSystemBarsAppearance(); method public int getSystemBarsBehavior(); method public default void hideInputMethod(); diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index f5afd106a4a7..405eccd56e3c 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -40,6 +40,7 @@ import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimationCallback.AnimationBounds; import android.view.WindowInsetsAnimationCallback.InsetsAnimation; import android.view.WindowManager.LayoutParams; +import android.view.animation.Interpolator; import com.android.internal.annotations.VisibleForTesting; @@ -84,8 +85,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, - InsetsAnimationControlCallbacks controller, long durationMs, boolean fade, - @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { + InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator, + boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { mControls = controls; mListener = listener; mTypes = types; @@ -101,8 +102,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mFrame = new Rect(frame); buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls); - mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes, - InsetsController.INTERPOLATOR, durationMs); + mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes, interpolator, + durationMs); mAnimation.setAlpha(getCurrentAlpha()); mController.startAnimation(this, listener, types, mAnimation, new AnimationBounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation); @@ -196,7 +197,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll state.getSource(control.getType()).setVisible(shown); } Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */); - setInsetsAndAlpha(insets, 1f /* alpha */, shown ? 1f : 0f /* fraction */); + setInsetsAndAlpha(insets, 1f /* alpha */, 1f /* fraction */); mFinished = true; mShownOnFinish = shown; } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 411e910e1af1..c6e383539a82 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -28,6 +28,7 @@ import android.animation.ObjectAnimator; import android.animation.TypeEvaluator; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Rect; import android.os.RemoteException; @@ -145,7 +146,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation controller.setInsetsAndAlpha( value, 1f /* alpha */, (((DefaultAnimationControlListener) ((InsetsAnimationControlImpl) controller).getListener()) - .getRawProgress())); + .getRawFraction())); } } @@ -204,9 +205,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mController.finish(mShow); } - protected float getRawProgress() { - float fraction = (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration(); - return mShow ? fraction : 1 - fraction; + protected float getRawFraction() { + return (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration(); } protected long getDurationMs() { @@ -437,27 +437,29 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs, - WindowInsetsAnimationControlListener listener) { - controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs, + @Nullable Interpolator interpolator, + @NonNull WindowInsetsAnimationControlListener listener) { + controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs, interpolator, ANIMATION_TYPE_USER); } private void controlWindowInsetsAnimation(@InsetsType int types, WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs, - @AnimationType int animationType) { + @Nullable Interpolator interpolator, @AnimationType int animationType) { // If the frame of our window doesn't span the entire display, the control API makes very // little sense, as we don't deal with negative insets. So just cancel immediately. if (!mState.getDisplayFrame().equals(mFrame)) { listener.onCancelled(); return; } - controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */, - animationType, getLayoutInsetsDuringAnimationMode(types)); + controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator, + false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types)); } private void controlAnimationUnchecked(@InsetsType int types, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, - long durationMs, boolean fade, @AnimationType int animationType, + long durationMs, Interpolator interpolator, boolean fade, + @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { if (types == 0) { // nothing to animate. @@ -488,7 +490,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls, - frame, mState, listener, typesReady, this, durationMs, fade, + frame, mState, listener, typesReady, this, durationMs, interpolator, fade, layoutInsetsDuringAnimation); mRunningAnimations.add(new RunningAnimation(controller, animationType)); } @@ -733,7 +735,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // and hidden state insets are correct. controlAnimationUnchecked( types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(), - true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, + INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN); } diff --git a/core/java/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java index 53d493985b32..1e04d02fcb80 100644 --- a/core/java/android/view/WindowInsetsAnimationCallback.java +++ b/core/java/android/view/WindowInsetsAnimationCallback.java @@ -88,7 +88,7 @@ public interface WindowInsetsAnimationCallback { * <ul> * <li>Application calls {@link WindowInsetsController#hideInputMethod()}, * {@link WindowInsetsController#showInputMethod()}, - * {@link WindowInsetsController#controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)}</li> + * {@link WindowInsetsController#controlInputMethodAnimation}</li> * <li>onPrepare is called on the view hierarchy listeners</li> * <li>{@link View#onApplyWindowInsets} will be called with the end state of the * animation</li> @@ -182,14 +182,26 @@ public interface WindowInsetsAnimationCallback { private final @InsetsType int mTypeMask; private float mFraction; @Nullable private final Interpolator mInterpolator; - private long mDurationMs; + private final long mDurationMillis; private float mAlpha; + /** + * Creates a new {@link InsetsAnimation} object. + * <p> + * This should only be used for testing, as usually the system creates this object for the + * application to listen to with {@link WindowInsetsAnimationCallback}. + * </p> + * @param typeMask The bitmask of {@link WindowInsets.Type}s that are animating. + * @param interpolator The interpolator of the animation. + * @param durationMillis The duration of the animation in + * {@link java.util.concurrent.TimeUnit#MILLISECONDS}. + */ public InsetsAnimation( - @InsetsType int typeMask, @Nullable Interpolator interpolator, long durationMs) { + @InsetsType int typeMask, @Nullable Interpolator interpolator, + long durationMillis) { mTypeMask = typeMask; mInterpolator = interpolator; - mDurationMs = durationMs; + mDurationMillis = durationMillis; } /** @@ -201,14 +213,18 @@ public interface WindowInsetsAnimationCallback { /** * Returns the raw fractional progress of this animation between - * {@link AnimationBounds#getLowerBound()} and {@link AnimationBounds#getUpperBound()}. Note + * start state of the animation and the end state of the animation. Note * that this progress is the global progress of the animation, whereas * {@link WindowInsetsAnimationCallback#onProgress} will only dispatch the insets that may * be inset with {@link WindowInsets#inset} by parents of views in the hierarchy. * Progress per insets animation is global for the entire animation. One animation animates * all things together (in, out, ...). If they don't animate together, we'd have * multiple animations. - * + * <p> + * Note: In case the application is controlling the animation, the valued returned here will + * be the same as the application passed into + * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}. + * </p> * @return The current progress of this animation. */ @FloatRange(from = 0f, to = 1f) @@ -218,16 +234,27 @@ public interface WindowInsetsAnimationCallback { /** * Returns the interpolated fractional progress of this animation between - * {@link AnimationBounds#getLowerBound()} and {@link AnimationBounds#getUpperBound()}. Note + * start state of the animation and the end state of the animation. Note * that this progress is the global progress of the animation, whereas * {@link WindowInsetsAnimationCallback#onProgress} will only dispatch the insets that may * be inset with {@link WindowInsets#inset} by parents of views in the hierarchy. * Progress per insets animation is global for the entire animation. One animation animates * all things together (in, out, ...). If they don't animate together, we'd have * multiple animations. + * <p> + * Note: In case the application is controlling the animation, the valued returned here will + * be the same as the application passed into + * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}, + * interpolated with the interpolator passed into + * {@link WindowInsetsController#controlInputMethodAnimation}. + * </p> + * <p> + * Note: For system-initiated animations, this will always return a valid value between 0 + * and 1. + * </p> * @see #getFraction() for raw fraction. * @return The current interpolated progress of this animation. -1 if interpolator isn't - * specified. + * specified. */ public float getInterpolatedFraction() { if (mInterpolator != null) { @@ -236,52 +263,66 @@ public interface WindowInsetsAnimationCallback { return -1; } + /** + * Retrieves the interpolator used for this animation, or {@code null} if this animation + * doesn't follow an interpolation curved. For system-initiated animations, this will never + * return {@code null}. + * + * @return The interpolator used for this animation. + */ @Nullable public Interpolator getInterpolator() { return mInterpolator; } /** - * @return duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}. + * @return duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or + * -1 if the animation doesn't have a fixed duration. */ public long getDurationMillis() { - return mDurationMs; + return mDurationMillis; } /** * Set fraction of the progress if {@link WindowInsets.Type.InsetsType} animation is - * controlled by the app {@see #getCurrentFraction}. - * <p>Note: If app didn't create {@link InsetsAnimation}, it shouldn't set progress either. - * Progress would be set by system with the system-default animation. + * controlled by the app. + * <p> + * Note: This should only be used for testing, as the system fills in the fraction for the + * application or the fraction that was passed into + * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being + * used. * </p> * @param fraction fractional progress between 0 and 1 where 0 represents hidden and * zero progress and 1 represent fully shown final state. + * @see #getFraction() */ public void setFraction(@FloatRange(from = 0f, to = 1f) float fraction) { mFraction = fraction; } /** - * Set duration of the animation if {@link WindowInsets.Type.InsetsType} animation is - * controlled by the app. - * <p>Note: If app didn't create {@link InsetsAnimation}, it shouldn't set duration either. - * Duration would be set by system with the system-default animation. - * </p> - * @param durationMs in {@link java.util.concurrent.TimeUnit#MILLISECONDS} - */ - public void setDuration(long durationMs) { - mDurationMs = durationMs; - } - - /** - * @return alpha of {@link WindowInsets.Type.InsetsType}. + * Retrieves the translucency of the windows that are animating. + * + * @return Alpha of windows that cause insets of type {@link WindowInsets.Type.InsetsType}. */ @FloatRange(from = 0f, to = 1f) public float getAlpha() { return mAlpha; } - void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) { + /** + * Sets the translucency of the windows that are animating. + * <p> + * Note: This should only be used for testing, as the system fills in the alpha for the + * application or the alpha that was passed into + * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being + * used. + * </p> + * @param alpha Alpha of windows that cause insets of type + * {@link WindowInsets.Type.InsetsType}. + * @see #getAlpha() + */ + public void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) { mAlpha = alpha; } } diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index f292ca4facbf..3bb6cfbbd7f2 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -20,8 +20,11 @@ import static android.view.WindowInsets.Type.ime; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Insets; import android.view.WindowInsets.Type.InsetsType; +import android.view.WindowInsetsAnimationCallback.InsetsAnimation; +import android.view.animation.Interpolator; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -148,29 +151,51 @@ public interface WindowInsetsController { * the position of the windows in the system causing insets directly. * * @param types The {@link InsetsType}s the application has requested to control. - * @param durationMillis duration of animation in + * @param durationMillis Duration of animation in * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the - * animation doesn't have a predetermined duration. + * animation doesn't have a predetermined duration.T his value will be + * passed to {@link InsetsAnimation#getDurationMillis()} + * @param interpolator The interpolator used for this animation, or {@code null} if this + * animation doesn't follow an interpolation curve. This value will be + * passed to {@link InsetsAnimation#getInterpolator()} and used to calculate + * {@link InsetsAnimation#getInterpolatedFraction()}. * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the * windows are ready to be controlled, among other callbacks. + * + * @see InsetsAnimation#getFraction() + * @see InsetsAnimation#getInterpolatedFraction() + * @see InsetsAnimation#getInterpolator() + * @see InsetsAnimation#getDurationMillis() * @hide */ void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis, + @Nullable Interpolator interpolator, @NonNull WindowInsetsAnimationControlListener listener); /** * Lets the application control the animation for showing the IME in a frame-by-frame manner by * modifying the position of the IME when it's causing insets. * - * @param durationMillis duration of the animation in + * @param durationMillis Duration of the animation in * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the - * animation doesn't have a predetermined duration. + * animation doesn't have a predetermined duration. This value will be + * passed to {@link InsetsAnimation#getDurationMillis()} + * @param interpolator The interpolator used for this animation, or {@code null} if this + * animation doesn't follow an interpolation curve. This value will be + * passed to {@link InsetsAnimation#getInterpolator()} and used to calculate + * {@link InsetsAnimation#getInterpolatedFraction()}. * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the * IME are ready to be controlled, among other callbacks. + * + * @see InsetsAnimation#getFraction() + * @see InsetsAnimation#getInterpolatedFraction() + * @see InsetsAnimation#getInterpolator() + * @see InsetsAnimation#getDurationMillis() */ default void controlInputMethodAnimation(long durationMillis, + @Nullable Interpolator interpolator, @NonNull WindowInsetsAnimationControlListener listener) { - controlWindowInsetsAnimation(ime(), durationMillis, listener); + controlWindowInsetsAnimation(ime(), durationMillis, interpolator, listener); } /** @@ -181,7 +206,7 @@ public interface WindowInsetsController { * the event by observing {@link View#onApplyWindowInsets} and checking visibility with * {@link WindowInsets#isVisible}. * - * @see #controlInputMethodAnimation(long, WindowInsetsAnimationControlListener) + * @see #controlInputMethodAnimation * @see #hideInputMethod() */ default void showInputMethod() { @@ -196,7 +221,7 @@ public interface WindowInsetsController { * the event by observing {@link View#onApplyWindowInsets} and checking visibility with * {@link WindowInsets#isVisible}. * - * @see #controlInputMethodAnimation(long, WindowInsetsAnimationControlListener) + * @see #controlInputMethodAnimation * @see #showInputMethod() */ default void hideInputMethod() { diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index f2852fa49b5e..bcf0b8ce4439 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -41,6 +41,7 @@ import android.platform.test.annotations.Presubmit; import android.util.SparseArray; import android.view.SurfaceControl.Transaction; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; +import android.view.animation.LinearInterpolator; import android.view.test.InsetsModeSession; import androidx.test.runner.AndroidJUnit4; @@ -121,7 +122,7 @@ public class InsetsAnimationControlImplTest { controls.put(ITYPE_NAVIGATION_BAR, navConsumer.getControl()); mController = new InsetsAnimationControlImpl(controls, new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(), - mMockController, 10 /* durationMs */, + mMockController, 10 /* durationMs */, new LinearInterpolator(), false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN); } diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 838190387c35..f720c987a525 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -44,6 +44,7 @@ import android.platform.test.annotations.Presubmit; import android.view.WindowInsets.Type; import android.view.WindowManager.BadTokenException; import android.view.WindowManager.LayoutParams; +import android.view.animation.LinearInterpolator; import android.view.test.InsetsModeSession; import android.widget.TextView; @@ -148,7 +149,7 @@ public class InsetsControllerTest { WindowInsetsAnimationControlListener mockListener = mock(WindowInsetsAnimationControlListener.class); mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */, - mockListener); + new LinearInterpolator(), mockListener); // Ready gets deferred until next predraw mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); @@ -164,7 +165,8 @@ public class InsetsControllerTest { mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200)); WindowInsetsAnimationControlListener controlListener = mock(WindowInsetsAnimationControlListener.class); - mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, controlListener); + mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(), + controlListener); verify(controlListener).onCancelled(); verify(controlListener, never()).onReady(any(), anyInt()); } @@ -375,7 +377,7 @@ public class InsetsControllerTest { WindowInsetsAnimationControlListener mockListener = mock(WindowInsetsAnimationControlListener.class); mController.controlWindowInsetsAnimation(statusBars(), 0 /* durationMs */, - mockListener); + new LinearInterpolator(), mockListener); ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor = ArgumentCaptor.forClass(WindowInsetsAnimationController.class); diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java index f3c89d8addf6..01e212d01574 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java @@ -22,6 +22,7 @@ import android.animation.ValueAnimator; import android.app.Activity; import android.graphics.Insets; import android.os.Bundle; +import android.util.Log; import android.util.Property; import android.view.View; import android.view.WindowInsets; @@ -67,8 +68,8 @@ public class WindowInsetsActivity extends Activity { } }; - float showY; - float hideY; + float startY; + float endY; InsetsAnimation imeAnim; @Override @@ -84,16 +85,6 @@ public class WindowInsetsActivity extends Activity { v.getWindowInsetsController().hide(Type.ime()); } }); - mRoot.getViewTreeObserver().addOnGlobalLayoutListener(() -> { - if (imeAnim == null) { - return; - } - if (mRoot.getRootWindowInsets().isVisible(Type.ime())) { - showY = mButton.getTop(); - } else { - hideY = mButton.getTop(); - } - }); mRoot.setWindowInsetsAnimationCallback(new WindowInsetsAnimationCallback() { @Override @@ -106,22 +97,19 @@ public class WindowInsetsActivity extends Activity { if ((animation.getTypeMask() & Type.ime()) != 0) { imeAnim = animation; } - if (mRoot.getRootWindowInsets().isVisible(Type.ime())) { - showY = mButton.getTop(); - } else { - hideY = mButton.getTop(); - } + startY = mButton.getTop(); } @Override public WindowInsets onProgress(WindowInsets insets) { - mButton.setY(hideY + (showY - hideY) * imeAnim.getInterpolatedFraction()); + mButton.setY(startY + (endY - startY) * imeAnim.getInterpolatedFraction()); return insets; } @Override public AnimationBounds onStart(InsetsAnimation animation, AnimationBounds bounds) { + endY = mButton.getTop(); return bounds; } |