diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index dad7696397eed..0653b06e4b9df 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -16,8 +16,6 @@ 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; @@ -188,7 +186,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll @Override public void finish(boolean shown) { - if (mCancelled) { + if (mCancelled || mFinished) { return; } setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 2785d216d24d0..22d6f37dbd95e 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -70,7 +70,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private static final int ANIMATION_DURATION_HIDE_MS = 340; private static final int PENDING_CONTROL_TIMEOUT_MS = 2000; - static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); /** * Layout mode during insets animation: The views should be laid out as if the changing inset @@ -78,7 +78,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation * 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; + public 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 @@ -86,7 +86,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation * 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; + public 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 @@ -149,7 +149,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void set(WindowInsetsAnimationController controller, Insets value) { controller.setInsetsAndAlpha( - value, 1f /* alpha */, (((DefaultAnimationControlListener) + value, 1f /* alpha */, (((InternalAnimationControlListener) ((InsetsAnimationControlImpl) controller).getListener()) .getRawFraction())); } @@ -166,7 +166,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private ObjectAnimator mAnimator; protected boolean mShow; - InternalAnimationControlListener(boolean show) { + public InternalAnimationControlListener(boolean show) { mShow = show; } @@ -214,7 +214,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration(); } - protected long getDurationMs() { + /** + * To get the animation duration in MS. + */ + public long getDurationMs() { if (mAnimator != null) { return mAnimator.getDuration(); } diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 77bc25b4c7292..d4961eab74731 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -193,6 +193,15 @@ public class InsetsSource implements Parcelable { dest.writeBoolean(mVisible); } + @Override + public String toString() { + return "InsetsSource: {" + + "mType=" + InsetsState.typeToString(mType) + + ", mFrame=" + mFrame.toShortString() + + ", mVisible" + mVisible + + "}"; + } + public static final @android.annotation.NonNull Creator CREATOR = new Creator() { public InsetsSource createFromParcel(Parcel in) { diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index bd19799bb6643..8648682aaa2eb 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -21,7 +21,6 @@ import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME; import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; import static android.view.ViewRootImpl.sNewInsetsMode; -import static android.view.WindowInsets.Type.IME; import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; import static android.view.WindowInsets.Type.SIZE; import static android.view.WindowInsets.Type.SYSTEM_GESTURES; @@ -43,7 +42,6 @@ import android.util.ArraySet; import android.util.SparseIntArray; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; -import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import java.io.PrintWriter; @@ -366,7 +364,12 @@ public class InsetsState implements Parcelable { return result; } - static @Type.InsetsType int toPublicType(@InternalInsetsType int type) { + /** + * Converting a internal type to the public type. + * @param type internal insets type, {@code InternalInsetsType}. + * @return public insets type, {@code Type.InsetsType}. + */ + public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) { switch (type) { case ITYPE_STATUS_BAR: return Type.STATUS_BARS; @@ -510,5 +513,13 @@ public class InsetsState implements Parcelable { mSources.put(source.getType(), source); } } + + @Override + public String toString() { + return "InsetsState: {" + + "mDisplayFrame=" + mDisplayFrame + + ", mSources=" + mSources + + "}"; + } } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 2d4211aa16d86..d0179adadbd77 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -20,17 +20,31 @@ import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; +import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.SyncRtSurfaceTransactionApplier.applyParams; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; import android.annotation.Nullable; import android.app.StatusBarManager; import android.util.IntArray; +import android.util.SparseArray; +import android.view.InsetsAnimationControlCallbacks; +import android.view.InsetsAnimationControlImpl; +import android.view.InsetsController; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; +import android.view.SurfaceControl; +import android.view.SyncRtSurfaceTransactionApplier; import android.view.ViewRootImpl; +import android.view.WindowInsetsAnimationCallback; +import android.view.WindowInsetsAnimationControlListener; + +import com.android.server.DisplayThread; /** * Policy that implements who gets control over the windows generating insets. @@ -46,6 +60,8 @@ class InsetsPolicy { private WindowState mFocusedWin; private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); + private boolean mAnimatingShown; + private final float[] mTmpFloat9 = new float[9]; InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { mStateController = stateController; @@ -91,11 +107,14 @@ class InsetsPolicy { changed = true; } if (changed) { - updateBarControlTarget(mFocusedWin); - mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(), - mShowingTransientTypes.toArray()); - mStateController.notifyInsetsChanged(); - // TODO(b/118118435): Animation + startAnimation(mShowingTransientTypes, true, () -> { + synchronized (mDisplayContent.mWmService.mGlobalLock) { + mPolicy.getStatusBarManagerInternal().showTransient( + mDisplayContent.getDisplayId(), + mShowingTransientTypes.toArray()); + mStateController.notifyInsetsChanged(); + } + }); } } @@ -103,11 +122,13 @@ class InsetsPolicy { if (mShowingTransientTypes.size() == 0) { return; } - - // TODO(b/118118435): Animation - mShowingTransientTypes.clear(); - updateBarControlTarget(mFocusedWin); - mStateController.notifyInsetsChanged(); + startAnimation(mShowingTransientTypes, false, () -> { + synchronized (mDisplayContent.mWmService.mGlobalLock) { + mShowingTransientTypes.clear(); + mStateController.notifyInsetsChanged(); + updateBarControlTarget(mFocusedWin); + } + }); } boolean isTransient(@InternalInsetsType int type) { @@ -247,6 +268,29 @@ class InsetsPolicy { return isDockedStackVisible || isFreeformStackVisible || isResizing; } + private void startAnimation(IntArray internalTypes, boolean show, Runnable callback) { + int typesReady = 0; + final SparseArray controls = new SparseArray<>(); + updateBarControlTarget(mFocusedWin); + for (int i = internalTypes.size() - 1; i >= 0; i--) { + InsetsSourceProvider provider = + mStateController.getSourceProvider(internalTypes.get(i)); + if (provider == null) continue; + InsetsSourceControl control = provider.getControl(provider.getControlTarget()); + if (control == null || control.getLeash() == null) continue; + typesReady |= InsetsState.toPublicType(internalTypes.get(i)); + controls.put(control.getType(), control); + } + controlAnimationUnchecked(typesReady, controls, show, callback); + } + + private void controlAnimationUnchecked(int typesReady, + SparseArray controls, boolean show, Runnable callback) { + InsetsPolicyAnimationControlListener listener = + new InsetsPolicyAnimationControlListener(show, callback); + listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show); + } + private class BarWindow { private final int mId; @@ -267,7 +311,103 @@ class InsetsPolicy { } } - // TODO(b/118118435): Implement animations for it (with SurfaceAnimator) + private class InsetsPolicyAnimationControlListener extends + InsetsController.InternalAnimationControlListener { + Runnable mFinishCallback; + InsetsPolicyAnimationControlCallbacks mControlCallbacks; + + InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) { + super(show); + mFinishCallback = finishCallback; + mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); + } + + @Override + protected void onAnimationFinish() { + super.onAnimationFinish(); + mControlCallbacks.mAnimationControl.finish(mAnimatingShown); + DisplayThread.getHandler().post(mFinishCallback); + } + + private class InsetsPolicyAnimationControlCallbacks implements + InsetsAnimationControlCallbacks { + private InsetsAnimationControlImpl mAnimationControl = null; + private InsetsPolicyAnimationControlListener mListener; + + InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) { + super(); + mListener = listener; + } + + private void controlAnimationUnchecked(int typesReady, + SparseArray controls, boolean show) { + if (typesReady == 0) { + // nothing to animate. + return; + } + mAnimatingShown = show; + + mAnimationControl = new InsetsAnimationControlImpl(controls, + mFocusedWin.getDisplayContent().getBounds(), getState(), + mListener, typesReady, this, mListener.getDurationMs(), + InsetsController.INTERPOLATOR, true, + show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN + : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN); + } + + /** Called on SurfaceAnimationThread lock without global WM lock held. */ + @Override + public void scheduleApplyChangeInsets() { + InsetsState state = getState(); + if (mAnimationControl.applyChangeInsets(state)) { + mAnimationControl.finish(mAnimatingShown); + } + } + + @Override + public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) { + // Nothing's needed here. Finish steps is handled in the listener + // onAnimationFinished callback. + } + + /** + * This method will return a state with fullscreen frame override. No need to make copy + * after getting state from this method. + * @return The client insets state with full display frame override. + */ + private InsetsState getState() { + // To animate the transient animation correctly, we need to let the state hold + // the full display frame. + InsetsState overrideState = new InsetsState(mFocusedWin.getRequestedInsetsState(), + true); + overrideState.setDisplayFrame(mFocusedWin.getDisplayContent().getBounds()); + return overrideState; + } + + /** Called on SurfaceAnimationThread lock without global WM lock held. */ + @Override + public void applySurfaceParams( + final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (int i = params.length - 1; i >= 0; i--) { + SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i]; + applyParams(t, surfaceParams, mTmpFloat9); + } + t.apply(); + } + + /** Called on SurfaceAnimationThread lock without global WM lock held. */ + @Override + public void startAnimation(InsetsAnimationControlImpl controller, + WindowInsetsAnimationControlListener listener, int types, + WindowInsetsAnimationCallback.InsetsAnimation animation, + WindowInsetsAnimationCallback.AnimationBounds bounds, + int layoutDuringAnimation) { + SurfaceAnimationThread.getHandler().post(() -> listener.onReady(controller, types)); + } + } + } + private class TransientControlTarget implements InsetsControlTarget { @Override