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); } }