• 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     // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
55     // aspect he desires.
56     @VisibleForTesting
57     public static final float MIN_ASPECT = 1.2f;
58 
59     /**
60      * Given a (x, y) point and its original starting down point and its original bounds, calculate
61      * and return a new resized bound.
62      * @param x the new moved X point.
63      * @param y the new moved Y point.
64      * @param startDragX the original starting X point.
65      * @param startDragY the original starting Y point.
66      * @param originalBounds the original bound before resize.
67      * @param ctrlType The type of resize operation.
68      * @param minVisibleWidth The minimal width required for the new size.
69      * @param minVisibleHeight The minimal height required for the new size.
70      * @param maxVisibleSize The maximum size allowed.
71      * @param preserveOrientation
72      * @param startOrientationWasLandscape
73      * @return
74      */
resizeDrag(float x, float y, float startDragX, float startDragY, Rect originalBounds, int ctrlType, int minVisibleWidth, int minVisibleHeight, Point maxVisibleSize, boolean preserveOrientation, boolean startOrientationWasLandscape)75     public static Rect resizeDrag(float x, float y, float startDragX, float startDragY,
76             Rect originalBounds, int ctrlType, int minVisibleWidth, int minVisibleHeight,
77             Point maxVisibleSize, boolean preserveOrientation,
78             boolean startOrientationWasLandscape) {
79         // This is a resizing operation.
80         // We need to keep various constraints:
81         // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
82         // 2. The orientation is kept - if required.
83         final int deltaX = Math.round(x - startDragX);
84         final int deltaY = Math.round(y - startDragY);
85         int left = originalBounds.left;
86         int top = originalBounds.top;
87         int right = originalBounds.right;
88         int bottom = originalBounds.bottom;
89 
90         // Calculate the resulting width and height of the drag operation.
91         int width = right - left;
92         int height = bottom - top;
93         if ((ctrlType & CTRL_LEFT) != 0) {
94             width = Math.max(minVisibleWidth, Math.min(width - deltaX, maxVisibleSize.x));
95         } else if ((ctrlType & CTRL_RIGHT) != 0) {
96             width = Math.max(minVisibleWidth, Math.min(width + deltaX, maxVisibleSize.x));
97         }
98         if ((ctrlType & CTRL_TOP) != 0) {
99             height = Math.max(minVisibleHeight, Math.min(height - deltaY, maxVisibleSize.y));
100         } else if ((ctrlType & CTRL_BOTTOM) != 0) {
101             height = Math.max(minVisibleHeight, Math.min(height + deltaY, maxVisibleSize.y));
102         }
103 
104         // If we have to preserve the orientation - check that we are doing so.
105         final float aspect = (float) width / (float) height;
106         if (preserveOrientation && ((startOrientationWasLandscape && aspect < MIN_ASPECT)
107                 || (!startOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
108             // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
109             // drag axis. What ever is producing the bigger rectangle will be chosen.
110             int width1;
111             int width2;
112             int height1;
113             int height2;
114             if (startOrientationWasLandscape) {
115                 // Assuming that the width is our target we calculate the height.
116                 width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
117                 height1 = Math.min(height, Math.round((float) width1 / MIN_ASPECT));
118                 if (height1 < minVisibleHeight) {
119                     // If the resulting height is too small we adjust to the minimal size.
120                     height1 = minVisibleHeight;
121                     width1 = Math.max(minVisibleWidth,
122                             Math.min(maxVisibleSize.x, Math.round((float) height1 * MIN_ASPECT)));
123                 }
124                 // Assuming that the height is our target we calculate the width.
125                 height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
126                 width2 = Math.max(width, Math.round((float) height2 * MIN_ASPECT));
127                 if (width2 < minVisibleWidth) {
128                     // If the resulting width is too small we adjust to the minimal size.
129                     width2 = minVisibleWidth;
130                     height2 = Math.max(minVisibleHeight,
131                             Math.min(maxVisibleSize.y, Math.round((float) width2 / MIN_ASPECT)));
132                 }
133             } else {
134                 // Assuming that the width is our target we calculate the height.
135                 width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
136                 height1 = Math.max(height, Math.round((float) width1 * MIN_ASPECT));
137                 if (height1 < minVisibleHeight) {
138                     // If the resulting height is too small we adjust to the minimal size.
139                     height1 = minVisibleHeight;
140                     width1 = Math.max(minVisibleWidth,
141                             Math.min(maxVisibleSize.x, Math.round((float) height1 / MIN_ASPECT)));
142                 }
143                 // Assuming that the height is our target we calculate the width.
144                 height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
145                 width2 = Math.min(width, Math.round((float) height2 / MIN_ASPECT));
146                 if (width2 < minVisibleWidth) {
147                     // If the resulting width is too small we adjust to the minimal size.
148                     width2 = minVisibleWidth;
149                     height2 = Math.max(minVisibleHeight,
150                             Math.min(maxVisibleSize.y, Math.round((float) width2 * MIN_ASPECT)));
151                 }
152             }
153 
154             // Use the bigger of the two rectangles if the major change was positive, otherwise
155             // do the opposite.
156             final boolean grows = width > (right - left) || height > (bottom - top);
157             if (grows == (width1 * height1 > width2 * height2)) {
158                 width = width1;
159                 height = height1;
160             } else {
161                 width = width2;
162                 height = height2;
163             }
164         }
165 
166         // Generate the final bounds by keeping the opposite drag edge constant.
167         if ((ctrlType & CTRL_LEFT) != 0) {
168             left = right - width;
169         } else { // Note: The right might have changed - if we pulled at the right or not.
170             right = left + width;
171         }
172         if ((ctrlType & CTRL_TOP) != 0) {
173             top = bottom - height;
174         } else { // Note: The height might have changed - if we pulled at the bottom or not.
175             bottom = top + height;
176         }
177         return new Rect(left, top, right, bottom);
178     }
179 }
180