• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.policy;
18 
19 import android.annotation.IntDef;
20 import android.graphics.Point;
21 import android.graphics.Rect;
22 
23 import com.android.internal.annotations.VisibleForTesting;
24 
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 
28 /**
29  * Given a move coordinate (x, y), the original taks bounds and relevant details, calculate the new
30  * bounds.
31  *
32  * @hide
33  */
34 public class TaskResizingAlgorithm {
35 
36     @IntDef(flag = true,
37             value = {
38                     CTRL_NONE,
39                     CTRL_LEFT,
40                     CTRL_RIGHT,
41                     CTRL_TOP,
42                     CTRL_BOTTOM
43             })
44     @Retention(RetentionPolicy.SOURCE)
45     public @interface CtrlType {}
46 
47     public static final int CTRL_NONE   = 0x0;
48     public static final int CTRL_LEFT   = 0x1;
49     public static final int CTRL_RIGHT  = 0x2;
50     public static final int CTRL_TOP    = 0x4;
51     public static final int CTRL_BOTTOM = 0x8;
52 
53     // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
54     @VisibleForTesting
55     public static final float MIN_ASPECT = 1.2f;
56 
57     /**
58      * Given a (x, y) point and its original starting down point and its original bounds, calculate
59      * and return a new resized bound.
60      * @param x the new moved X point.
61      * @param y the new moved Y point.
62      * @param startDragX the original starting X point.
63      * @param startDragY the original starting Y point.
64      * @param originalBounds the original bound before resize.
65      * @param ctrlType The type of resize operation.
66      * @param minVisibleWidth The minimal width required for the new size.
67      * @param minVisibleHeight The minimal height required for the new size.
68      * @param maxVisibleSize The maximum size allowed.
69      * @param preserveOrientation
70      * @param startOrientationWasLandscape
71      * @return
72      */
resizeDrag(float x, float y, float startDragX, float startDragY, Rect originalBounds, int ctrlType, int minVisibleWidth, int minVisibleHeight, Point maxVisibleSize, boolean preserveOrientation, boolean startOrientationWasLandscape)73     public static Rect resizeDrag(float x, float y, float startDragX, float startDragY,
74             Rect originalBounds, int ctrlType, int minVisibleWidth, int minVisibleHeight,
75             Point maxVisibleSize, boolean preserveOrientation,
76             boolean startOrientationWasLandscape) {
77         // This is a resizing operation.
78         // We need to keep various constraints:
79         // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
80         // 2. The orientation is kept - if required.
81         final int deltaX = Math.round(x - startDragX);
82         final int deltaY = Math.round(y - startDragY);
83         int left = originalBounds.left;
84         int top = originalBounds.top;
85         int right = originalBounds.right;
86         int bottom = originalBounds.bottom;
87 
88         // Calculate the resulting width and height of the drag operation.
89         int width = right - left;
90         int height = bottom - top;
91         if ((ctrlType & CTRL_LEFT) != 0) {
92             width = Math.max(minVisibleWidth, Math.min(width - deltaX, maxVisibleSize.x));
93         } else if ((ctrlType & CTRL_RIGHT) != 0) {
94             width = Math.max(minVisibleWidth, Math.min(width + deltaX, maxVisibleSize.x));
95         }
96         if ((ctrlType & CTRL_TOP) != 0) {
97             height = Math.max(minVisibleHeight, Math.min(height - deltaY, maxVisibleSize.y));
98         } else if ((ctrlType & CTRL_BOTTOM) != 0) {
99             height = Math.max(minVisibleHeight, Math.min(height + deltaY, maxVisibleSize.y));
100         }
101 
102         // If we have to preserve the orientation - check that we are doing so.
103         final float aspect = (float) width / (float) height;
104         if (preserveOrientation && ((startOrientationWasLandscape && aspect < MIN_ASPECT)
105                 || (!startOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
106             // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
107             // drag axis. What ever is producing the bigger rectangle will be chosen.
108             int width1;
109             int width2;
110             int height1;
111             int height2;
112             if (startOrientationWasLandscape) {
113                 // Assuming that the width is our target we calculate the height.
114                 width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
115                 height1 = Math.min(height, Math.round((float) width1 / MIN_ASPECT));
116                 if (height1 < minVisibleHeight) {
117                     // If the resulting height is too small we adjust to the minimal size.
118                     height1 = minVisibleHeight;
119                     width1 = Math.max(minVisibleWidth,
120                             Math.min(maxVisibleSize.x, Math.round((float) height1 * MIN_ASPECT)));
121                 }
122                 // Assuming that the height is our target we calculate the width.
123                 height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
124                 width2 = Math.max(width, Math.round((float) height2 * MIN_ASPECT));
125                 if (width2 < minVisibleWidth) {
126                     // If the resulting width is too small we adjust to the minimal size.
127                     width2 = minVisibleWidth;
128                     height2 = Math.max(minVisibleHeight,
129                             Math.min(maxVisibleSize.y, Math.round((float) width2 / MIN_ASPECT)));
130                 }
131             } else {
132                 // Assuming that the width is our target we calculate the height.
133                 width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
134                 height1 = Math.max(height, Math.round((float) width1 * MIN_ASPECT));
135                 if (height1 < minVisibleHeight) {
136                     // If the resulting height is too small we adjust to the minimal size.
137                     height1 = minVisibleHeight;
138                     width1 = Math.max(minVisibleWidth,
139                             Math.min(maxVisibleSize.x, Math.round((float) height1 / MIN_ASPECT)));
140                 }
141                 // Assuming that the height is our target we calculate the width.
142                 height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
143                 width2 = Math.min(width, Math.round((float) height2 / MIN_ASPECT));
144                 if (width2 < minVisibleWidth) {
145                     // If the resulting width is too small we adjust to the minimal size.
146                     width2 = minVisibleWidth;
147                     height2 = Math.max(minVisibleHeight,
148                             Math.min(maxVisibleSize.y, Math.round((float) width2 * MIN_ASPECT)));
149                 }
150             }
151 
152             // Use the bigger of the two rectangles if the major change was positive, otherwise
153             // do the opposite.
154             final boolean grows = width > (right - left) || height > (bottom - top);
155             if (grows == (width1 * height1 > width2 * height2)) {
156                 width = width1;
157                 height = height1;
158             } else {
159                 width = width2;
160                 height = height2;
161             }
162         }
163 
164         // Generate the final bounds by keeping the opposite drag edge constant.
165         if ((ctrlType & CTRL_LEFT) != 0) {
166             left = right - width;
167         } else { // Note: The right might have changed - if we pulled at the right or not.
168             right = left + width;
169         }
170         if ((ctrlType & CTRL_TOP) != 0) {
171             top = bottom - height;
172         } else { // Note: The height might have changed - if we pulled at the bottom or not.
173             bottom = top + height;
174         }
175         return new Rect(left, top, right, bottom);
176     }
177 }
178