From abeab16373216a5fedf773a322a8283b4aebf324 Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Wed, 27 Oct 2021 19:34:54 +0200 Subject: [PATCH] Account for task and inset crops in default legacy task animations This is to make sure that the task animation looks good and consistent when the taskbar is and isn't visible Test: Check task transitions animations look ok both when taskbar is and isn't present Bug: 200675009 Change-Id: I8ff70271a81dcdf329849907175691f901edc1d9 --- .../java/android/view/TaskTransitionSpec.java | 1 + .../server/wm/InsetsSourceProvider.java | 71 +++++++++++++++++++ .../core/java/com/android/server/wm/Task.java | 27 +++++++ .../android/server/wm/WindowContainer.java | 29 ++++++++ 4 files changed, 128 insertions(+) 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) -> {