From f583427b0de21037b9bda8a8a66b1ca4e4e1c9b9 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Mon, 4 Apr 2016 20:25:41 -0700 Subject: [PATCH] Fix dialog placement When calculating the frame for non-fullscreen windows, we incorrectly used the wrong bounds to calculate the frame, which lead to wrong positioning. To fix this, we use the inset bounds, which we consider the source of truth for all layout related aspects, to calculate the frame, and then offset everything by the difference between the inset bounds and the task bounds to position them correctly. Bug: 28012565 Bug: 27860956 Bug: 27441808 Change-Id: I90d45054e0bcce78d021ad2cd20e5ef7f79ded3d --- .../server/wm/WindowManagerService.java | 2 +- .../com/android/server/wm/WindowState.java | 137 ++++++++++-------- 2 files changed, 81 insertions(+), 58 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 25da773ffc899..a632bdb7beefd 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2586,7 +2586,7 @@ public class WindowManagerService extends IWindowManager.Stub try { - win.applyGravityAndUpdateFrame(); + win.applyGravityAndUpdateFrame(win.mContainingFrame, win.mDisplayFrame); win.mWinAnimator.computeShownFrameLocked(); win.mWinAnimator.setSurfaceBoundariesLocked(false); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c62292dfeb867..1eca4d444db99 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -328,6 +328,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { */ final Rect mInsetFrame = new Rect(); + private static final Rect sTmpRect = new Rect(); + boolean mContentChanged; // If a window showing a wallpaper: the requested offset for the @@ -629,6 +631,20 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mAttrs.packageName; } + /** + * Subtracts the insets calculated by intersecting {@param layoutFrame} with {@param insetFrame} + * from {@param frame}. In other words, it applies the insets that would result if + * {@param frame} would be shifted to {@param layoutFrame} and then applying the insets from + * {@param insetFrame}. + */ + private void subtractInsets(Rect frame, Rect layoutFrame, Rect insetFrame) { + final int left = Math.max(0, insetFrame.left - layoutFrame.left); + final int top = Math.max(0, insetFrame.top - layoutFrame.top); + final int right = Math.max(0, layoutFrame.right - insetFrame.right); + final int bottom = Math.max(0, layoutFrame.bottom - insetFrame.bottom); + frame.inset(left, top, right, bottom); + } + @Override public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf, Rect osf) { @@ -654,11 +670,25 @@ final class WindowState implements WindowManagerPolicy.WindowState { task.getTempInsetBounds(mInsetFrame); } + // Denotes the actual frame used to calculate the insets and to perform the layout. When + // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the + // insets temporarily. By the notion of a task having a different layout frame, we can + // achieve that while still moving the task around. + final Rect layoutContainingFrame; + final Rect layoutDisplayFrame; + + // The offset from the layout containing frame to the actual containing frame. + final int layoutXDiff; + final int layoutYDiff; if (mInsetFrame.isEmpty() && (fullscreenTask || layoutInParentFrame())) { // We use the parent frame as the containing frame for fullscreen and child windows mContainingFrame.set(pf); mDisplayFrame.set(df); + layoutDisplayFrame = df; + layoutContainingFrame = pf; + layoutXDiff = 0; + layoutYDiff = 0; } else { task.getBounds(mContainingFrame); if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) { @@ -693,6 +723,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } mDisplayFrame.set(mContainingFrame); + layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mContainingFrame.left : 0; + layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0; + layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame; + subtractInsets(mDisplayFrame, layoutContainingFrame, df); + subtractInsets(mContainingFrame, layoutContainingFrame, pf); + subtractInsets(mInsetFrame, layoutContainingFrame, pf); + layoutDisplayFrame = df; + layoutDisplayFrame.intersect(layoutContainingFrame); } final int pw = mContainingFrame.width(); @@ -723,7 +761,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { final int fw = mFrame.width(); final int fh = mFrame.height(); - applyGravityAndUpdateFrame(); + applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame); + + // Offset the actual frame by the amount layout frame is off. + mFrame.offset(-layoutXDiff, -layoutYDiff); + mCompatFrame.offset(-layoutXDiff, -layoutYDiff); // Calculate the outsets before the content frame gets shrinked to the window frame. if (hasOutsets) { @@ -735,12 +777,6 @@ final class WindowState implements WindowManagerPolicy.WindowState { mOutsets.set(0, 0, 0, 0); } - // Denotes the actual frame used to calculate the insets. When resizing in docked mode, - // we'd like to freeze the layout, so we also need to freeze the insets temporarily. By the - // notion of a task having a different inset frame, we can achieve that while still moving - // the task around. - final Rect frame = !mInsetFrame.isEmpty() ? mInsetFrame : mFrame; - // Make sure the content and visible frames are inside of the // final window frame. if (windowsAreFloating && !mFrame.isEmpty()) { @@ -769,29 +805,29 @@ final class WindowState implements WindowManagerPolicy.WindowState { mMovedByResize = true; } } else { - mContentFrame.set(Math.max(mContentFrame.left, frame.left), - Math.max(mContentFrame.top, frame.top), - Math.min(mContentFrame.right, frame.right), - Math.min(mContentFrame.bottom, frame.bottom)); + mContentFrame.set(Math.max(mContentFrame.left, layoutContainingFrame.left), + Math.max(mContentFrame.top, layoutContainingFrame.top), + Math.min(mContentFrame.right, layoutContainingFrame.right), + Math.min(mContentFrame.bottom, layoutContainingFrame.bottom)); - mVisibleFrame.set(Math.max(mVisibleFrame.left, frame.left), - Math.max(mVisibleFrame.top, frame.top), - Math.min(mVisibleFrame.right, frame.right), - Math.min(mVisibleFrame.bottom, frame.bottom)); + mVisibleFrame.set(Math.max(mVisibleFrame.left, layoutContainingFrame.left), + Math.max(mVisibleFrame.top, layoutContainingFrame.top), + Math.min(mVisibleFrame.right, layoutContainingFrame.right), + Math.min(mVisibleFrame.bottom, layoutContainingFrame.bottom)); - mStableFrame.set(Math.max(mStableFrame.left, frame.left), - Math.max(mStableFrame.top, frame.top), - Math.min(mStableFrame.right, frame.right), - Math.min(mStableFrame.bottom, frame.bottom)); + mStableFrame.set(Math.max(mStableFrame.left, layoutContainingFrame.left), + Math.max(mStableFrame.top, layoutContainingFrame.top), + Math.min(mStableFrame.right, layoutContainingFrame.right), + Math.min(mStableFrame.bottom, layoutContainingFrame.bottom)); } if (fullscreenTask && !windowsAreFloating) { // Windows that are not fullscreen can be positioned outside of the display frame, // but that is not a reason to provide them with overscan insets. - mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0), - Math.max(mOverscanFrame.top - frame.top, 0), - Math.max(frame.right - mOverscanFrame.right, 0), - Math.max(frame.bottom - mOverscanFrame.bottom, 0)); + mOverscanInsets.set(Math.max(mOverscanFrame.left - layoutContainingFrame.left, 0), + Math.max(mOverscanFrame.top - layoutContainingFrame.top, 0), + Math.max(layoutContainingFrame.right - mOverscanFrame.right, 0), + Math.max(layoutContainingFrame.bottom - mOverscanFrame.bottom, 0)); } if (mAttrs.type == TYPE_DOCK_DIVIDER) { @@ -812,45 +848,32 @@ final class WindowState implements WindowManagerPolicy.WindowState { // non-fullscreen mode. boolean overrideRightInset = !fullscreenTask && mFrame.right > mTmpRect.right; boolean overrideBottomInset = !fullscreenTask && mFrame.bottom > mTmpRect.bottom; - mContentInsets.set(mContentFrame.left - frame.left, - mContentFrame.top - frame.top, + mContentInsets.set(mContentFrame.left - layoutContainingFrame.left, + mContentFrame.top - layoutContainingFrame.top, overrideRightInset ? mTmpRect.right - mContentFrame.right - : frame.right - mContentFrame.right, + : layoutContainingFrame.right - mContentFrame.right, overrideBottomInset ? mTmpRect.bottom - mContentFrame.bottom - : frame.bottom - mContentFrame.bottom); + : layoutContainingFrame.bottom - mContentFrame.bottom); - mVisibleInsets.set(mVisibleFrame.left - frame.left, - mVisibleFrame.top - frame.top, + mVisibleInsets.set(mVisibleFrame.left - layoutContainingFrame.left, + mVisibleFrame.top - layoutContainingFrame.top, overrideRightInset ? mTmpRect.right - mVisibleFrame.right - : frame.right - mVisibleFrame.right, + : layoutContainingFrame.right - mVisibleFrame.right, overrideBottomInset ? mTmpRect.bottom - mVisibleFrame.bottom - : frame.bottom - mVisibleFrame.bottom); + : layoutContainingFrame.bottom - mVisibleFrame.bottom); - mStableInsets.set(Math.max(mStableFrame.left - frame.left, 0), - Math.max(mStableFrame.top - frame.top, 0), + mStableInsets.set(Math.max(mStableFrame.left - layoutContainingFrame.left, 0), + Math.max(mStableFrame.top - layoutContainingFrame.top, 0), overrideRightInset ? Math.max(mTmpRect.right - mStableFrame.right, 0) - : Math.max(frame.right - mStableFrame.right, 0), + : Math.max(layoutContainingFrame.right - mStableFrame.right, 0), overrideBottomInset ? Math.max(mTmpRect.bottom - mStableFrame.bottom, 0) - : Math.max(frame.bottom - mStableFrame.bottom, 0)); + : Math.max(layoutContainingFrame.bottom - mStableFrame.bottom, 0)); } - if (!mInsetFrame.isEmpty()) { - mContentFrame.set(mFrame); - mContentFrame.top += mContentInsets.top; - mContentFrame.bottom -= mContentInsets.bottom; - mContentFrame.left += mContentInsets.left; - mContentFrame.right -= mContentInsets.right; - mVisibleFrame.set(mFrame); - mVisibleFrame.top += mVisibleInsets.top; - mVisibleFrame.bottom -= mVisibleInsets.bottom; - mVisibleFrame.left += mVisibleInsets.left; - mVisibleFrame.right -= mVisibleInsets.right; - mStableFrame.set(mFrame); - mStableFrame.top += mStableInsets.top; - mStableFrame.bottom -= mStableInsets.bottom; - mStableFrame.left += mStableInsets.left; - mStableFrame.right -= mStableInsets.right; - } + mContentFrame.offset(-layoutXDiff, -layoutYDiff); + mVisibleFrame.offset(-layoutXDiff, -layoutYDiff); + mStableFrame.offset(-layoutXDiff, -layoutYDiff); + mCompatFrame.set(mFrame); if (mEnforceSizeCompat) { // If there is a size compatibility scale being applied to the @@ -2549,9 +2572,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } - void applyGravityAndUpdateFrame() { - final int pw = mContainingFrame.width(); - final int ph = mContainingFrame.height(); + void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) { + final int pw = containingFrame.width(); + final int ph = containingFrame.height(); final Task task = getTask(); final boolean nonFullscreenTask = isInMultiWindowMode(); final boolean fitToDisplay = task != null && !task.isFloating() && !layoutInParentFrame(); @@ -2606,13 +2629,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { } // Set mFrame - Gravity.apply(mAttrs.gravity, w, h, mContainingFrame, + Gravity.apply(mAttrs.gravity, w, h, containingFrame, (int) (x + mAttrs.horizontalMargin * pw), (int) (y + mAttrs.verticalMargin * ph), mFrame); // Now make sure the window fits in the overall display frame. if (fitToDisplay) { - Gravity.applyDisplay(mAttrs.gravity, mDisplayFrame, mFrame); + Gravity.applyDisplay(mAttrs.gravity, displayFrame, mFrame); } // We need to make sure we update the CompatFrame as it is used for