From 8563943bd39107b5a7ff9ea475592d0040423ba1 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Fri, 6 May 2016 17:27:55 -0700 Subject: [PATCH] Fix smallest width configuration calculation For calculating the smallest width, we need to iterate through all possible orientations, snap the task bounds to a valid snap target and then use the smallest width across all orientations. In addition to that, when flinging the divider handle to the bottom of the screen, we need to pass in fullscreen task bounds to avoid that this new logic applies so the maximizing app only receives one configuration change. Change-Id: I37aa9a40938517dfaf26770eb41327d76aec7c62 Fixes: 28469673 --- .../internal/policy/DividerSnapAlgorithm.java | 34 +++++--- .../systemui/stackdivider/DividerView.java | 2 +- .../com/android/server/am/TaskRecord.java | 14 ++-- .../wm/DockedStackDividerController.java | 81 +++++++++++++++++++ .../java/com/android/server/wm/TaskStack.java | 8 +- .../server/wm/WindowManagerService.java | 28 +++++-- 6 files changed, 143 insertions(+), 24 deletions(-) diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java index d6172db3d3697..cf14471f381a5 100644 --- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java @@ -246,7 +246,8 @@ public class DividerSnapAlgorithm { int dividerMax = isHorizontalDivision ? mDisplayHeight : mDisplayWidth; - mTargets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START, 0.35f)); + mTargets.add(new SnapTarget(-mDividerSize, -mDividerSize, SnapTarget.FLAG_DISMISS_START, + 0.35f)); switch (mSnapMode) { case SNAP_MODE_16_9: addRatio16_9Targets(isHorizontalDivision, dividerMax); @@ -259,7 +260,8 @@ public class DividerSnapAlgorithm { break; } int navBarSize = isHorizontalDivision ? mInsets.bottom : mInsets.right; - mTargets.add(new SnapTarget(dividerMax - navBarSize, SnapTarget.FLAG_DISMISS_END, 0.35f)); + mTargets.add(new SnapTarget(dividerMax - navBarSize, dividerMax, + SnapTarget.FLAG_DISMISS_END, 0.35f)); } private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition, @@ -269,13 +271,15 @@ public class DividerSnapAlgorithm { maybeAddTarget(bottomPosition, dividerMax - mInsets.bottom - (bottomPosition + mDividerSize)); } + private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) { int start = isHorizontalDivision ? mInsets.top : mInsets.left; int end = isHorizontalDivision ? mDisplayHeight - mInsets.bottom : mDisplayWidth - mInsets.right; - int topPosition = (int) (start + mFixedRatio * (end - start)) - mDividerSize / 2; - int bottomPosition = (int) (start + (1 - mFixedRatio) * (end - start)) - mDividerSize / 2; + int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2; + int topPosition = start + size; + int bottomPosition = end - size - mDividerSize; addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); } @@ -301,13 +305,14 @@ public class DividerSnapAlgorithm { */ private void maybeAddTarget(int position, int smallerSize) { if (smallerSize >= mMinimalSizeResizableTask) { - mTargets.add(new SnapTarget(position, SnapTarget.FLAG_NONE)); + mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); } } private void addMiddleTarget(boolean isHorizontalDivision) { - mTargets.add(new SnapTarget(DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, - mInsets, mDisplayWidth, mDisplayHeight, mDividerSize), SnapTarget.FLAG_NONE)); + int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, + mInsets, mDisplayWidth, mDisplayHeight, mDividerSize); + mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); } public SnapTarget getMiddleTarget() { @@ -370,7 +375,15 @@ public class DividerSnapAlgorithm { /** If the divider reaches this value, the right/bottom task should be dismissed */ public static final int FLAG_DISMISS_END = 2; + /** Position of this snap target. The right/bottom edge of the top/left task snaps here. */ public final int position; + + /** + * Like {@link #position}, but used to calculate the task bounds which might be different + * from the stack bounds. + */ + public final int taskPosition; + public final int flag; /** @@ -379,12 +392,13 @@ public class DividerSnapAlgorithm { */ private final float distanceMultiplier; - public SnapTarget(int position, int flag) { - this(position, flag, 1f); + public SnapTarget(int position, int taskPosition, int flag) { + this(position, taskPosition, flag, 1f); } - public SnapTarget(int position, int flag, float distanceMultiplier) { + public SnapTarget(int position, int taskPosition, int flag, float distanceMultiplier) { this.position = position; + this.taskPosition = taskPosition; this.flag = flag; this.distanceMultiplier = distanceMultiplier; } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 9ba1cc945afa8..cff4702c77887 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -535,7 +535,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, anim.addUpdateListener(animation -> resizeStack((Integer) animation.getAnimatedValue(), taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f ? TASK_POSITION_SAME - : snapTarget.position, snapTarget)); + : snapTarget.taskPosition, snapTarget)); Runnable endAction = () -> { commitSnapFlags(snapTarget); mWindowManagerProxy.setResizing(false); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 0165fdf6ae7c0..af7740f8083a2 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -1508,16 +1508,18 @@ final class TaskRecord { ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; - // For calculating screen layout and smallest screen width, we need to use the non-decor - // inset screen area for the calculation for compatibility reasons, i.e. screen area without - // system bars that could never go away in Honeycomb. + // For calculating screen layout, we need to use the non-decor inset screen area for the + // calculation for compatibility reasons, i.e. screen area without system bars that could + // never go away in Honeycomb. final int compatScreenWidthDp = (int)(mTmpNonDecorBounds.width() / density); final int compatScreenHeightDp = (int)(mTmpNonDecorBounds.height() / density); final int sl = Configuration.resetScreenLayout(serviceConfig.screenLayout); final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp); - config.smallestScreenWidthDp = Math.min(compatScreenHeightDp, compatScreenWidthDp); - config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, - config.smallestScreenWidthDp); + final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);; + config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize); + + config.smallestScreenWidthDp = mService.mWindowManager.getSmallestWidthForTaskBounds( + insetBounds != null ? insetBounds : bounds); return config; } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index ca68d55dfb839..166fca30bc48b 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -19,6 +19,10 @@ package com.android.server.wm; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; @@ -30,6 +34,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -41,6 +46,8 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import com.android.internal.policy.DividerSnapAlgorithm; +import com.android.internal.policy.DockedDividerUtils; import com.android.server.wm.DimLayer.DimLayerUser; import com.android.server.wm.WindowManagerService.H; @@ -96,6 +103,7 @@ public class DockedStackDividerController implements DimLayerUser { private WindowState mWindow; private final Rect mTmpRect = new Rect(); private final Rect mTmpRect2 = new Rect(); + private final Rect mTmpRect3 = new Rect(); private final Rect mLastRect = new Rect(); private boolean mLastVisibility = false; private final RemoteCallbackList mDockedStackListeners @@ -121,6 +129,7 @@ public class DockedStackDividerController implements DimLayerUser { private float mDividerAnimationTarget; private float mLastAnimationProgress; private float mLastDividerProgress; + private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4]; DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) { mService = service; @@ -133,6 +142,77 @@ public class DockedStackDividerController implements DimLayerUser { loadDimens(); } + int getSmallestWidthDpForBounds(Rect bounds) { + final DisplayInfo di = mDisplayContent.getDisplayInfo(); + + // If the bounds are fullscreen, return the value of the fullscreen configuration + if (bounds == null || (bounds.left == 0 && bounds.top == 0 + && bounds.right == di.logicalWidth && bounds.bottom == di.logicalHeight)) { + return mService.mCurConfiguration.smallestScreenWidthDp; + } + final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth; + final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight; + int minWidth = Integer.MAX_VALUE; + + // Go through all screen orientations and find the orientation in which the task has the + // smallest width. + for (int rotation = 0; rotation < 4; rotation++) { + mTmpRect.set(bounds); + mDisplayContent.rotateBounds(di.rotation, rotation, mTmpRect); + final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); + mTmpRect2.set(0, 0, + rotated ? baseDisplayHeight : baseDisplayWidth, + rotated ? baseDisplayWidth : baseDisplayHeight); + final int orientation = mTmpRect2.width() <= mTmpRect2.height() + ? ORIENTATION_PORTRAIT + : ORIENTATION_LANDSCAPE; + final int dockSide = TaskStack.getDockSideUnchecked(mTmpRect, mTmpRect2, orientation); + final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide, + getContentWidth()); + + // Since we only care about feasible states, snap to the closest snap target, like it + // would happen when actually rotating the screen. + final int snappedPosition = mSnapAlgorithmForRotation[rotation] + .calculateNonDismissingSnapTarget(position).position; + DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect, + mTmpRect2.width(), mTmpRect2.height(), getContentWidth()); + mService.mPolicy.getStableInsetsLw(rotation, mTmpRect2.width(), mTmpRect2.height(), + mTmpRect3); + mService.subtractInsets(mTmpRect2, mTmpRect3, mTmpRect); + minWidth = Math.min(mTmpRect.width(), minWidth); + } + return (int) (minWidth / mDisplayContent.getDisplayMetrics().density); + } + + private void initSnapAlgorithmForRotations() { + final Configuration baseConfig = mService.mCurConfiguration; + + // Initialize the snap algorithms for all 4 screen orientations. + final Configuration config = new Configuration(); + for (int rotation = 0; rotation < 4; rotation++) { + final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); + final int dw = rotated + ? mDisplayContent.mBaseDisplayHeight + : mDisplayContent.mBaseDisplayWidth; + final int dh = rotated + ? mDisplayContent.mBaseDisplayWidth + : mDisplayContent.mBaseDisplayHeight; + mService.mPolicy.getStableInsetsLw(rotation, dw, dh, mTmpRect); + config.setToDefaults(); + config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + config.screenWidthDp = (int) + (mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, baseConfig.uiMode) / + mDisplayContent.getDisplayMetrics().density); + config.screenHeightDp = (int) + (mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, baseConfig.uiMode) / + mDisplayContent.getDisplayMetrics().density); + final Context rotationContext = mService.mContext.createConfigurationContext(config); + mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm( + rotationContext.getResources(), dw, dh, getContentWidth(), + config.orientation == ORIENTATION_PORTRAIT, mTmpRect); + } + } + private void loadDimens() { final Context context = mService.mContext; mDividerWindowWidth = context.getResources().getDimensionPixelSize( @@ -141,6 +221,7 @@ public class DockedStackDividerController implements DimLayerUser { com.android.internal.R.dimen.docked_stack_divider_insets); mDividerWindowWidthInactive = WindowManagerService.dipToPixel( DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics()); + initSnapAlgorithmForRotations(); } void onConfigurationChanged() { diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index b2f3df7782370..58468f62eefb6 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1197,16 +1197,20 @@ public class TaskStack implements DimLayer.DimLayerUser, } mDisplayContent.getLogicalDisplayRect(mTmpRect); final int orientation = mService.mCurConfiguration.orientation; + return getDockSideUnchecked(bounds, mTmpRect, orientation); + } + + static int getDockSideUnchecked(Rect bounds, Rect displayRect, int orientation) { if (orientation == Configuration.ORIENTATION_PORTRAIT) { // Portrait mode, docked either at the top or the bottom. - if (bounds.top - mTmpRect.top <= mTmpRect.bottom - bounds.bottom) { + if (bounds.top - displayRect.top <= displayRect.bottom - bounds.bottom) { return DOCKED_TOP; } else { return DOCKED_BOTTOM; } } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { // Landscape mode, docked either on the left or on the right. - if (bounds.left - mTmpRect.left <= mTmpRect.right - bounds.right) { + if (bounds.left - displayRect.left <= displayRect.right - bounds.right) { return DOCKED_LEFT; } else { return DOCKED_RIGHT; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5a394d03f3abe..95d8a5475d58a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -501,7 +501,7 @@ public class WindowManagerService extends IWindowManager.Stub final float[] mTmpFloats = new float[9]; final Rect mTmpRect = new Rect(); final Rect mTmpRect2 = new Rect(); - final Region mTmpRegion = new Region(); + final Rect mTmpRect3 = new Rect(); boolean mDisplayReady; boolean mSafeMode; @@ -10881,8 +10881,7 @@ public class WindowManagerService extends IWindowManager.Stub getStableInsetsLocked(mTmpRect2); final DisplayInfo di = getDefaultDisplayInfoLocked(); mTmpRect.set(0, 0, di.logicalWidth, di.logicalHeight); - mTmpRect.inset(mTmpRect2); - inOutBounds.intersect(mTmpRect); + subtractInsets(mTmpRect, mTmpRect2, inOutBounds); } } @@ -10898,8 +10897,27 @@ public class WindowManagerService extends IWindowManager.Stub getNonDecorInsetsLocked(mTmpRect2); final DisplayInfo di = getDefaultDisplayInfoLocked(); mTmpRect.set(0, 0, di.logicalWidth, di.logicalHeight); - mTmpRect.inset(mTmpRect2); - inOutBounds.intersect(mTmpRect); + subtractInsets(mTmpRect, mTmpRect2, inOutBounds); + } + } + + void subtractInsets(Rect display, Rect insets, Rect inOutBounds) { + mTmpRect3.set(display); + mTmpRect3.inset(insets); + inOutBounds.intersect(mTmpRect3); + } + + /** + * Calculates the smallest width for a task given the {@param bounds}. It does that by iterating + * across all screen orientations, and returns the minimum of the task width taking into account + * that the bounds might change because the snap algorithm snaps to a different value. + * + * @return the smallest width to be used in the Configuration, in dips + */ + public int getSmallestWidthForTaskBounds(Rect bounds) { + synchronized (mWindowMap) { + return getDefaultDisplayContentLocked().getDockedDividerController() + .getSmallestWidthDpForBounds(bounds); } }