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:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*/);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user