Loading api/current.txt +6 −4 Original line number Original line Diff line number Diff line Loading @@ -51607,9 +51607,10 @@ package android.view { method public boolean dispatchUnhandledMove(android.view.View, int); method public boolean dispatchUnhandledMove(android.view.View, int); method protected void dispatchVisibilityChanged(@NonNull android.view.View, int); method protected void dispatchVisibilityChanged(@NonNull android.view.View, int); method public void dispatchWindowFocusChanged(boolean); method public void dispatchWindowFocusChanged(boolean); method public void dispatchWindowInsetsAnimationFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method public void dispatchWindowInsetsAnimationFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method public void dispatchWindowInsetsAnimationPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets); method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets); method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); method public void dispatchWindowSystemUiVisiblityChanged(int); method public void dispatchWindowSystemUiVisiblityChanged(int); method public void dispatchWindowVisibilityChanged(int); method public void dispatchWindowVisibilityChanged(int); method @CallSuper public void draw(android.graphics.Canvas); method @CallSuper public void draw(android.graphics.Canvas); Loading Loading @@ -53289,9 +53290,10 @@ package android.view { } } public interface WindowInsetsAnimationCallback { public interface WindowInsetsAnimationCallback { method public default void onFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method public default void onFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method public default void onPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets); method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets); method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); } } public static final class WindowInsetsAnimationCallback.AnimationBounds { public static final class WindowInsetsAnimationCallback.AnimationBounds { core/java/android/view/InsetsAnimationControlCallbacks.java +15 −4 Original line number Original line Diff line number Diff line Loading @@ -16,17 +16,28 @@ package android.view; 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. * Provide an interface to let InsetsAnimationControlImpl call back into its owner. * @hide * @hide */ */ public interface InsetsAnimationControlCallbacks { public interface InsetsAnimationControlCallbacks { /** /** * Dispatch the animation started event to all listeners. * Executes the necessary code to start the animation in the correct order, including: * @param animation * <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, void startAnimation(InsetsAnimationControlImpl controller, WindowInsetsAnimationCallback.AnimationBounds bounds); WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation, AnimationBounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation); /** /** * Schedule the apply by posting the animation callback. * Schedule the apply by posting the animation callback. Loading core/java/android/view/InsetsAnimationControlImpl.java +7 −6 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.view; 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_BOTTOM; import static android.view.InsetsState.ISIDE_FLOATING; import static android.view.InsetsState.ISIDE_FLOATING; import static android.view.InsetsState.ISIDE_LEFT; import static android.view.InsetsState.ISIDE_LEFT; Loading @@ -30,6 +32,7 @@ import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseIntArray; import android.util.SparseSetArray; import android.util.SparseSetArray; import android.view.InsetsController.LayoutInsetsDuringAnimation; import android.view.InsetsState.InternalInsetsSide; import android.view.InsetsState.InternalInsetsSide; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsets.Type.InsetsType; Loading Loading @@ -80,7 +83,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame, public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs, boolean fade) { InsetsAnimationControlCallbacks controller, long durationMs, boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { mControls = controls; mControls = controls; mListener = listener; mListener = listener; mTypes = types; mTypes = types; Loading @@ -95,14 +99,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mFrame = new Rect(frame); mFrame = new Rect(frame); buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls); buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls); // TODO: Check for controllability first and wait for IME if needed. listener.onReady(this, types); mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes, mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes, InsetsController.INTERPOLATOR, durationMs); InsetsController.INTERPOLATOR, durationMs); mAnimation.setAlpha(getCurrentAlpha()); mAnimation.setAlpha(getCurrentAlpha()); mController.dispatchAnimationStarted(mAnimation, mController.startAnimation(this, listener, types, mAnimation, new AnimationBounds(mHiddenInsets, mShownInsets)); new AnimationBounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation); } } @Override @Override Loading core/java/android/view/InsetsController.java +98 −15 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.util.SparseArray; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsState.InternalInsetsType; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; import android.view.SurfaceControl.Transaction; import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimationCallback.AnimationBounds; import android.view.WindowInsetsAnimationCallback.AnimationBounds; Loading @@ -47,6 +48,8 @@ import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.ArrayList; /** /** Loading @@ -66,6 +69,37 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE}) @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE}) private @interface AnimationDirection{} 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. * Translation animation evaluator. */ */ Loading Loading @@ -109,11 +143,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override @Override public void onReady(WindowInsetsAnimationController controller, int types) { public void onReady(WindowInsetsAnimationController controller, int types) { mController = controller; mController = controller; if (mShow) { showDirectly(types); } else { hideDirectly(types); } mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE; mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE; mAnimator = ObjectAnimator.ofObject( mAnimator = ObjectAnimator.ofObject( controller, controller, Loading @@ -131,7 +161,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation onAnimationFinish(); onAnimationFinish(); } } }); }); mStartingAnimation = true; mAnimator.start(); mAnimator.start(); mStartingAnimation = false; } } @Override @Override Loading Loading @@ -185,6 +217,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private int mPendingTypesToShow; private int mPendingTypesToShow; private int mLastLegacySoftInputMode; private int mLastLegacySoftInputMode; private boolean mStartingAnimation; private SyncRtSurfaceTransactionApplier mApplier; private SyncRtSurfaceTransactionApplier mApplier; Loading Loading @@ -312,7 +345,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // Only one animator (with multiple InsetsType) can run at a time. // Only one animator (with multiple InsetsType) can run at a time. // previous one should be cancelled for simplicity. // previous one should be cancelled for simplicity. cancelExistingAnimation(); cancelExistingAnimation(); } else if (consumer.isVisible() } else if (consumer.isRequestedVisible() && (mAnimationDirection == DIRECTION_NONE && (mAnimationDirection == DIRECTION_NONE || mAnimationDirection == DIRECTION_HIDE)) { || mAnimationDirection == DIRECTION_HIDE)) { // no-op: already shown or animating in (because window visibility is // no-op: already shown or animating in (because window visibility is Loading @@ -338,7 +371,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); if (mAnimationDirection == DIRECTION_SHOW) { if (mAnimationDirection == DIRECTION_SHOW) { cancelExistingAnimation(); cancelExistingAnimation(); } else if (!consumer.isVisible() } else if (!consumer.isRequestedVisible() && (mAnimationDirection == DIRECTION_NONE && (mAnimationDirection == DIRECTION_NONE || mAnimationDirection == DIRECTION_HIDE)) { || mAnimationDirection == DIRECTION_HIDE)) { // no-op: already hidden or animating out. // no-op: already hidden or animating out. Loading @@ -363,12 +396,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation listener.onCancelled(); listener.onCancelled(); return; 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, private void controlAnimationUnchecked(@InsetsType int types, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, long durationMs, boolean fade) { long durationMs, boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { if (types == 0) { if (types == 0) { // nothing to animate. // nothing to animate. return; return; Loading Loading @@ -398,7 +433,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls, final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls, frame, mState, listener, typesReady, this, durationMs, fade); frame, mState, listener, typesReady, this, durationMs, fade, layoutInsetsDuringAnimation); mAnimationControls.add(controller); mAnimationControls.add(controller); } } Loading @@ -412,7 +448,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean isReady = true; boolean isReady = true; for (int i = internalTypes.size() - 1; i >= 0; i--) { for (int i = internalTypes.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); boolean setVisible = !consumer.isVisible(); boolean setVisible = !consumer.isRequestedVisible(); if (setVisible) { if (setVisible) { // Show request // Show request switch(consumer.requestShow(fromIme)) { switch(consumer.requestShow(fromIme)) { Loading Loading @@ -454,6 +490,29 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return typesReady; 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) { private void cancelExistingControllers(@InsetsType int types) { for (int i = mAnimationControls.size() - 1; i >= 0; i--) { for (int i = mAnimationControls.size() - 1; i >= 0; i--) { InsetsAnimationControlImpl control = mAnimationControls.get(i); InsetsAnimationControlImpl control = mAnimationControls.get(i); Loading Loading @@ -597,7 +656,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // and hidden state insets are correct. // and hidden state insets are correct. controlAnimationUnchecked( controlAnimationUnchecked( types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(), 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) { private void hideDirectly(@InsetsType int types) { Loading Loading @@ -629,18 +690,40 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @VisibleForTesting @Override @Override public void dispatchAnimationStarted(InsetsAnimation animation, AnimationBounds bounds) { public void startAnimation(InsetsAnimationControlImpl controller, mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation, bounds); 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 @VisibleForTesting public void dispatchAnimationFinished(InsetsAnimation animation) { public void dispatchAnimationFinished(InsetsAnimation animation) { mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation); mViewRoot.mView.dispatchWindowInsetsAnimationFinish(animation); } } @VisibleForTesting @VisibleForTesting @Override @Override public void scheduleApplyChangeInsets() { public void scheduleApplyChangeInsets() { if (mStartingAnimation) { mAnimCallback.run(); mAnimCallbackScheduled = false; return; } if (!mAnimCallbackScheduled) { if (!mAnimCallbackScheduled) { mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, mAnimCallback, null /* token*/); mAnimCallback, null /* token*/); Loading core/java/android/view/InsetsSourceConsumer.java +16 −12 Original line number Original line Diff line number Diff line Loading @@ -53,7 +53,7 @@ public class InsetsSourceConsumer { } } protected final InsetsController mController; protected final InsetsController mController; protected boolean mVisible; protected boolean mRequestedVisible; private final Supplier<Transaction> mTransactionSupplier; private final Supplier<Transaction> mTransactionSupplier; private final @InternalInsetsType int mType; private final @InternalInsetsType int mType; private final InsetsState mState; private final InsetsState mState; Loading @@ -66,7 +66,7 @@ public class InsetsSourceConsumer { mState = state; mState = state; mTransactionSupplier = transactionSupplier; mTransactionSupplier = transactionSupplier; mController = controller; mController = controller; mVisible = InsetsState.getDefaultVisibility(type); mRequestedVisible = InsetsState.getDefaultVisibility(type); } } public void setControl(@Nullable InsetsSourceControl control) { public void setControl(@Nullable InsetsSourceControl control) { Loading Loading @@ -94,12 +94,12 @@ public class InsetsSourceConsumer { @VisibleForTesting @VisibleForTesting public void show() { public void show() { setVisible(true); setRequestedVisible(true); } } @VisibleForTesting @VisibleForTesting public void hide() { public void hide() { setVisible(false); setRequestedVisible(false); } } /** /** Loading @@ -126,16 +126,16 @@ public class InsetsSourceConsumer { if (mSourceControl == null) { if (mSourceControl == null) { return false; return false; } } if (mState.getSource(mType).isVisible() == mVisible) { if (mState.getSource(mType).isVisible() == mRequestedVisible) { return false; return false; } } mState.getSource(mType).setVisible(mVisible); mState.getSource(mType).setVisible(mRequestedVisible); return true; return true; } } @VisibleForTesting @VisibleForTesting public boolean isVisible() { public boolean isRequestedVisible() { return mVisible; return mRequestedVisible; } } /** /** Loading @@ -157,11 +157,15 @@ public class InsetsSourceConsumer { // no-op for types that always return ShowResult#SHOW_IMMEDIATELY. // 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; return; } } mVisible = visible; mRequestedVisible = requestedVisible; applyLocalVisibilityOverride(); applyLocalVisibilityOverride(); mController.notifyVisibilityChanged(); mController.notifyVisibilityChanged(); } } Loading @@ -173,7 +177,7 @@ public class InsetsSourceConsumer { } } final Transaction t = mTransactionSupplier.get(); final Transaction t = mTransactionSupplier.get(); if (mVisible) { if (mRequestedVisible) { t.show(mSourceControl.getLeash()); t.show(mSourceControl.getLeash()); } else { } else { t.hide(mSourceControl.getLeash()); t.hide(mSourceControl.getLeash()); Loading Loading
api/current.txt +6 −4 Original line number Original line Diff line number Diff line Loading @@ -51607,9 +51607,10 @@ package android.view { method public boolean dispatchUnhandledMove(android.view.View, int); method public boolean dispatchUnhandledMove(android.view.View, int); method protected void dispatchVisibilityChanged(@NonNull android.view.View, int); method protected void dispatchVisibilityChanged(@NonNull android.view.View, int); method public void dispatchWindowFocusChanged(boolean); method public void dispatchWindowFocusChanged(boolean); method public void dispatchWindowInsetsAnimationFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method public void dispatchWindowInsetsAnimationFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method public void dispatchWindowInsetsAnimationPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets); method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets); method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); method public void dispatchWindowSystemUiVisiblityChanged(int); method public void dispatchWindowSystemUiVisiblityChanged(int); method public void dispatchWindowVisibilityChanged(int); method public void dispatchWindowVisibilityChanged(int); method @CallSuper public void draw(android.graphics.Canvas); method @CallSuper public void draw(android.graphics.Canvas); Loading Loading @@ -53289,9 +53290,10 @@ package android.view { } } public interface WindowInsetsAnimationCallback { public interface WindowInsetsAnimationCallback { method public default void onFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method public default void onFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method public default void onPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets); method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets); method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); } } public static final class WindowInsetsAnimationCallback.AnimationBounds { public static final class WindowInsetsAnimationCallback.AnimationBounds {
core/java/android/view/InsetsAnimationControlCallbacks.java +15 −4 Original line number Original line Diff line number Diff line Loading @@ -16,17 +16,28 @@ package android.view; 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. * Provide an interface to let InsetsAnimationControlImpl call back into its owner. * @hide * @hide */ */ public interface InsetsAnimationControlCallbacks { public interface InsetsAnimationControlCallbacks { /** /** * Dispatch the animation started event to all listeners. * Executes the necessary code to start the animation in the correct order, including: * @param animation * <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, void startAnimation(InsetsAnimationControlImpl controller, WindowInsetsAnimationCallback.AnimationBounds bounds); WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation, AnimationBounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation); /** /** * Schedule the apply by posting the animation callback. * Schedule the apply by posting the animation callback. Loading
core/java/android/view/InsetsAnimationControlImpl.java +7 −6 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.view; 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_BOTTOM; import static android.view.InsetsState.ISIDE_FLOATING; import static android.view.InsetsState.ISIDE_FLOATING; import static android.view.InsetsState.ISIDE_LEFT; import static android.view.InsetsState.ISIDE_LEFT; Loading @@ -30,6 +32,7 @@ import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseIntArray; import android.util.SparseSetArray; import android.util.SparseSetArray; import android.view.InsetsController.LayoutInsetsDuringAnimation; import android.view.InsetsState.InternalInsetsSide; import android.view.InsetsState.InternalInsetsSide; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsets.Type.InsetsType; Loading Loading @@ -80,7 +83,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame, public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs, boolean fade) { InsetsAnimationControlCallbacks controller, long durationMs, boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { mControls = controls; mControls = controls; mListener = listener; mListener = listener; mTypes = types; mTypes = types; Loading @@ -95,14 +99,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mFrame = new Rect(frame); mFrame = new Rect(frame); buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls); buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls); // TODO: Check for controllability first and wait for IME if needed. listener.onReady(this, types); mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes, mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes, InsetsController.INTERPOLATOR, durationMs); InsetsController.INTERPOLATOR, durationMs); mAnimation.setAlpha(getCurrentAlpha()); mAnimation.setAlpha(getCurrentAlpha()); mController.dispatchAnimationStarted(mAnimation, mController.startAnimation(this, listener, types, mAnimation, new AnimationBounds(mHiddenInsets, mShownInsets)); new AnimationBounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation); } } @Override @Override Loading
core/java/android/view/InsetsController.java +98 −15 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.util.SparseArray; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsState.InternalInsetsType; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; import android.view.SurfaceControl.Transaction; import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimationCallback.AnimationBounds; import android.view.WindowInsetsAnimationCallback.AnimationBounds; Loading @@ -47,6 +48,8 @@ import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.ArrayList; /** /** Loading @@ -66,6 +69,37 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE}) @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE}) private @interface AnimationDirection{} 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. * Translation animation evaluator. */ */ Loading Loading @@ -109,11 +143,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override @Override public void onReady(WindowInsetsAnimationController controller, int types) { public void onReady(WindowInsetsAnimationController controller, int types) { mController = controller; mController = controller; if (mShow) { showDirectly(types); } else { hideDirectly(types); } mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE; mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE; mAnimator = ObjectAnimator.ofObject( mAnimator = ObjectAnimator.ofObject( controller, controller, Loading @@ -131,7 +161,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation onAnimationFinish(); onAnimationFinish(); } } }); }); mStartingAnimation = true; mAnimator.start(); mAnimator.start(); mStartingAnimation = false; } } @Override @Override Loading Loading @@ -185,6 +217,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private int mPendingTypesToShow; private int mPendingTypesToShow; private int mLastLegacySoftInputMode; private int mLastLegacySoftInputMode; private boolean mStartingAnimation; private SyncRtSurfaceTransactionApplier mApplier; private SyncRtSurfaceTransactionApplier mApplier; Loading Loading @@ -312,7 +345,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // Only one animator (with multiple InsetsType) can run at a time. // Only one animator (with multiple InsetsType) can run at a time. // previous one should be cancelled for simplicity. // previous one should be cancelled for simplicity. cancelExistingAnimation(); cancelExistingAnimation(); } else if (consumer.isVisible() } else if (consumer.isRequestedVisible() && (mAnimationDirection == DIRECTION_NONE && (mAnimationDirection == DIRECTION_NONE || mAnimationDirection == DIRECTION_HIDE)) { || mAnimationDirection == DIRECTION_HIDE)) { // no-op: already shown or animating in (because window visibility is // no-op: already shown or animating in (because window visibility is Loading @@ -338,7 +371,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); if (mAnimationDirection == DIRECTION_SHOW) { if (mAnimationDirection == DIRECTION_SHOW) { cancelExistingAnimation(); cancelExistingAnimation(); } else if (!consumer.isVisible() } else if (!consumer.isRequestedVisible() && (mAnimationDirection == DIRECTION_NONE && (mAnimationDirection == DIRECTION_NONE || mAnimationDirection == DIRECTION_HIDE)) { || mAnimationDirection == DIRECTION_HIDE)) { // no-op: already hidden or animating out. // no-op: already hidden or animating out. Loading @@ -363,12 +396,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation listener.onCancelled(); listener.onCancelled(); return; 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, private void controlAnimationUnchecked(@InsetsType int types, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, long durationMs, boolean fade) { long durationMs, boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { if (types == 0) { if (types == 0) { // nothing to animate. // nothing to animate. return; return; Loading Loading @@ -398,7 +433,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls, final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls, frame, mState, listener, typesReady, this, durationMs, fade); frame, mState, listener, typesReady, this, durationMs, fade, layoutInsetsDuringAnimation); mAnimationControls.add(controller); mAnimationControls.add(controller); } } Loading @@ -412,7 +448,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean isReady = true; boolean isReady = true; for (int i = internalTypes.size() - 1; i >= 0; i--) { for (int i = internalTypes.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); boolean setVisible = !consumer.isVisible(); boolean setVisible = !consumer.isRequestedVisible(); if (setVisible) { if (setVisible) { // Show request // Show request switch(consumer.requestShow(fromIme)) { switch(consumer.requestShow(fromIme)) { Loading Loading @@ -454,6 +490,29 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return typesReady; 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) { private void cancelExistingControllers(@InsetsType int types) { for (int i = mAnimationControls.size() - 1; i >= 0; i--) { for (int i = mAnimationControls.size() - 1; i >= 0; i--) { InsetsAnimationControlImpl control = mAnimationControls.get(i); InsetsAnimationControlImpl control = mAnimationControls.get(i); Loading Loading @@ -597,7 +656,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // and hidden state insets are correct. // and hidden state insets are correct. controlAnimationUnchecked( controlAnimationUnchecked( types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(), 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) { private void hideDirectly(@InsetsType int types) { Loading Loading @@ -629,18 +690,40 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @VisibleForTesting @Override @Override public void dispatchAnimationStarted(InsetsAnimation animation, AnimationBounds bounds) { public void startAnimation(InsetsAnimationControlImpl controller, mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation, bounds); 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 @VisibleForTesting public void dispatchAnimationFinished(InsetsAnimation animation) { public void dispatchAnimationFinished(InsetsAnimation animation) { mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation); mViewRoot.mView.dispatchWindowInsetsAnimationFinish(animation); } } @VisibleForTesting @VisibleForTesting @Override @Override public void scheduleApplyChangeInsets() { public void scheduleApplyChangeInsets() { if (mStartingAnimation) { mAnimCallback.run(); mAnimCallbackScheduled = false; return; } if (!mAnimCallbackScheduled) { if (!mAnimCallbackScheduled) { mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, mAnimCallback, null /* token*/); mAnimCallback, null /* token*/); Loading
core/java/android/view/InsetsSourceConsumer.java +16 −12 Original line number Original line Diff line number Diff line Loading @@ -53,7 +53,7 @@ public class InsetsSourceConsumer { } } protected final InsetsController mController; protected final InsetsController mController; protected boolean mVisible; protected boolean mRequestedVisible; private final Supplier<Transaction> mTransactionSupplier; private final Supplier<Transaction> mTransactionSupplier; private final @InternalInsetsType int mType; private final @InternalInsetsType int mType; private final InsetsState mState; private final InsetsState mState; Loading @@ -66,7 +66,7 @@ public class InsetsSourceConsumer { mState = state; mState = state; mTransactionSupplier = transactionSupplier; mTransactionSupplier = transactionSupplier; mController = controller; mController = controller; mVisible = InsetsState.getDefaultVisibility(type); mRequestedVisible = InsetsState.getDefaultVisibility(type); } } public void setControl(@Nullable InsetsSourceControl control) { public void setControl(@Nullable InsetsSourceControl control) { Loading Loading @@ -94,12 +94,12 @@ public class InsetsSourceConsumer { @VisibleForTesting @VisibleForTesting public void show() { public void show() { setVisible(true); setRequestedVisible(true); } } @VisibleForTesting @VisibleForTesting public void hide() { public void hide() { setVisible(false); setRequestedVisible(false); } } /** /** Loading @@ -126,16 +126,16 @@ public class InsetsSourceConsumer { if (mSourceControl == null) { if (mSourceControl == null) { return false; return false; } } if (mState.getSource(mType).isVisible() == mVisible) { if (mState.getSource(mType).isVisible() == mRequestedVisible) { return false; return false; } } mState.getSource(mType).setVisible(mVisible); mState.getSource(mType).setVisible(mRequestedVisible); return true; return true; } } @VisibleForTesting @VisibleForTesting public boolean isVisible() { public boolean isRequestedVisible() { return mVisible; return mRequestedVisible; } } /** /** Loading @@ -157,11 +157,15 @@ public class InsetsSourceConsumer { // no-op for types that always return ShowResult#SHOW_IMMEDIATELY. // 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; return; } } mVisible = visible; mRequestedVisible = requestedVisible; applyLocalVisibilityOverride(); applyLocalVisibilityOverride(); mController.notifyVisibilityChanged(); mController.notifyVisibilityChanged(); } } Loading @@ -173,7 +177,7 @@ public class InsetsSourceConsumer { } } final Transaction t = mTransactionSupplier.get(); final Transaction t = mTransactionSupplier.get(); if (mVisible) { if (mRequestedVisible) { t.show(mSourceControl.getLeash()); t.show(mSourceControl.getLeash()); } else { } else { t.hide(mSourceControl.getLeash()); t.hide(mSourceControl.getLeash()); Loading