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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IDockedStackListener> 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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user