Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 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.server.am; |
| 18 | |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 19 | import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; |
| 20 | import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; |
| 21 | |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 22 | import android.app.ActivityOptions; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 23 | import android.content.pm.ActivityInfo; |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 24 | import android.graphics.Rect; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 25 | import android.util.Slog; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 26 | import android.view.Gravity; |
Bryce Lee | 81e30a2 | 2017-10-06 13:34:12 -0700 | [diff] [blame] | 27 | import com.android.internal.annotations.VisibleForTesting; |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 28 | import com.android.server.am.LaunchParamsController.LaunchParams; |
| 29 | import com.android.server.am.LaunchParamsController.LaunchParamsModifier; |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 30 | |
| 31 | import java.util.ArrayList; |
| 32 | |
| 33 | /** |
| 34 | * Determines where a launching task should be positioned and sized on the display. |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 35 | * |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 36 | * The modifier is fairly simple. For the new task it tries default position based on the gravity |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 37 | * and compares corners of the task with corners of existing tasks. If some two pairs of corners are |
| 38 | * sufficiently close enough, it shifts the bounds of the new task and tries again. When it exhausts |
| 39 | * all possible shifts, it gives up and puts the task in the original position. |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 40 | * |
| 41 | * Note that the only gravities of concern are the corners and the center. |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 42 | */ |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 43 | class TaskLaunchParamsModifier implements LaunchParamsModifier { |
| 44 | private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_AM; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 45 | |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 46 | // Determines how close window frames/corners have to be to call them colliding. |
| 47 | private static final int BOUNDS_CONFLICT_MIN_DISTANCE = 4; |
| 48 | |
| 49 | // Task will receive dimensions based on available dimensions divided by this. |
| 50 | private static final int WINDOW_SIZE_DENOMINATOR = 2; |
| 51 | |
| 52 | // Task will receive margins based on available dimensions divided by this. |
| 53 | private static final int MARGIN_SIZE_DENOMINATOR = 4; |
| 54 | |
| 55 | // If task bounds collide with some other, we will step and try again until we find a good |
| 56 | // position. The step will be determined by using dimensions and dividing it by this. |
| 57 | private static final int STEP_DENOMINATOR = 16; |
| 58 | |
| 59 | // We always want to step by at least this. |
| 60 | private static final int MINIMAL_STEP = 1; |
| 61 | |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 62 | // Used to indicate if positioning algorithm is allowed to restart from the beginning, when it |
| 63 | // reaches the end of stack bounds. |
| 64 | private static final boolean ALLOW_RESTART = true; |
| 65 | |
| 66 | private static final int SHIFT_POLICY_DIAGONAL_DOWN = 1; |
| 67 | private static final int SHIFT_POLICY_HORIZONTAL_RIGHT = 2; |
| 68 | private static final int SHIFT_POLICY_HORIZONTAL_LEFT = 3; |
| 69 | |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 70 | private final Rect mAvailableRect = new Rect(); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 71 | private final Rect mTmpProposal = new Rect(); |
| 72 | private final Rect mTmpOriginal = new Rect(); |
| 73 | |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 74 | /** |
| 75 | * Tries to set task's bound in a way that it won't collide with any other task. By colliding |
| 76 | * we mean that two tasks have left-top corner very close to each other, so one might get |
| 77 | * obfuscated by the other one. |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 78 | */ |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 79 | @Override |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 80 | public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout, |
| 81 | ActivityRecord activity, ActivityRecord source, ActivityOptions options, |
| 82 | LaunchParams currentParams, LaunchParams outParams) { |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 83 | // We can only apply positioning if we're in a freeform stack. |
| 84 | if (task == null || task.getStack() == null || !task.inFreeformWindowingMode()) { |
| 85 | return RESULT_SKIP; |
| 86 | } |
| 87 | |
| 88 | final ArrayList<TaskRecord> tasks = task.getStack().getAllTasks(); |
| 89 | |
Bryce Lee | f3c6a47 | 2017-11-14 14:53:06 -0800 | [diff] [blame] | 90 | mAvailableRect.set(task.getParent().getBounds()); |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 91 | |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 92 | final Rect resultBounds = outParams.mBounds; |
| 93 | |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 94 | if (layout == null) { |
| 95 | positionCenter(tasks, mAvailableRect, getFreeformWidth(mAvailableRect), |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 96 | getFreeformHeight(mAvailableRect), resultBounds); |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 97 | return RESULT_CONTINUE; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 98 | } |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 99 | |
| 100 | int width = getFinalWidth(layout, mAvailableRect); |
| 101 | int height = getFinalHeight(layout, mAvailableRect); |
| 102 | int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK; |
| 103 | int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 104 | if (verticalGravity == Gravity.TOP) { |
| 105 | if (horizontalGravity == Gravity.RIGHT) { |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 106 | positionTopRight(tasks, mAvailableRect, width, height, resultBounds); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 107 | } else { |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 108 | positionTopLeft(tasks, mAvailableRect, width, height, resultBounds); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 109 | } |
| 110 | } else if (verticalGravity == Gravity.BOTTOM) { |
| 111 | if (horizontalGravity == Gravity.RIGHT) { |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 112 | positionBottomRight(tasks, mAvailableRect, width, height, resultBounds); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 113 | } else { |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 114 | positionBottomLeft(tasks, mAvailableRect, width, height, resultBounds); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 115 | } |
| 116 | } else { |
| 117 | // Some fancy gravity setting that we don't support yet. We just put the activity in the |
| 118 | // center. |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 119 | Slog.w(TAG, "Received unsupported gravity: " + layout.gravity |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 120 | + ", positioning in the center instead."); |
Bryce Lee | ec55eb0 | 2017-12-05 20:51:27 -0800 | [diff] [blame] | 121 | positionCenter(tasks, mAvailableRect, width, height, resultBounds); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 122 | } |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 123 | |
| 124 | return RESULT_CONTINUE; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 125 | } |
| 126 | |
Bryce Lee | 81e30a2 | 2017-10-06 13:34:12 -0700 | [diff] [blame] | 127 | @VisibleForTesting |
| 128 | static int getFreeformStartLeft(Rect bounds) { |
| 129 | return bounds.left + bounds.width() / MARGIN_SIZE_DENOMINATOR; |
| 130 | } |
| 131 | |
| 132 | @VisibleForTesting |
| 133 | static int getFreeformStartTop(Rect bounds) { |
| 134 | return bounds.top + bounds.height() / MARGIN_SIZE_DENOMINATOR; |
| 135 | } |
| 136 | |
| 137 | @VisibleForTesting |
| 138 | static int getFreeformWidth(Rect bounds) { |
| 139 | return bounds.width() / WINDOW_SIZE_DENOMINATOR; |
| 140 | } |
| 141 | |
| 142 | @VisibleForTesting |
| 143 | static int getFreeformHeight(Rect bounds) { |
| 144 | return bounds.height() / WINDOW_SIZE_DENOMINATOR; |
| 145 | } |
| 146 | |
| 147 | @VisibleForTesting |
| 148 | static int getHorizontalStep(Rect bounds) { |
| 149 | return Math.max(bounds.width() / STEP_DENOMINATOR, MINIMAL_STEP); |
| 150 | } |
| 151 | |
| 152 | @VisibleForTesting |
| 153 | static int getVerticalStep(Rect bounds) { |
| 154 | return Math.max(bounds.height() / STEP_DENOMINATOR, MINIMAL_STEP); |
| 155 | } |
| 156 | |
| 157 | |
| 158 | |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 159 | private int getFinalWidth(ActivityInfo.WindowLayout windowLayout, Rect availableRect) { |
| 160 | int width = getFreeformWidth(availableRect); |
Andrii Kulian | 2e751b8 | 2016-03-16 16:59:32 -0700 | [diff] [blame] | 161 | if (windowLayout.width > 0) { |
| 162 | width = windowLayout.width; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 163 | } |
Andrii Kulian | 2e751b8 | 2016-03-16 16:59:32 -0700 | [diff] [blame] | 164 | if (windowLayout.widthFraction > 0) { |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 165 | width = (int) (availableRect.width() * windowLayout.widthFraction); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 166 | } |
| 167 | return width; |
| 168 | } |
| 169 | |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 170 | private int getFinalHeight(ActivityInfo.WindowLayout windowLayout, Rect availableRect) { |
| 171 | int height = getFreeformHeight(availableRect); |
Andrii Kulian | 2e751b8 | 2016-03-16 16:59:32 -0700 | [diff] [blame] | 172 | if (windowLayout.height > 0) { |
| 173 | height = windowLayout.height; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 174 | } |
Andrii Kulian | 2e751b8 | 2016-03-16 16:59:32 -0700 | [diff] [blame] | 175 | if (windowLayout.heightFraction > 0) { |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 176 | height = (int) (availableRect.height() * windowLayout.heightFraction); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 177 | } |
| 178 | return height; |
| 179 | } |
| 180 | |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 181 | private void positionBottomLeft(ArrayList<TaskRecord> tasks, Rect availableRect, int width, |
| 182 | int height, Rect result) { |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 183 | mTmpProposal.set(availableRect.left, availableRect.bottom - height, |
| 184 | availableRect.left + width, availableRect.bottom); |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 185 | position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT, |
| 186 | result); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 187 | } |
| 188 | |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 189 | private void positionBottomRight(ArrayList<TaskRecord> tasks, Rect availableRect, int width, |
| 190 | int height, Rect result) { |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 191 | mTmpProposal.set(availableRect.right - width, availableRect.bottom - height, |
| 192 | availableRect.right, availableRect.bottom); |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 193 | position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT, |
| 194 | result); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 195 | } |
| 196 | |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 197 | private void positionTopLeft(ArrayList<TaskRecord> tasks, Rect availableRect, int width, |
| 198 | int height, Rect result) { |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 199 | mTmpProposal.set(availableRect.left, availableRect.top, |
| 200 | availableRect.left + width, availableRect.top + height); |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 201 | position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT, |
| 202 | result); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 203 | } |
| 204 | |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 205 | private void positionTopRight(ArrayList<TaskRecord> tasks, Rect availableRect, int width, |
| 206 | int height, Rect result) { |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 207 | mTmpProposal.set(availableRect.right - width, availableRect.top, |
| 208 | availableRect.right, availableRect.top + height); |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 209 | position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT, |
| 210 | result); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 211 | } |
| 212 | |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 213 | private void positionCenter(ArrayList<TaskRecord> tasks, Rect availableRect, int width, |
| 214 | int height, Rect result) { |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 215 | final int defaultFreeformLeft = getFreeformStartLeft(availableRect); |
| 216 | final int defaultFreeformTop = getFreeformStartTop(availableRect); |
| 217 | mTmpProposal.set(defaultFreeformLeft, defaultFreeformTop, |
| 218 | defaultFreeformLeft + width, defaultFreeformTop + height); |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 219 | position(tasks, availableRect, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN, |
| 220 | result); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 221 | } |
| 222 | |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 223 | private void position(ArrayList<TaskRecord> tasks, Rect availableRect, |
| 224 | Rect proposal, boolean allowRestart, int shiftPolicy, Rect result) { |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 225 | mTmpOriginal.set(proposal); |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 226 | boolean restarted = false; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 227 | while (boundsConflict(proposal, tasks)) { |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 228 | // Unfortunately there is already a task at that spot, so we need to look for some |
| 229 | // other place. |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 230 | shiftStartingPoint(proposal, availableRect, shiftPolicy); |
| 231 | if (shiftedTooFar(proposal, availableRect, shiftPolicy)) { |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 232 | // We don't want the task to go outside of the stack, because it won't look |
| 233 | // nice. Depending on the starting point we either restart, or immediately give up. |
| 234 | if (!allowRestart) { |
| 235 | proposal.set(mTmpOriginal); |
| 236 | break; |
| 237 | } |
| 238 | // We must have started not from the top. Let's restart from there because there |
| 239 | // might be some space there. |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 240 | proposal.set(availableRect.left, availableRect.top, |
| 241 | availableRect.left + proposal.width(), |
| 242 | availableRect.top + proposal.height()); |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 243 | restarted = true; |
| 244 | } |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 245 | if (restarted && (proposal.left > getFreeformStartLeft(availableRect) |
| 246 | || proposal.top > getFreeformStartTop(availableRect))) { |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 247 | // If we restarted and crossed the initial position, let's not struggle anymore. |
| 248 | // The user already must have ton of tasks visible, we can just smack the new |
| 249 | // one in the center. |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 250 | proposal.set(mTmpOriginal); |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 251 | break; |
| 252 | } |
| 253 | } |
Bryce Lee | dacefc4 | 2017-10-10 12:56:02 -0700 | [diff] [blame] | 254 | result.set(proposal); |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 255 | } |
| 256 | |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 257 | private boolean shiftedTooFar(Rect start, Rect availableRect, int shiftPolicy) { |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 258 | switch (shiftPolicy) { |
| 259 | case SHIFT_POLICY_HORIZONTAL_LEFT: |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 260 | return start.left < availableRect.left; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 261 | case SHIFT_POLICY_HORIZONTAL_RIGHT: |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 262 | return start.right > availableRect.right; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 263 | default: // SHIFT_POLICY_DIAGONAL_DOWN |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 264 | return start.right > availableRect.right || start.bottom > availableRect.bottom; |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 265 | } |
| 266 | } |
| 267 | |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 268 | private void shiftStartingPoint(Rect posposal, Rect availableRect, int shiftPolicy) { |
| 269 | final int defaultFreeformStepHorizontal = getHorizontalStep(availableRect); |
| 270 | final int defaultFreeformStepVertical = getVerticalStep(availableRect); |
| 271 | |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 272 | switch (shiftPolicy) { |
| 273 | case SHIFT_POLICY_HORIZONTAL_LEFT: |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 274 | posposal.offset(-defaultFreeformStepHorizontal, 0); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 275 | break; |
| 276 | case SHIFT_POLICY_HORIZONTAL_RIGHT: |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 277 | posposal.offset(defaultFreeformStepHorizontal, 0); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 278 | break; |
| 279 | default: // SHIFT_POLICY_DIAGONAL_DOWN: |
Bryce Lee | 9ad3eb3 | 2017-10-10 10:10:31 -0700 | [diff] [blame] | 280 | posposal.offset(defaultFreeformStepHorizontal, defaultFreeformStepVertical); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 281 | break; |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | private static boolean boundsConflict(Rect proposal, ArrayList<TaskRecord> tasks) { |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 286 | for (int i = tasks.size() - 1; i >= 0; i--) { |
Bryce Lee | f3c6a47 | 2017-11-14 14:53:06 -0800 | [diff] [blame] | 287 | final TaskRecord task = tasks.get(i); |
| 288 | if (!task.mActivities.isEmpty() && !task.matchParentBounds()) { |
| 289 | final Rect bounds = task.getOverrideBounds(); |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 290 | if (closeLeftTopCorner(proposal, bounds) || closeRightTopCorner(proposal, bounds) |
| 291 | || closeLeftBottomCorner(proposal, bounds) |
| 292 | || closeRightBottomCorner(proposal, bounds)) { |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 293 | return true; |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | return false; |
| 298 | } |
| 299 | |
Filip Gruszczynski | 9b1ce52 | 2015-08-20 18:37:19 -0700 | [diff] [blame] | 300 | private static final boolean closeLeftTopCorner(Rect first, Rect second) { |
| 301 | return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE |
| 302 | && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE; |
| 303 | } |
| 304 | |
| 305 | private static final boolean closeRightTopCorner(Rect first, Rect second) { |
| 306 | return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE |
| 307 | && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE; |
| 308 | } |
| 309 | |
| 310 | private static final boolean closeLeftBottomCorner(Rect first, Rect second) { |
| 311 | return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE |
| 312 | && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE; |
| 313 | } |
| 314 | |
| 315 | private static final boolean closeRightBottomCorner(Rect first, Rect second) { |
| 316 | return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE |
| 317 | && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE; |
| 318 | } |
Filip Gruszczynski | e5390e7 | 2015-08-18 16:39:00 -0700 | [diff] [blame] | 319 | } |