diff options
3 files changed, 192 insertions, 142 deletions
diff --git a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java new file mode 100644 index 000000000000..1fce098fb93e --- /dev/null +++ b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java @@ -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); + } +} diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 8bbb0d7da4b6..b5892b9b4dc6 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -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); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java index 7aaf3fbccb5e..52b465ff4634 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java @@ -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; |