From 18f622f08ce84bfb1a6cf19bca33d3ef0ad4aca9 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 8 May 2017 11:20:43 -0700 Subject: [PATCH] Preserve non-floating state when entering pinned stack. When transitioning between the fullscreen and pinned states we often have a situation where we go from having a navigation and status bar in the window to not. We'd like to use the source bounds animation to crop these out rather than a sudden jump or scaling but in order to do so we need to ensure they last until the end of the animation. We track this state, and return the appropriate value from isFloating. Furthermore, we add support to the bounds animation to use the content frame as a source bounds when there is no source bounds present, this means that we can crop out the navigation and status bar so they will be invisible by the end of the animation. Bug: 37531386 Test: Manual Change-Id: I72c549e3a3318534428d17b68ebee5832c32e6d7 --- core/java/android/app/Activity.java | 17 ------- .../android/app/PictureInPictureArgs.java | 12 ----- .../android/app/PictureInPictureParams.java | 50 +------------------ core/java/android/view/ViewRootImpl.java | 10 ---- .../server/am/ActivityManagerService.java | 6 --- .../core/java/com/android/server/wm/Task.java | 23 ++++++++- .../java/com/android/server/wm/TaskStack.java | 19 +++++-- .../server/wm/WindowManagerService.java | 1 + .../com/android/server/wm/WindowState.java | 10 ++++ .../server/wm/WindowStateAnimator.java | 36 +++++++++++-- 10 files changed, 81 insertions(+), 103 deletions(-) diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ad272b8b83b33..1d46804e5fd3a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2092,7 +2092,6 @@ public class Activity extends ContextThemeWrapper if (params == null) { throw new IllegalArgumentException("Expected non-null picture-in-picture params"); } - updatePictureInPictureParamsForContentInsets(params); return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, params); } catch (RemoteException e) { return false; @@ -2116,7 +2115,6 @@ public class Activity extends ContextThemeWrapper if (params == null) { throw new IllegalArgumentException("Expected non-null picture-in-picture params"); } - updatePictureInPictureParamsForContentInsets(params); ActivityManagerNative.getDefault().setPictureInPictureParams(mToken, params); } catch (RemoteException e) { } @@ -2136,21 +2134,6 @@ public class Activity extends ContextThemeWrapper } } - /** - * Updates the provided {@param params} with the last known content insets for this activity, to - * be used with the source hint rect for the transition into PiP. - */ - private void updatePictureInPictureParamsForContentInsets(PictureInPictureParams params) { - if (params != null && params.hasSourceBoundsHint() && getWindow() != null && - getWindow().peekDecorView() != null && - getWindow().peekDecorView().getViewRootImpl() != null) { - params.setSourceRectHintInsets( - getWindow().peekDecorView().getViewRootImpl().getLastContentInsets()); - } else { - params.setSourceRectHintInsets(null); - } - } - void dispatchMovedToDisplay(int displayId, Configuration config) { updateDisplay(displayId); onMovedToDisplay(displayId, config); diff --git a/core/java/android/app/PictureInPictureArgs.java b/core/java/android/app/PictureInPictureArgs.java index d7317f4bd1dba..cbe8bb9deea18 100644 --- a/core/java/android/app/PictureInPictureArgs.java +++ b/core/java/android/app/PictureInPictureArgs.java @@ -160,9 +160,6 @@ public final class PictureInPictureArgs implements Parcelable { if (in.readInt() != 0) { mSourceRectHint = Rect.CREATOR.createFromParcel(in); } - if (in.readInt() != 0) { - mSourceRectHintInsets = Rect.CREATOR.createFromParcel(in); - } } private PictureInPictureArgs(Rational aspectRatio, List actions, @@ -220,9 +217,6 @@ public final class PictureInPictureArgs implements Parcelable { if (otherArgs.hasSourceBoundsHint()) { mSourceRectHint = new Rect(otherArgs.getSourceRectHint()); } - if (otherArgs.hasSourceBoundsHintInsets()) { - mSourceRectHintInsets = new Rect(otherArgs.getSourceRectHintInsets()); - } } /** @@ -346,12 +340,6 @@ public final class PictureInPictureArgs implements Parcelable { } else { out.writeInt(0); } - if (mSourceRectHintInsets != null) { - out.writeInt(1); - mSourceRectHintInsets.writeToParcel(out, 0); - } else { - out.writeInt(0); - } } public static final Creator CREATOR = diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java index 323a0fbaa5b14..7313b0d941e5a 100644 --- a/core/java/android/app/PictureInPictureParams.java +++ b/core/java/android/app/PictureInPictureParams.java @@ -135,13 +135,6 @@ public final class PictureInPictureParams implements Parcelable { @Nullable private Rect mSourceRectHint; - /** - * The content insets that are used with the source hint rect for the transition into PiP where - * the insets are removed at the beginning of the transition. - */ - @Nullable - private Rect mSourceRectHintInsets; - /** {@hide} */ PictureInPictureParams() { } @@ -158,9 +151,6 @@ public final class PictureInPictureParams implements Parcelable { if (in.readInt() != 0) { mSourceRectHint = Rect.CREATOR.createFromParcel(in); } - if (in.readInt() != 0) { - mSourceRectHintInsets = Rect.CREATOR.createFromParcel(in); - } } /** {@hide} */ @@ -185,9 +175,6 @@ public final class PictureInPictureParams implements Parcelable { if (otherArgs.hasSourceBoundsHint()) { mSourceRectHint = new Rect(otherArgs.getSourceRectHint()); } - if (otherArgs.hasSourceBoundsHintInsets()) { - mSourceRectHintInsets = new Rect(otherArgs.getSourceRectHintInsets()); - } } /** @@ -240,19 +227,6 @@ public final class PictureInPictureParams implements Parcelable { } } - /** - * Sets the insets to be used with the source rect hint bounds. - * @hide - */ - @Deprecated - public void setSourceRectHintInsets(Rect insets) { - if (insets == null) { - mSourceRectHintInsets = null; - } else { - mSourceRectHintInsets = new Rect(insets); - } - } - /** * @return the source rect hint * @hide @@ -261,14 +235,6 @@ public final class PictureInPictureParams implements Parcelable { return mSourceRectHint; } - /** - * @return the source rect hint insets. - * @hide - */ - public Rect getSourceRectHintInsets() { - return mSourceRectHintInsets; - } - /** * @return whether there are launch bounds set * @hide @@ -277,14 +243,6 @@ public final class PictureInPictureParams implements Parcelable { return mSourceRectHint != null && !mSourceRectHint.isEmpty(); } - /** - * @return whether there are source rect hint insets set - * @hide - */ - public boolean hasSourceBoundsHintInsets() { - return mSourceRectHintInsets != null; - } - @Override public int describeContents() { return 0; @@ -311,12 +269,6 @@ public final class PictureInPictureParams implements Parcelable { } else { out.writeInt(0); } - if (mSourceRectHintInsets != null) { - out.writeInt(1); - mSourceRectHintInsets.writeToParcel(out, 0); - } else { - out.writeInt(0); - } } public static final Creator CREATOR = @@ -328,4 +280,4 @@ public final class PictureInPictureParams implements Parcelable { return new PictureInPictureParams[size]; } }; -} \ No newline at end of file +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1f13220289c21..f143f28ef9637 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1560,16 +1560,6 @@ public final class ViewRootImpl implements ViewParent, host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */)); } - /** - * @return the last content insets for use in adjusting the source hint rect for the - * picture-in-picture transition. - * - * @hide - */ - public Rect getLastContentInsets() { - return mAttachInfo.mContentInsets; - } - private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) { return lp.type == TYPE_STATUS_BAR_PANEL || lp.type == TYPE_INPUT_METHOD diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7304c22f0f152..ab825a63dad02 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7891,12 +7891,6 @@ public class ActivityManagerService extends IActivityManager.Stub final List actions = r.pictureInPictureArgs.getActions(); // Adjust the source bounds by the insets for the transition down final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint()); - final Rect insets = r.pictureInPictureArgs.getSourceRectHintInsets(); - if (insets != null) { - sourceBounds.offsetTo(Math.max(0, sourceBounds.left - insets.left), - Math.max(0, sourceBounds.top - insets.top)); - } - mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio, true /* moveHomeStackToFront */, "enterPictureInPictureMode"); final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 0a999e65d3a23..27661e2ef0450 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -92,6 +92,12 @@ class Task extends WindowContainer implements DimLayer.DimLayerU private TaskDescription mTaskDescription; + // If set to true, the task will report that it is not in the floating + // state regardless of it's stack affilation. As the floating state drives + // production of content insets this can be used to preserve them across + // stack moves and we in fact do so when moving from full screen to pinned. + private boolean mPreserveNonFloatingState = false; + Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, boolean homeTask, TaskDescription taskDescription, @@ -194,6 +200,16 @@ class Task extends WindowContainer implements DimLayer.DimLayerU EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "reParentTask"); final DisplayContent prevDisplayContent = getDisplayContent(); + // If we are moving from the fullscreen stack to the pinned stack + // then we want to preserve our insets so that there will not + // be a jump in the area covered by system decorations. We rely + // on the pinned animation to later unset this value. + if (stack.mStackId == PINNED_STACK_ID) { + mPreserveNonFloatingState = true; + } else { + mPreserveNonFloatingState = false; + } + getParent().removeChild(this); stack.addTask(this, position, showForAllUsers(), moveParents); @@ -593,7 +609,8 @@ class Task extends WindowContainer implements DimLayer.DimLayerU * we will have a jump at the end. */ boolean isFloating() { - return StackId.tasksAreFloating(mStack.mStackId) && !mStack.isAnimatingBoundsToFullscreen(); + return StackId.tasksAreFloating(mStack.mStackId) + && !mStack.isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState; } WindowState getTopVisibleAppMainWindow() { @@ -675,6 +692,10 @@ class Task extends WindowContainer implements DimLayer.DimLayerU return toShortString(); } + void clearPreserveNonFloatingState() { + mPreserveNonFloatingState = false; + } + @Override public String toShortString() { return "Task=" + mTaskId; diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index da7a9f0ec58ff..9d48ce58bb2a5 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -139,6 +139,8 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration(). private final Rect mBoundsAfterRotation = new Rect(); + Rect mPreAnimationBounds = new Rect(); + TaskStack(WindowManagerService service, int stackId) { mService = service; mStackId = stackId; @@ -336,6 +338,8 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye } else { mBoundsAnimationSourceHintBounds.setEmpty(); } + + mPreAnimationBounds.set(mBounds); } /** @@ -1530,10 +1534,17 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye // Hold the lock since this is called from the BoundsAnimator running on the UiThread synchronized (mService.mWindowMap) { mBoundsAnimating = false; + for (int i = 0; i < mChildren.size(); i++) { + final Task t = mChildren.get(i); + t.clearPreserveNonFloatingState(); + } mService.requestTraversal(); } if (mStackId == PINNED_STACK_ID) { + // Update to the final bounds if requested. This is done here instead of in the bounds + // animator to allow us to coordinate this after we notify the PiP mode changed + final PinnedStackWindowController controller = (PinnedStackWindowController) getController(); if (schedulePipModeChangedCallback && controller != null) { @@ -1543,8 +1554,6 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye mBoundsAnimationTarget); } - // Update to the final bounds if requested. This is done here instead of in the bounds - // animator to allow us to coordinate this after we notify the PiP mode changed if (finalStackSize != null) { setPinnedStackSize(finalStackSize, null); } @@ -1584,8 +1593,12 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye return mBoundsAnimating; } + public boolean lastAnimatingBoundsWasToFullscreen() { + return mBoundsAnimatingToFullscreen; + } + public boolean isAnimatingBoundsToFullscreen() { - return mBoundsAnimating && mBoundsAnimatingToFullscreen; + return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen(); } public boolean pinnedStackResizeDisallowed() { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 63cc9bfddb540..66be84d24700f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2083,6 +2083,7 @@ public class WindowManagerService extends IWindowManager.Stub outFrame.set(win.mCompatFrame); outOverscanInsets.set(win.mOverscanInsets); outContentInsets.set(win.mContentInsets); + win.mLastRelayoutContentInsets.set(win.mContentInsets); outVisibleInsets.set(win.mVisibleInsets); outStableInsets.set(win.mStableInsets); outOutsets.set(win.mOutsets); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 25b6561b7575c..8c8f633094253 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -257,6 +257,16 @@ class WindowState extends WindowContainer implements WindowManagerP */ final Rect mContentInsets = new Rect(); final Rect mLastContentInsets = new Rect(); + + /** + * The last content insets returned to the client in relayout. We use + * these in the bounds animation to ensure we only observe inset changes + * at the same time that a client resizes it's surface so that we may use + * the geometryAppliesWithResize synchronization mechanism to keep + * the contents in place. + */ + final Rect mLastRelayoutContentInsets = new Rect(); + private boolean mContentInsetsChanged; /** diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a6b95d635ba7c..dc96e7e8a788b 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1370,7 +1370,23 @@ class WindowStateAnimator { int posX = mTmpSize.left; int posY = mTmpSize.top; task.mStack.getDimBounds(mTmpStackBounds); + + boolean allowStretching = false; task.mStack.getFinalAnimationSourceHintBounds(mTmpSourceBounds); + // If we don't have source bounds, we can attempt to use the content insets + // in the following scenario: + // 1. We have content insets. + // 2. We are not transitioning to full screen + // We have to be careful to check "lastAnimatingBoundsWasToFullscreen" rather than + // the mBoundsAnimating state, as we may have already left it and only be here + // because of the force-scale until resize state. + if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0 + || mWin.mLastRelayoutContentInsets.height() > 0) + && !task.mStack.lastAnimatingBoundsWasToFullscreen()) { + mTmpSourceBounds.set(task.mStack.mPreAnimationBounds); + mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets); + allowStretching = true; + } if (!mTmpSourceBounds.isEmpty()) { // Get the final target stack bounds, if we are not animating, this is just the // current stack bounds @@ -1380,14 +1396,24 @@ class WindowStateAnimator { // and source bounds float finalWidth = mTmpAnimatingBounds.width(); float initialWidth = mTmpSourceBounds.width(); - float t = (surfaceContentWidth - mTmpStackBounds.width()) + float tw = (surfaceContentWidth - mTmpStackBounds.width()) / (surfaceContentWidth - mTmpAnimatingBounds.width()); - mExtraHScale = (initialWidth + t * (finalWidth - initialWidth)) / initialWidth; - mExtraVScale = mExtraHScale; + float th = tw; + mExtraHScale = (initialWidth + tw * (finalWidth - initialWidth)) / initialWidth; + if (allowStretching) { + float finalHeight = mTmpAnimatingBounds.height(); + float initialHeight = mTmpSourceBounds.height(); + th = (surfaceContentHeight - mTmpStackBounds.height()) + / (surfaceContentHeight - mTmpAnimatingBounds.height()); + mExtraVScale = (initialHeight + tw * (finalHeight - initialHeight)) + / initialHeight; + } else { + mExtraVScale = mExtraHScale; + } // Adjust the position to account for the inset bounds - posX -= (int) (t * mExtraHScale * mTmpSourceBounds.left); - posY -= (int) (t * mExtraVScale * mTmpSourceBounds.top); + posX -= (int) (tw * mExtraHScale * mTmpSourceBounds.left); + posY -= (int) (th * mExtraVScale * mTmpSourceBounds.top); // Always clip to the stack bounds since the surface can be larger with the current // scale