Refactor TaskPositioner resizing logic into a shareable class.
So this can be used by other resizing not done by the framework (e.g. PIP resizing done via SysUI). Bug: 147361175 Test: Compile Change-Id: I0010b292af5ea48e4303a4faaf9c0bbe49d5d64f
This commit is contained in:
179
core/java/com/android/internal/policy/TaskResizingAlgorithm.java
Normal file
179
core/java/com/android/internal/policy/TaskResizingAlgorithm.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user