diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index d2e603649655f..f827eda07901e 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -68,7 +68,6 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll private final @InsetsType int mTypes; private final InsetsAnimationControlCallbacks mController; private final WindowInsetsAnimation mAnimation; - private final boolean mFade; private Insets mCurrentInsets; private Insets mPendingInsets; private float mPendingFraction; @@ -83,11 +82,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator, - boolean fade, @AnimationType int animationType) { + @AnimationType int animationType) { mControls = controls; mListener = listener; mTypes = types; - mFade = fade; mController = controller; mInitialInsetsState = new InsetsState(state, true /* copySources */); mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */); @@ -299,8 +297,6 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll // If the system is controlling the insets source, the leash can be null. if (leash != null) { - // TODO: use a better interpolation for fade. - alpha = mFade ? ((float) inset / maxInset * 0.3f + 0.7f) : alpha; SurfaceParams params = new SurfaceParams.Builder(leash) .withAlpha(side == ISIDE_FLOATING ? 1 : alpha) .withMatrix(mTmpMatrix) diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index 13b4cd83b4df2..40ffa7ef48bf7 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -88,11 +88,11 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator, - boolean fade, @AnimationType int animationType, Handler mainThreadHandler) { + @AnimationType int animationType, Handler mainThreadHandler) { mMainThreadHandler = mainThreadHandler; mOuterCallbacks = controller; mControl = new InsetsAnimationControlImpl(copyControls(controls), frame, state, listener, - types, mCallbacks, durationMs, interpolator, fade, animationType); + types, mCallbacks, durationMs, interpolator, animationType); InsetsAnimationThread.getHandler().post(() -> listener.onReady(mControl, types)); } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 96f73409ae0bd..048c0e26cdb80 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -27,7 +27,9 @@ import android.animation.AnimationHandler; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -36,6 +38,7 @@ import android.graphics.Rect; import android.os.CancellationSignal; import android.os.Handler; import android.os.RemoteException; +import android.renderscript.Sampler.Value; import android.util.ArraySet; import android.util.Log; import android.util.Pair; @@ -50,6 +53,7 @@ import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; @@ -72,9 +76,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private static final int ANIMATION_DURATION_SHOW_MS = 275; private static final int ANIMATION_DURATION_HIDE_MS = 340; + + private static final int ANIMATION_DURATION_SYNC_IME_MS = 285; + private static final int ANIMATION_DURATION_UNSYNC_IME_MS = 200; + private static final int PENDING_CONTROL_TIMEOUT_MS = 2000; - public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + public static final Interpolator SYSTEM_BARS_INTERPOLATOR = + new PathInterpolator(0.4f, 0f, 0.2f, 1f); + private static final Interpolator SYNC_IME_INTERPOLATOR = + new PathInterpolator(0.2f, 0f, 0f, 1f); + private static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR = + new PathInterpolator(0, 0, 0.2f, 1f); + private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = + new PathInterpolator(0.4f, 0f, 1f, 1f); /** * Layout mode during insets animation: The views should be laid out as if the changing inset @@ -138,27 +153,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation (int) (startValue.right + fraction * (endValue.right - startValue.right)), (int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom))); - /** - * Linear animation property - */ - private static class InsetsProperty extends Property { - InsetsProperty() { - super(Insets.class, "Insets"); - } - - @Override - public Insets get(WindowInsetsAnimationController object) { - return object.getCurrentInsets(); - } - @Override - public void set(WindowInsetsAnimationController controller, Insets value) { - controller.setInsetsAndAlpha( - value, 1f /* alpha */, (((InternalAnimationControlListener) - ((InsetsAnimationControlImpl) controller).getListener()) - .getRawFraction())); - } - } - /** * The default implementation of listener, to be used by InsetsController and InsetsPolicy to * animate insets. @@ -167,9 +161,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation implements WindowInsetsAnimationControlListener { private WindowInsetsAnimationController mController; - private ObjectAnimator mAnimator; + private ValueAnimator mAnimator; private final boolean mShow; - private final boolean mUseSfVsync; + private final boolean mHasAnimationCallbacks; + private final @InsetsType int mRequestedTypes; + private final long mDurationMs; private ThreadLocal mSfAnimationHandlerThreadLocal = new ThreadLocal() { @@ -181,24 +177,40 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } }; - public InternalAnimationControlListener(boolean show, boolean useSfVsync) { + public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks, + int requestedTypes) { mShow = show; - mUseSfVsync = useSfVsync; + mHasAnimationCallbacks = hasAnimationCallbacks; + mRequestedTypes = requestedTypes; + mDurationMs = calculateDurationMs(); } @Override public void onReady(WindowInsetsAnimationController controller, int types) { mController = controller; - mAnimator = ObjectAnimator.ofObject( - controller, - new InsetsProperty(), - sEvaluator, - mShow ? controller.getHiddenStateInsets() : controller.getShownStateInsets(), - mShow ? controller.getShownStateInsets() : controller.getHiddenStateInsets() - ); - mAnimator.setDuration(getDurationMs()); - mAnimator.setInterpolator(INTERPOLATOR); + mAnimator = ValueAnimator.ofFloat(0f, 1f); + mAnimator.setDuration(mDurationMs); + mAnimator.setInterpolator(new LinearInterpolator()); + Insets start = mShow + ? controller.getHiddenStateInsets() + : controller.getShownStateInsets(); + Insets end = mShow + ? controller.getShownStateInsets() + : controller.getHiddenStateInsets(); + Interpolator insetsInterpolator = getInterpolator(); + Interpolator alphaInterpolator = getAlphaInterpolator(); + mAnimator.addUpdateListener(animation -> { + float rawFraction = animation.getAnimatedFraction(); + float alphaFraction = mShow + ? rawFraction + : 1 - rawFraction; + float insetsFraction = insetsInterpolator.getInterpolation(rawFraction); + controller.setInsetsAndAlpha( + sEvaluator.evaluate(insetsFraction, start, end), + alphaInterpolator.getInterpolation(alphaFraction), + rawFraction); + }); mAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -206,7 +218,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation onAnimationFinish(); } }); - if (mUseSfVsync) { + if (!mHasAnimationCallbacks) { mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get()); } mAnimator.start(); @@ -224,22 +236,57 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } - protected void onAnimationFinish() { - mController.finish(mShow); + Interpolator getInterpolator() { + if ((mRequestedTypes & ime()) != 0) { + if (mHasAnimationCallbacks) { + return SYNC_IME_INTERPOLATOR; + } else if (mShow) { + return LINEAR_OUT_SLOW_IN_INTERPOLATOR; + } else { + return FAST_OUT_LINEAR_IN_INTERPOLATOR; + } + } else { + return SYSTEM_BARS_INTERPOLATOR; + } } - protected float getRawFraction() { - return (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration(); + Interpolator getAlphaInterpolator() { + if ((mRequestedTypes & ime()) != 0) { + if (mHasAnimationCallbacks) { + return input -> 1f; + } else if (mShow) { + + // Alpha animation takes half the time with linear interpolation; + return input -> Math.min(1f, 2 * input); + } else { + return FAST_OUT_LINEAR_IN_INTERPOLATOR; + } + } else { + return input -> 1f; + } + } + + protected void onAnimationFinish() { + mController.finish(mShow); } /** * To get the animation duration in MS. */ public long getDurationMs() { - if (mAnimator != null) { - return mAnimator.getDuration(); + return mDurationMs; + } + + private long calculateDurationMs() { + if ((mRequestedTypes & ime()) != 0) { + if (mHasAnimationCallbacks) { + return ANIMATION_DURATION_SYNC_IME_MS; + } else { + return ANIMATION_DURATION_UNSYNC_IME_MS; + } + } else { + return mShow ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS; } - return mShow ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS; } } @@ -525,7 +572,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation pendingRequest.types, pendingRequest.cancellationSignal, pendingRequest.listener, mFrame, true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator, - false /* fade */, pendingRequest.animationType, + pendingRequest.animationType, pendingRequest.layoutInsetsDuringAnimation, pendingRequest.useInsetsAnimationThread); return; @@ -590,9 +637,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation listener.onCancelled(null); return; } + controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs, - interpolator, false /* fade */, animationType, - getLayoutInsetsDuringAnimationMode(types), + interpolator, animationType, getLayoutInsetsDuringAnimationMode(types), false /* useInsetsAnimationThread */); } @@ -606,7 +653,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void controlAnimationUnchecked(@InsetsType int types, @Nullable CancellationSignal cancellationSignal, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, - long durationMs, Interpolator interpolator, boolean fade, + long durationMs, Interpolator interpolator, @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, boolean useInsetsAnimationThread) { @@ -652,10 +699,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final InsetsAnimationControlRunner runner = useInsetsAnimationThread ? new InsetsAnimationThreadControlRunner(controls, - frame, mState, listener, typesReady, this, durationMs, interpolator, fade, + frame, mState, listener, typesReady, this, durationMs, interpolator, animationType, mViewRoot.mHandler) : new InsetsAnimationControlImpl(controls, - frame, mState, listener, typesReady, this, durationMs, interpolator, fade, + frame, mState, listener, typesReady, this, durationMs, interpolator, animationType); mRunningAnimations.add(new RunningAnimation(runner, animationType)); if (cancellationSignal != null) { @@ -921,25 +968,26 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return; } - boolean useInsetsAnimationThread = canUseInsetsAnimationThread(); + boolean hasAnimationCallbacks = hasAnimationCallbacks(); final InternalAnimationControlListener listener = - new InternalAnimationControlListener(show, useInsetsAnimationThread); + new InternalAnimationControlListener(show, hasAnimationCallbacks, types); + // Show/hide animations always need to be relative to the display frame, in order that shown // and hidden state insets are correct. controlAnimationUnchecked( types, null /* cancellationSignal */, listener, mState.getDisplayFrame(), fromIme, - listener.getDurationMs(), - INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, - show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN - : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, - useInsetsAnimationThread); + listener.getDurationMs(), listener.getInterpolator(), + show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, + show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, + !hasAnimationCallbacks /* useInsetsAnimationThread */); + } - private boolean canUseInsetsAnimationThread() { + private boolean hasAnimationCallbacks() { if (mViewRoot.mView == null) { - return true; + return false; } - return !mViewRoot.mView.hasWindowInsetsAnimationCallback(); + return mViewRoot.mView.hasWindowInsetsAnimationCallback(); } private void hideDirectly( @@ -1118,7 +1166,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // Make sure a frame gets scheduled. mViewRoot.mView.invalidate(); } else { - sc.release(); + sc.release(); } } } diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index c87433f247e51..d78746918e656 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -20,7 +20,6 @@ import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowInsets.Type.systemBars; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -44,8 +43,6 @@ import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.animation.LinearInterpolator; import android.view.test.InsetsModeSession; -import androidx.test.runner.AndroidJUnit4; - import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -57,6 +54,8 @@ import org.mockito.MockitoAnnotations; import java.util.List; +import androidx.test.runner.AndroidJUnit4; + /** * Tests for {@link InsetsAnimationControlImpl}. * @@ -124,7 +123,7 @@ public class InsetsAnimationControlImplTest { mController = new InsetsAnimationControlImpl(controls, new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(), mMockController, 10 /* durationMs */, new LinearInterpolator(), - false /* fade */, 0 /* animationType */); + 0 /* animationType */); } @Test diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 30912e55f9080..bb0278964f851 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -22,8 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; -import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; -import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.SyncRtSurfaceTransactionApplier.applyParams; @@ -299,7 +297,7 @@ class InsetsPolicy { private void controlAnimationUnchecked(int typesReady, SparseArray controls, boolean show, Runnable callback) { InsetsPolicyAnimationControlListener listener = - new InsetsPolicyAnimationControlListener(show, callback); + new InsetsPolicyAnimationControlListener(show, callback, typesReady); listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show); } @@ -328,8 +326,8 @@ class InsetsPolicy { Runnable mFinishCallback; InsetsPolicyAnimationControlCallbacks mControlCallbacks; - InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) { - super(show, true /* useSfVsync */); + InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) { + super(show, false /* hasCallbacks */, types); mFinishCallback = finishCallback; mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); } @@ -361,7 +359,7 @@ class InsetsPolicy { mAnimationControl = new InsetsAnimationControlImpl(controls, mFocusedWin.getDisplayContent().getBounds(), getState(), mListener, typesReady, this, mListener.getDurationMs(), - InsetsController.INTERPOLATOR, true, + InsetsController.SYSTEM_BARS_INTERPOLATOR, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE); SurfaceAnimationThread.getHandler().post( () -> mListener.onReady(mAnimationControl, typesReady));