Merge "Refactor TaskPositioner resizing logic into a shareable class."

This commit is contained in:
Ben Lin
2020-02-08 05:51:03 +00:00
committed by Android (Google) Code Review
3 changed files with 192 additions and 142 deletions

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.policy;
import android.annotation.IntDef;
import android.graphics.Point;
import android.graphics.Rect;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Given a move coordinate (x, y), the original taks bounds and relevant details, calculate the new
* bounds.
*
* @hide
*/
public class TaskResizingAlgorithm {
@IntDef(flag = true,
value = {
CTRL_NONE,
CTRL_LEFT,
CTRL_RIGHT,
CTRL_TOP,
CTRL_BOTTOM
})
@Retention(RetentionPolicy.SOURCE)
public @interface CtrlType {}
public static final int CTRL_NONE = 0x0;
public static final int CTRL_LEFT = 0x1;
public static final int CTRL_RIGHT = 0x2;
public static final int CTRL_TOP = 0x4;
public static final int CTRL_BOTTOM = 0x8;
// The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
// Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
// aspect he desires.
@VisibleForTesting
public static final float MIN_ASPECT = 1.2f;
/**
* Given a (x, y) point and its original starting down point and its original bounds, calculate
* and return a new resized bound.
* @param x the new moved X point.
* @param y the new moved Y point.
* @param startDragX the original starting X point.
* @param startDragY the original starting Y point.
* @param originalBounds the original bound before resize.
* @param ctrlType The type of resize operation.
* @param minVisibleWidth The minimal width required for the new size.
* @param minVisibleHeight The minimal height required for the new size.
* @param maxVisibleSize The maximum size allowed.
* @param preserveOrientation
* @param startOrientationWasLandscape
* @return
*/
public static Rect resizeDrag(float x, float y, float startDragX, float startDragY,
Rect originalBounds, int ctrlType, int minVisibleWidth, int minVisibleHeight,
Point maxVisibleSize, boolean preserveOrientation,
boolean startOrientationWasLandscape) {
// This is a resizing operation.
// We need to keep various constraints:
// 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
// 2. The orientation is kept - if required.
final int deltaX = Math.round(x - startDragX);
final int deltaY = Math.round(y - startDragY);
int left = originalBounds.left;
int top = originalBounds.top;
int right = originalBounds.right;
int bottom = originalBounds.bottom;
// Calculate the resulting width and height of the drag operation.
int width = right - left;
int height = bottom - top;
if ((ctrlType & CTRL_LEFT) != 0) {
width = Math.max(minVisibleWidth, width - deltaX);
} else if ((ctrlType & CTRL_RIGHT) != 0) {
width = Math.max(minVisibleWidth, width + deltaX);
}
if ((ctrlType & CTRL_TOP) != 0) {
height = Math.max(minVisibleHeight, height - deltaY);
} else if ((ctrlType & CTRL_BOTTOM) != 0) {
height = Math.max(minVisibleHeight, height + deltaY);
}
// If we have to preserve the orientation - check that we are doing so.
final float aspect = (float) width / (float) height;
if (preserveOrientation && ((startOrientationWasLandscape && aspect < MIN_ASPECT)
|| (!startOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
// Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
// drag axis. What ever is producing the bigger rectangle will be chosen.
int width1;
int width2;
int height1;
int height2;
if (startOrientationWasLandscape) {
// Assuming that the width is our target we calculate the height.
width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
height1 = Math.min(height, Math.round((float) width1 / MIN_ASPECT));
if (height1 < minVisibleHeight) {
// If the resulting height is too small we adjust to the minimal size.
height1 = minVisibleHeight;
width1 = Math.max(minVisibleWidth,
Math.min(maxVisibleSize.x, Math.round((float) height1 * MIN_ASPECT)));
}
// Assuming that the height is our target we calculate the width.
height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
width2 = Math.max(width, Math.round((float) height2 * MIN_ASPECT));
if (width2 < minVisibleWidth) {
// If the resulting width is too small we adjust to the minimal size.
width2 = minVisibleWidth;
height2 = Math.max(minVisibleHeight,
Math.min(maxVisibleSize.y, Math.round((float) width2 / MIN_ASPECT)));
}
} else {
// Assuming that the width is our target we calculate the height.
width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
height1 = Math.max(height, Math.round((float) width1 * MIN_ASPECT));
if (height1 < minVisibleHeight) {
// If the resulting height is too small we adjust to the minimal size.
height1 = minVisibleHeight;
width1 = Math.max(minVisibleWidth,
Math.min(maxVisibleSize.x, Math.round((float) height1 / MIN_ASPECT)));
}
// Assuming that the height is our target we calculate the width.
height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
width2 = Math.min(width, Math.round((float) height2 / MIN_ASPECT));
if (width2 < minVisibleWidth) {
// If the resulting width is too small we adjust to the minimal size.
width2 = minVisibleWidth;
height2 = Math.max(minVisibleHeight,
Math.min(maxVisibleSize.y, Math.round((float) width2 * MIN_ASPECT)));
}
}
// Use the bigger of the two rectangles if the major change was positive, otherwise
// do the opposite.
final boolean grows = width > (right - left) || height > (bottom - top);
if (grows == (width1 * height1 > width2 * height2)) {
width = width1;
height = height1;
} else {
width = width2;
height = height2;
}
}
// Generate the final bounds by keeping the opposite drag edge constant.
if ((ctrlType & CTRL_LEFT) != 0) {
left = right - width;
} else { // Note: The right might have changed - if we pulled at the right or not.
right = left + width;
}
if ((ctrlType & CTRL_TOP) != 0) {
top = bottom - height;
} else { // Note: The height might have changed - if we pulled at the bottom or not.
bottom = top + height;
}
return new Rect(left, top, right, bottom);
}
}

View File

@@ -20,6 +20,11 @@ import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
@@ -29,7 +34,6 @@ import static com.android.server.wm.WindowManagerService.dipToPixel;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.IActivityTaskManager;
import android.graphics.Point;
@@ -55,11 +59,10 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
import com.android.server.protolog.common.ProtoLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
class TaskPositioner implements IBinder.DeathRecipient {
private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
private static final String TAG_LOCAL = "TaskPositioner";
@@ -67,33 +70,10 @@ class TaskPositioner implements IBinder.DeathRecipient {
private static Factory sFactory;
@IntDef(flag = true,
value = {
CTRL_NONE,
CTRL_LEFT,
CTRL_RIGHT,
CTRL_TOP,
CTRL_BOTTOM
})
@Retention(RetentionPolicy.SOURCE)
@interface CtrlType {}
private static final int CTRL_NONE = 0x0;
private static final int CTRL_LEFT = 0x1;
private static final int CTRL_RIGHT = 0x2;
private static final int CTRL_TOP = 0x4;
private static final int CTRL_BOTTOM = 0x8;
public static final float RESIZING_HINT_ALPHA = 0.5f;
public static final int RESIZING_HINT_DURATION_MS = 0;
// The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
// Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
// aspect he desires.
@VisibleForTesting
static final float MIN_ASPECT = 1.2f;
private final WindowManagerService mService;
private final IActivityTaskManager mActivityManager;
private WindowPositionerEventReceiver mInputEventReceiver;
@@ -477,122 +457,13 @@ class TaskPositioner implements IBinder.DeathRecipient {
*/
@VisibleForTesting
void resizeDrag(float x, float y) {
// This is a resizing operation.
// We need to keep various constraints:
// 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
// 2. The orientation is kept - if required.
final int deltaX = Math.round(x - mStartDragX);
final int deltaY = Math.round(y - mStartDragY);
int left = mWindowOriginalBounds.left;
int top = mWindowOriginalBounds.top;
int right = mWindowOriginalBounds.right;
int bottom = mWindowOriginalBounds.bottom;
// Calculate the resulting width and height of the drag operation.
int width = right - left;
int height = bottom - top;
if ((mCtrlType & CTRL_LEFT) != 0) {
width = Math.max(mMinVisibleWidth, width - deltaX);
} else if ((mCtrlType & CTRL_RIGHT) != 0) {
width = Math.max(mMinVisibleWidth, width + deltaX);
}
if ((mCtrlType & CTRL_TOP) != 0) {
height = Math.max(mMinVisibleHeight, height - deltaY);
} else if ((mCtrlType & CTRL_BOTTOM) != 0) {
height = Math.max(mMinVisibleHeight, height + deltaY);
}
// If we have to preserve the orientation - check that we are doing so.
final float aspect = (float) width / (float) height;
if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
|| (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
// Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
// drag axis. What ever is producing the bigger rectangle will be chosen.
int width1;
int width2;
int height1;
int height2;
if (mStartOrientationWasLandscape) {
// Assuming that the width is our target we calculate the height.
width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
if (height1 < mMinVisibleHeight) {
// If the resulting height is too small we adjust to the minimal size.
height1 = mMinVisibleHeight;
width1 = Math.max(mMinVisibleWidth,
Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
}
// Assuming that the height is our target we calculate the width.
height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
if (width2 < mMinVisibleWidth) {
// If the resulting width is too small we adjust to the minimal size.
width2 = mMinVisibleWidth;
height2 = Math.max(mMinVisibleHeight,
Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
}
} else {
// Assuming that the width is our target we calculate the height.
width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
if (height1 < mMinVisibleHeight) {
// If the resulting height is too small we adjust to the minimal size.
height1 = mMinVisibleHeight;
width1 = Math.max(mMinVisibleWidth,
Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
}
// Assuming that the height is our target we calculate the width.
height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
if (width2 < mMinVisibleWidth) {
// If the resulting width is too small we adjust to the minimal size.
width2 = mMinVisibleWidth;
height2 = Math.max(mMinVisibleHeight,
Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
}
}
// Use the bigger of the two rectangles if the major change was positive, otherwise
// do the opposite.
final boolean grows = width > (right - left) || height > (bottom - top);
if (grows == (width1 * height1 > width2 * height2)) {
width = width1;
height = height1;
} else {
width = width2;
height = height2;
}
}
// Update mWindowDragBounds to the new drag size.
updateDraggedBounds(left, top, right, bottom, width, height);
updateDraggedBounds(TaskResizingAlgorithm.resizeDrag(x, y, mStartDragX, mStartDragY,
mWindowOriginalBounds, mCtrlType, mMinVisibleWidth, mMinVisibleHeight,
mMaxVisibleSize, mPreserveOrientation, mStartOrientationWasLandscape));
}
/**
* Given the old coordinates and the new width and height, update the mWindowDragBounds.
*
* @param left The original left bound before the user started dragging.
* @param top The original top bound before the user started dragging.
* @param right The original right bound before the user started dragging.
* @param bottom The original bottom bound before the user started dragging.
* @param newWidth The new dragged width.
* @param newHeight The new dragged height.
*/
void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
int newHeight) {
// Generate the final bounds by keeping the opposite drag edge constant.
if ((mCtrlType & CTRL_LEFT) != 0) {
left = right - newWidth;
} else { // Note: The right might have changed - if we pulled at the right or not.
right = left + newWidth;
}
if ((mCtrlType & CTRL_TOP) != 0) {
top = bottom - newHeight;
} else { // Note: The height might have changed - if we pulled at the bottom or not.
bottom = top + newHeight;
}
mWindowDragBounds.set(left, top, right, bottom);
private void updateDraggedBounds(Rect newBounds) {
mWindowDragBounds.set(newBounds);
checkBoundsForOrientationViolations(mWindowDragBounds);
}

View File

@@ -20,7 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
import static com.android.internal.policy.TaskResizingAlgorithm.MIN_ASPECT;
import static com.android.server.wm.WindowManagerService.dipToPixel;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;