diff options
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; } |