diff --git a/core/java/android/view/TaskTransitionSpec.java b/core/java/android/view/TaskTransitionSpec.java index e90d6e10f846f..5f498a19f196c 100644 --- a/core/java/android/view/TaskTransitionSpec.java +++ b/core/java/android/view/TaskTransitionSpec.java @@ -39,6 +39,7 @@ public class TaskTransitionSpec implements Parcelable { /** * TEMPORARY FIELD (b/202383002) * TODO: Remove once we use surfaceflinger rounded corners on tasks rather than taskbar overlays + * or when shell transitions are fully enabled * * A set of {@InsetsState.InternalInsetsType}s we want to use as the source to set the bounds * of the task during the animation. Used to make sure that task animate above the taskbar. diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index d202587bd3069..3948eeec20b04 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -108,6 +108,16 @@ class InsetsSourceProvider { private final boolean mControllable; + /** + * Whether to forced the dimensions of the source window to the inset frame and crop out any + * overflow. + * Used to crop the taskbar inset source when a task animation is occurring to hide the taskbar + * rounded corners overlays. + * + * TODO: Remove when we enable shell transitions (b/202383002) + */ + private boolean mCropToProvidingInsets = false; + InsetsSourceProvider(InsetsSource source, InsetsStateController stateController, DisplayContent displayContent) { mClientVisible = InsetsState.getDefaultVisibility(source.getType()); @@ -303,6 +313,62 @@ class InsetsSourceProvider { mFakeControlTarget = fakeTarget; } + /** + * Ensures that the inset source window is cropped so that anything that doesn't fit within the + * inset frame is cropped out until removeCropToProvidingInsetsBounds is called. + * + * The inset source surface will get cropped to the be of the size of the insets it's providing. + * + * For example, for the taskbar window which serves as the ITYPE_EXTRA_NAVIGATION_BAR inset + * source, the window is larger than the insets because of the rounded corners overlay, but + * during task animations we want to make sure that the overlay is cropped out of the window so + * that they don't hide the window animations. + * + * @param t The transaction to use to apply immediate overflow cropping operations. + * + * NOTE: The relies on the inset source window to have a leash (usually this would be a leash + * for the ANIMATION_TYPE_INSETS_CONTROL animation if the inset is controlled by the client) + * + * TODO: Remove when we migrate over to shell transitions (b/202383002) + */ + void setCropToProvidingInsetsBounds(Transaction t) { + mCropToProvidingInsets = true; + + if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) { + // apply to existing leash + t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, getProvidingInsetsBoundsCropRect()); + } + } + + /** + * Removes any overflow cropping and future cropping to the inset source window's leash that may + * have been set with a call to setCropToProvidingInsetsBounds(). + * @param t The transaction to use to apply immediate removal of overflow cropping. + * + * TODO: Remove when we migrate over to shell transitions (b/202383002) + */ + void removeCropToProvidingInsetsBounds(Transaction t) { + mCropToProvidingInsets = false; + + // apply to existing leash + if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) { + t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, null); + } + } + + private Rect getProvidingInsetsBoundsCropRect() { + Rect sourceWindowFrame = mWin.getFrame(); + Rect insetFrame = getSource().getFrame(); + + // The rectangle in buffer space we want to crop to + return new Rect( + insetFrame.left - sourceWindowFrame.left, + insetFrame.top - sourceWindowFrame.top, + insetFrame.right - sourceWindowFrame.left, + insetFrame.bottom - sourceWindowFrame.top + ); + } + void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) { if (mSeamlessRotating) { // We are un-rotating the window against the display rotation. We don't want the target @@ -548,6 +614,11 @@ class InsetsSourceProvider { mCapturedLeash = animationLeash; t.setPosition(mCapturedLeash, mSurfacePosition.x, mSurfacePosition.y); + + if (mCropToProvidingInsets) { + // Apply crop to hide overflow + t.setWindowCrop(mCapturedLeash, getProvidingInsetsBoundsCropRect()); + } } @Override diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ba2da06efab4a..a799e7c18703c 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -155,6 +155,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; @@ -178,9 +179,11 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; +import android.view.InsetsState; import android.view.RemoteAnimationAdapter; import android.view.Surface; import android.view.SurfaceControl; +import android.view.TaskTransitionSpec; import android.view.WindowManager; import android.view.WindowManager.TransitionOldType; import android.window.ITaskOrganizer; @@ -2819,6 +2822,30 @@ class Task extends TaskFragment { return; } + /** + * Account for specified insets to crop the animation bounds by to avoid the animation + * occurring over "out of bounds" regions + * + * For example this is used to make sure the tasks are cropped to be fully above the + * taskbar when animating. + * + * @param animationBounds The animations bounds to adjust to account for the custom spec insets. + */ + void adjustAnimationBoundsForTransition(Rect animationBounds) { + TaskTransitionSpec spec = mWmService.mTaskTransitionSpec; + if (spec != null) { + for (@InsetsState.InternalInsetsType int insetType : spec.animationBoundInsets) { + InsetsSourceProvider insetProvider = getDisplayContent() + .getInsetsStateController() + .getSourceProvider(insetType); + + Insets insets = insetProvider.getSource().calculateVisibleInsets( + animationBounds); + animationBounds.inset(insets); + } + } + } + void setDragResizing(boolean dragResizing, int dragResizeMode) { if (mDragResizing != dragResizing) { // No need to check if the mode is allowed if it's leaving dragResize diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a68b09e2e307a..7f6b493fc520a 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -108,6 +108,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -2731,6 +2732,9 @@ class WindowContainer extends ConfigurationContainer< // Separate position and size for use in animators. final Rect screenBounds = getAnimationBounds(appRootTaskClipMode); mTmpRect.set(screenBounds); + if (this.asTask() != null && isTaskTransitOld(transit)) { + this.asTask().adjustAnimationBoundsForTransition(mTmpRect); + } getAnimationPosition(mTmpPoint); mTmpRect.offsetTo(0, 0); @@ -2826,6 +2830,11 @@ class WindowContainer extends ConfigurationContainer< if (isTaskTransitOld(transit)) { animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor()); + // TODO: Remove when we migrate to shell (b/202383002) + if (mWmService.mTaskTransitionSpec != null) { + animationRunnerBuilder.hideInsetSourceViewOverflows( + mWmService.mTaskTransitionSpec.animationBoundInsets); + } } animationRunnerBuilder.build() @@ -3570,6 +3579,26 @@ class WindowContainer extends ConfigurationContainer< } } + private void hideInsetSourceViewOverflows(Set insetTypes) { + final ArrayList surfaceControls = + new ArrayList<>(insetTypes.size()); + + for (int insetType : insetTypes) { + InsetsSourceProvider insetProvider = getDisplayContent().getInsetsStateController() + .getSourceProvider(insetType); + + // Will apply it immediately to current leash and to all future inset animations + // until we disable it. + insetProvider.setCropToProvidingInsetsBounds(getPendingTransaction()); + + // Only clear the size restriction of the inset once the surface animation is over + // and not if it's canceled to be replace by another animation. + mOnAnimationFinished.add(() -> { + insetProvider.removeCropToProvidingInsetsBounds(getPendingTransaction()); + }); + } + } + private IAnimationStarter build() { return (Transaction t, AnimationAdapter adapter, boolean hidden, @AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {