Correct layout/draw/animation interleaving for insets callbacks

See WindowInsetsAnimationCallback.onPrepare for detailed
description of new behavior.

Also rename InsetsSourceConsumer.setVisible to setRequestedVisible
to communicate that this is client intent vs. current state.

Test: windowinsetstest application
Bug: 111084606
Change-Id: Id35c60e0f59a8aa4f0d300220391d1e2b96a91fd
This commit is contained in:
Jorim Jaggi
2019-12-27 15:17:44 +01:00
parent 7b469d73d2
commit a51168aaee
15 changed files with 316 additions and 129 deletions

View File

@@ -16,17 +16,28 @@
package android.view;
import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
/**
* Provide an interface to let InsetsAnimationControlImpl call back into its owner.
* @hide
*/
public interface InsetsAnimationControlCallbacks {
/**
* Dispatch the animation started event to all listeners.
* @param animation
* Executes the necessary code to start the animation in the correct order, including:
* <ul>
* <li>Dispatch {@link WindowInsetsAnimationCallback#onPrepare}</li>
* <li>Update insets state and run layout according to {@code layoutDuringAnimation}</li>
* <li>Dispatch {@link WindowInsetsAnimationCallback#onStart}</li>
* <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li>
* </ul>
*/
void dispatchAnimationStarted(WindowInsetsAnimationCallback.InsetsAnimation animation,
WindowInsetsAnimationCallback.AnimationBounds bounds);
void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation,
AnimationBounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation);
/**
* Schedule the apply by posting the animation callback.

View File

@@ -16,6 +16,8 @@
package android.view;
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.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_FLOATING;
import static android.view.InsetsState.ISIDE_LEFT;
@@ -30,6 +32,7 @@ import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.InsetsState.InternalInsetsSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -80,7 +83,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) {
InsetsAnimationControlCallbacks controller, long durationMs, boolean fade,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -95,14 +99,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
mFrame = new Rect(frame);
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls);
// TODO: Check for controllability first and wait for IME if needed.
listener.onReady(this, types);
mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes,
InsetsController.INTERPOLATOR, durationMs);
mAnimation.setAlpha(getCurrentAlpha());
mController.dispatchAnimationStarted(mAnimation,
new AnimationBounds(mHiddenInsets, mShownInsets));
mController.startAnimation(this, listener, types, mAnimation,
new AnimationBounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation);
}
@Override

View File

@@ -37,6 +37,7 @@ import android.util.SparseArray;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
@@ -47,6 +48,8 @@ import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
@@ -66,6 +69,37 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE})
private @interface AnimationDirection{}
/**
* Layout mode during insets animation: The views should be laid out as if the changing inset
* types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will
* be called as if the changing insets types are shown, which will result in the views being
* laid out as if the insets are fully shown.
*/
static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
/**
* Layout mode during insets animation: The views should be laid out as if the changing inset
* types are fully hidden. Before starting the animation, {@link View#onApplyWindowInsets} will
* be called as if the changing insets types are hidden, which will result in the views being
* laid out as if the insets are fully hidden.
*/
static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
/**
* Determines the behavior of how the views should be laid out during an insets animation that
* is controlled by the application by calling {@link #controlWindowInsetsAnimation}.
* <p>
* When the animation is system-initiated, the layout mode is always chosen such that the
* pre-animation layout will represent the opposite of the starting state, i.e. when insets
* are appearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_SHOWN} will be used. When insets
* are disappearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_HIDDEN} will be used.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {LAYOUT_INSETS_DURING_ANIMATION_SHOWN,
LAYOUT_INSETS_DURING_ANIMATION_HIDDEN})
@interface LayoutInsetsDuringAnimation {
}
/**
* Translation animation evaluator.
*/
@@ -109,11 +143,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void onReady(WindowInsetsAnimationController controller, int types) {
mController = controller;
if (mShow) {
showDirectly(types);
} else {
hideDirectly(types);
}
mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE;
mAnimator = ObjectAnimator.ofObject(
controller,
@@ -131,7 +161,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
onAnimationFinish();
}
});
mStartingAnimation = true;
mAnimator.start();
mStartingAnimation = false;
}
@Override
@@ -185,6 +217,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private int mPendingTypesToShow;
private int mLastLegacySoftInputMode;
private boolean mStartingAnimation;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -312,7 +345,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// Only one animator (with multiple InsetsType) can run at a time.
// previous one should be cancelled for simplicity.
cancelExistingAnimation();
} else if (consumer.isVisible()
} else if (consumer.isRequestedVisible()
&& (mAnimationDirection == DIRECTION_NONE
|| mAnimationDirection == DIRECTION_HIDE)) {
// no-op: already shown or animating in (because window visibility is
@@ -338,7 +371,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
if (mAnimationDirection == DIRECTION_SHOW) {
cancelExistingAnimation();
} else if (!consumer.isVisible()
} else if (!consumer.isRequestedVisible()
&& (mAnimationDirection == DIRECTION_NONE
|| mAnimationDirection == DIRECTION_HIDE)) {
// no-op: already hidden or animating out.
@@ -363,12 +396,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
listener.onCancelled();
return;
}
controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */);
controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */,
getLayoutInsetsDuringAnimationMode(types));
}
private void controlAnimationUnchecked(@InsetsType int types,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
long durationMs, boolean fade) {
long durationMs, boolean fade,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
if (types == 0) {
// nothing to animate.
return;
@@ -398,7 +433,8 @@ 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, fade,
layoutInsetsDuringAnimation);
mAnimationControls.add(controller);
}
@@ -412,7 +448,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
boolean isReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
boolean setVisible = !consumer.isVisible();
boolean setVisible = !consumer.isRequestedVisible();
if (setVisible) {
// Show request
switch(consumer.requestShow(fromIme)) {
@@ -454,6 +490,29 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return typesReady;
}
private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
@InsetsType int types) {
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
// Generally, we want to layout the opposite of the current state. This is to make animation
// callbacks easy to use: The can capture the layout values and then treat that as end-state
// during the animation.
//
// However, if controlling multiple sources, we want to treat it as shown if any of the
// types is currently hidden.
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i));
if (consumer == null) {
continue;
}
if (!consumer.isRequestedVisible()) {
return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
}
}
return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
}
private void cancelExistingControllers(@InsetsType int types) {
for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
InsetsAnimationControlImpl control = mAnimationControls.get(i);
@@ -597,7 +656,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// and hidden state insets are correct.
controlAnimationUnchecked(
types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
true /* fade */);
true /* fade */, show
? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
}
private void hideDirectly(@InsetsType int types) {
@@ -629,18 +690,40 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
@Override
public void dispatchAnimationStarted(InsetsAnimation animation, AnimationBounds bounds) {
mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation, bounds);
public void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation,
AnimationBounds bounds, int layoutDuringAnimation) {
if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
showDirectly(types);
} else {
hideDirectly(types);
}
mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation);
mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this);
mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
listener.onReady(controller, types);
return true;
}
});
mViewRoot.mView.invalidate();
}
@VisibleForTesting
public void dispatchAnimationFinished(InsetsAnimation animation) {
mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
mViewRoot.mView.dispatchWindowInsetsAnimationFinish(animation);
}
@VisibleForTesting
@Override
public void scheduleApplyChangeInsets() {
if (mStartingAnimation) {
mAnimCallback.run();
mAnimCallbackScheduled = false;
return;
}
if (!mAnimCallbackScheduled) {
mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
mAnimCallback, null /* token*/);

View File

@@ -53,7 +53,7 @@ public class InsetsSourceConsumer {
}
protected final InsetsController mController;
protected boolean mVisible;
protected boolean mRequestedVisible;
private final Supplier<Transaction> mTransactionSupplier;
private final @InternalInsetsType int mType;
private final InsetsState mState;
@@ -66,7 +66,7 @@ public class InsetsSourceConsumer {
mState = state;
mTransactionSupplier = transactionSupplier;
mController = controller;
mVisible = InsetsState.getDefaultVisibility(type);
mRequestedVisible = InsetsState.getDefaultVisibility(type);
}
public void setControl(@Nullable InsetsSourceControl control) {
@@ -94,12 +94,12 @@ public class InsetsSourceConsumer {
@VisibleForTesting
public void show() {
setVisible(true);
setRequestedVisible(true);
}
@VisibleForTesting
public void hide() {
setVisible(false);
setRequestedVisible(false);
}
/**
@@ -126,16 +126,16 @@ public class InsetsSourceConsumer {
if (mSourceControl == null) {
return false;
}
if (mState.getSource(mType).isVisible() == mVisible) {
if (mState.getSource(mType).isVisible() == mRequestedVisible) {
return false;
}
mState.getSource(mType).setVisible(mVisible);
mState.getSource(mType).setVisible(mRequestedVisible);
return true;
}
@VisibleForTesting
public boolean isVisible() {
return mVisible;
public boolean isRequestedVisible() {
return mRequestedVisible;
}
/**
@@ -157,11 +157,15 @@ public class InsetsSourceConsumer {
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
}
private void setVisible(boolean visible) {
if (mVisible == visible) {
/**
* Sets requested visibility from the client, regardless of whether we are able to control it at
* the moment.
*/
private void setRequestedVisible(boolean requestedVisible) {
if (mRequestedVisible == requestedVisible) {
return;
}
mVisible = visible;
mRequestedVisible = requestedVisible;
applyLocalVisibilityOverride();
mController.notifyVisibilityChanged();
}
@@ -173,7 +177,7 @@ public class InsetsSourceConsumer {
}
final Transaction t = mTransactionSupplier.get();
if (mVisible) {
if (mRequestedVisible) {
t.show(mSourceControl.getLeash());
} else {
t.hide(mSourceControl.getLeash());

View File

@@ -11117,7 +11117,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
* Dispatches {@link WindowInsetsAnimationCallback#onStarted(InsetsAnimation, AnimationBounds)}
* Dispatches {@link WindowInsetsAnimationCallback#onPrepare(InsetsAnimation)}
* when Window Insets animation is being prepared.
* @param animation current animation
*
* @see WindowInsetsAnimationCallback#onPrepare(InsetsAnimation)
*/
public void dispatchWindowInsetsAnimationPrepare(
@NonNull InsetsAnimation animation) {
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
mListenerInfo.mWindowInsetsAnimationCallback.onPrepare(animation);
}
}
/**
* Dispatches {@link WindowInsetsAnimationCallback#onStart(InsetsAnimation, AnimationBounds)}
* when Window Insets animation is started.
* @param animation current animation
* @param bounds the upper and lower {@link AnimationBounds} that provides range of
@@ -11125,10 +11139,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return the upper and lower {@link AnimationBounds}.
*/
@NonNull
public AnimationBounds dispatchWindowInsetsAnimationStarted(
public AnimationBounds dispatchWindowInsetsAnimationStart(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
return mListenerInfo.mWindowInsetsAnimationCallback.onStarted(animation, bounds);
return mListenerInfo.mWindowInsetsAnimationCallback.onStart(animation, bounds);
}
return bounds;
}
@@ -11149,13 +11163,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
* Dispatches {@link WindowInsetsAnimationCallback#onFinished(InsetsAnimation)}
* Dispatches {@link WindowInsetsAnimationCallback#onFinish(InsetsAnimation)}
* when Window Insets animation finishes.
* @param animation The current ongoing {@link InsetsAnimation}.
*/
public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
public void dispatchWindowInsetsAnimationFinish(@NonNull InsetsAnimation animation) {
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
mListenerInfo.mWindowInsetsAnimationCallback.onFinished(animation);
mListenerInfo.mWindowInsetsAnimationCallback.onFinish(animation);
}
}

View File

@@ -7199,13 +7199,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
@NonNull
public AnimationBounds dispatchWindowInsetsAnimationStarted(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
super.dispatchWindowInsetsAnimationStarted(animation, bounds);
public void dispatchWindowInsetsAnimationPrepare(
@NonNull InsetsAnimation animation) {
super.dispatchWindowInsetsAnimationPrepare(animation);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).dispatchWindowInsetsAnimationStarted(animation, bounds);
getChildAt(i).dispatchWindowInsetsAnimationPrepare(animation);
}
}
@Override
@NonNull
public AnimationBounds dispatchWindowInsetsAnimationStart(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
super.dispatchWindowInsetsAnimationStart(animation, bounds);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).dispatchWindowInsetsAnimationStart(animation, bounds);
}
return bounds;
}
@@ -7222,11 +7232,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
super.dispatchWindowInsetsAnimationFinished(animation);
public void dispatchWindowInsetsAnimationFinish(@NonNull InsetsAnimation animation) {
super.dispatchWindowInsetsAnimationFinish(animation);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).dispatchWindowInsetsAnimationFinished(animation);
getChildAt(i).dispatchWindowInsetsAnimationFinish(animation);
}
}

View File

@@ -1464,6 +1464,7 @@ public final class ViewRootImpl implements ViewParent,
return;
}
mApplyInsetsRequested = true;
requestLayout();
// If this changes during traversal, no need to schedule another one as it will dispatch it
// during the current traversal.

View File

@@ -20,6 +20,7 @@ import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Insets;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
@@ -30,7 +31,47 @@ import android.view.animation.Interpolator;
public interface WindowInsetsAnimationCallback {
/**
* Called when an inset animation gets started.
* Called when an insets animation is about to start and before the views have been laid out in
* the end state of the animation. The ordering of events during an insets animation is the
* following:
* <p>
* <ul>
* <li>Application calls {@link WindowInsetsController#hideInputMethod()},
* {@link WindowInsetsController#showInputMethod()},
* {@link WindowInsetsController#controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)}</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>
* <li>View hierarchy gets laid out according to the changes the application has requested
* due to the new insets being dispatched</li>
* <li>{@link #onStart} is called <em>before</em> the view
* hierarchy gets drawn in the new laid out state</li>
* <li>{@link #onProgress} is called immediately after with the animation start state</li>
* <li>The frame gets drawn.</li>
* </ul>
* <p>
* This ordering allows the application to inspect the end state after the animation has
* finished, and then revert to the starting state of the animation in the first
* {@link #onProgress} callback by using post-layout view properties like {@link View#setX} and
* related methods.
* <p>
* Note: If the animation is application controlled by using
* {@link WindowInsetsController#controlInputMethodAnimation}, the end state of the animation
* is undefined as the application may decide on the end state only by passing in the
* {@code shown} parameter when calling {@link WindowInsetsAnimationController#finish}. In this
* situation, the system will dispatch the insets in the opposite visibility state before the
* animation starts. Example: When controlling the input method with
* {@link WindowInsetsController#controlInputMethodAnimation} and the input method is currently
* showing, {@link View#onApplyWindowInsets} will receive a {@link WindowInsets} instance for
* which {@link WindowInsets#isVisible} will return {@code false} for {@link Type#ime}.
*
* @param animation The animation that is about to start.
*/
default void onPrepare(@NonNull InsetsAnimation animation) {
}
/**
* Called when an insets animation gets started.
* <p>
* Note that, like {@link #onProgress}, dispatch of the animation start event is hierarchical:
* It will starts at the root of the view hierarchy and then traverse it and invoke the callback
@@ -45,7 +86,7 @@ public interface WindowInsetsAnimationCallback {
* subtree of the hierarchy.
*/
@NonNull
default AnimationBounds onStarted(
default AnimationBounds onStart(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
return bounds;
}
@@ -72,12 +113,12 @@ public interface WindowInsetsAnimationCallback {
WindowInsets onProgress(@NonNull WindowInsets insets);
/**
* Called when an inset animation has finished.
* Called when an insets animation has finished.
*
* @param animation The animation that has finished running. This will be the same instance as
* passed into {@link #onStarted}
* passed into {@link #onStart}
*/
default void onFinished(@NonNull InsetsAnimation animation) {
default void onFinish(@NonNull InsetsAnimation animation) {
}
/**
@@ -253,14 +294,14 @@ public interface WindowInsetsAnimationCallback {
/**
* Insets both the lower and upper bound by the specified insets. This is to be used in
* {@link WindowInsetsAnimationCallback#onStarted} to indicate that a part of the insets has
* {@link WindowInsetsAnimationCallback#onStart} to indicate that a part of the insets has
* been used to offset or clip its children, and the children shouldn't worry about that
* part anymore.
*
* @param insets The amount to inset.
* @return A copy of this instance inset in the given directions.
* @see WindowInsets#inset
* @see WindowInsetsAnimationCallback#onStarted
* @see WindowInsetsAnimationCallback#onStart
*/
@NonNull
public AnimationBounds inset(@NonNull Insets insets) {

View File

@@ -16,6 +16,7 @@
package android.view;
import android.annotation.Hide;
import android.annotation.NonNull;
import android.view.WindowInsets.Type.InsetsType;
import android.view.inputmethod.EditorInfo;
@@ -25,6 +26,12 @@ import android.view.inputmethod.EditorInfo;
*/
public interface WindowInsetsAnimationControlListener {
/**
* @hide
*/
default void onPrepare(int types) {
}
/**
* Called when the animation is ready to be controlled. This may be delayed when the IME needs
* to redraw because of an {@link EditorInfo} change, or when the window is starting up.

View File

@@ -149,7 +149,8 @@ public interface WindowInsetsController {
*
* @param types The {@link InsetsType}s the application has requested to control.
* @param durationMillis duration of animation in
* {@link java.util.concurrent.TimeUnit#MILLISECONDS}
* {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
* animation doesn't have a predetermined duration.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* windows are ready to be controlled, among other callbacks.
* @hide
@@ -162,7 +163,8 @@ public interface WindowInsetsController {
* modifying the position of the IME when it's causing insets.
*
* @param durationMillis duration of the animation in
* {@link java.util.concurrent.TimeUnit#MILLISECONDS}
* {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
* animation doesn't have a predetermined duration.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* IME are ready to be controlled, among other callbacks.
*/