Merge "Fine tune animation parameters for IME animations" into rvc-dev am: 08a45d2445 am: 2a392ab3a8
Change-Id: Ic79b78235446e227af9ac6d8e62af96edccf2594
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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<WindowInsetsAnimationController, Insets> {
|
||||
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<AnimationHandler> mSfAnimationHandlerThreadLocal =
|
||||
new ThreadLocal<AnimationHandler>() {
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<InsetsSourceControl> 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));
|
||||
|
||||
Reference in New Issue
Block a user