1e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski/* 2e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * Copyright (C) 2015 The Android Open Source Project 3e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * 4e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * Licensed under the Apache License, Version 2.0 (the "License"); 5e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * you may not use this file except in compliance with the License. 6e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * You may obtain a copy of the License at 7e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * 8e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * http://www.apache.org/licenses/LICENSE-2.0 9e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * 10e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * Unless required by applicable law or agreed to in writing, software 11e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * distributed under the License is distributed on an "AS IS" BASIS, 12e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * See the License for the specific language governing permissions and 14e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * limitations under the License. 15e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski */ 16e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 17e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynskipackage com.android.server.am; 18e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 199b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynskiimport static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 209b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynskiimport static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 219b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 229b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynskiimport android.annotation.Nullable; 239b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynskiimport android.content.pm.ActivityInfo; 24e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynskiimport android.graphics.Point; 25e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynskiimport android.graphics.Rect; 269b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynskiimport android.util.Slog; 27e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynskiimport android.view.Display; 289b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynskiimport android.view.Gravity; 29e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 30e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynskiimport java.util.ArrayList; 31e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 32e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski/** 33e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * Determines where a launching task should be positioned and sized on the display. 349b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski * 359b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski * The positioner is fairly simple. For the new task it tries default position based on the gravity 369b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski * and compares corners of the task with corners of existing tasks. If some two pairs of corners are 379b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski * sufficiently close enough, it shifts the bounds of the new task and tries again. When it exhausts 389b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski * all possible shifts, it gives up and puts the task in the original position. 39e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski */ 40e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynskiclass LaunchingTaskPositioner { 419b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final String TAG = TAG_WITH_CLASS_NAME ? "LaunchingTaskPositioner" : TAG_AM; 429b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 43e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // Determines how close window frames/corners have to be to call them colliding. 44e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private static final int BOUNDS_CONFLICT_MIN_DISTANCE = 4; 45e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 46e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // Task will receive dimensions based on available dimensions divided by this. 47e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private static final int WINDOW_SIZE_DENOMINATOR = 2; 48e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 49e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // Task will receive margins based on available dimensions divided by this. 50e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private static final int MARGIN_SIZE_DENOMINATOR = 4; 51e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 52e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // If task bounds collide with some other, we will step and try again until we find a good 53e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // position. The step will be determined by using dimensions and dividing it by this. 54e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private static final int STEP_DENOMINATOR = 16; 55e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 56e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // We always want to step by at least this. 57e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private static final int MINIMAL_STEP = 1; 58e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 599b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski // Used to indicate if positioning algorithm is allowed to restart from the beginning, when it 609b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski // reaches the end of stack bounds. 619b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final boolean ALLOW_RESTART = true; 629b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 639b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final int SHIFT_POLICY_DIAGONAL_DOWN = 1; 649b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final int SHIFT_POLICY_HORIZONTAL_RIGHT = 2; 659b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final int SHIFT_POLICY_HORIZONTAL_LEFT = 3; 669b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 67e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private boolean mDefaultStartBoundsConfigurationSet = false; 68e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private final Rect mAvailableRect = new Rect(); 699b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private final Rect mTmpProposal = new Rect(); 709b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private final Rect mTmpOriginal = new Rect(); 719b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 72e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private int mDefaultFreeformStartX; 73e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private int mDefaultFreeformStartY; 74e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private int mDefaultFreeformWidth; 75e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private int mDefaultFreeformHeight; 76e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private int mDefaultFreeformStepHorizontal; 77e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private int mDefaultFreeformStepVertical; 78e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private int mDisplayWidth; 79e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski private int mDisplayHeight; 80e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 81e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski void setDisplay(Display display) { 82e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski Point size = new Point(); 83e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski display.getSize(size); 84e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDisplayWidth = size.x; 85e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDisplayHeight = size.y; 86e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 87e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 88e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski void configure(Rect stackBounds) { 89e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski if (stackBounds == null) { 90e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mAvailableRect.set(0, 0, mDisplayWidth, mDisplayHeight); 91e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } else { 92e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mAvailableRect.set(stackBounds); 93e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 94e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski int width = mAvailableRect.width(); 95e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski int height = mAvailableRect.height(); 96e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDefaultFreeformStartX = mAvailableRect.left + width / MARGIN_SIZE_DENOMINATOR; 97e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDefaultFreeformStartY = mAvailableRect.top + height / MARGIN_SIZE_DENOMINATOR; 98e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDefaultFreeformWidth = width / WINDOW_SIZE_DENOMINATOR; 99e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDefaultFreeformHeight = height / WINDOW_SIZE_DENOMINATOR; 100e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDefaultFreeformStepHorizontal = Math.max(width / STEP_DENOMINATOR, MINIMAL_STEP); 101e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDefaultFreeformStepVertical = Math.max(height / STEP_DENOMINATOR, MINIMAL_STEP); 102e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDefaultStartBoundsConfigurationSet = true; 103e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 104e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 105e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski /** 106e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * Tries to set task's bound in a way that it won't collide with any other task. By colliding 107e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * we mean that two tasks have left-top corner very close to each other, so one might get 108e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * obfuscated by the other one. 109e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * 110e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * @param task Task for which we want to find bounds that won't collide with other. 111e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski * @param tasks Existing tasks with which we don't want to collide. 1122e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian * @param windowLayout Optional information from the client about how it would like to be sized 1139b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski * and positioned. 114e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski */ 1159b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks, 1162e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian @Nullable ActivityInfo.WindowLayout windowLayout) { 117e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski if (!mDefaultStartBoundsConfigurationSet) { 118e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski return; 119e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 1202e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian if (windowLayout == null) { 1219b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski positionCenter(task, tasks, mDefaultFreeformWidth, mDefaultFreeformHeight); 1229b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return; 1239b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1242e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian int width = getFinalWidth(windowLayout); 1252e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian int height = getFinalHeight(windowLayout); 1262e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK; 1272e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; 1289b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski if (verticalGravity == Gravity.TOP) { 1299b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski if (horizontalGravity == Gravity.RIGHT) { 1309b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski positionTopRight(task, tasks, width, height); 1319b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } else { 1329b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski positionTopLeft(task, tasks, width, height); 1339b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1349b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } else if (verticalGravity == Gravity.BOTTOM) { 1359b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski if (horizontalGravity == Gravity.RIGHT) { 1369b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski positionBottomRight(task, tasks, width, height); 1379b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } else { 1389b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski positionBottomLeft(task, tasks, width, height); 1399b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1409b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } else { 1419b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski // Some fancy gravity setting that we don't support yet. We just put the activity in the 1429b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski // center. 1432e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian Slog.w(TAG, "Received unsupported gravity: " + windowLayout.gravity 1449b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski + ", positioning in the center instead."); 1459b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski positionCenter(task, tasks, width, height); 1469b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1479b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1489b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 1492e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian private int getFinalWidth(ActivityInfo.WindowLayout windowLayout) { 1509b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski int width = mDefaultFreeformWidth; 1512e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian if (windowLayout.width > 0) { 1522e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian width = windowLayout.width; 1539b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1542e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian if (windowLayout.widthFraction > 0) { 1552e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian width = (int) (mAvailableRect.width() * windowLayout.widthFraction); 1569b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1579b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return width; 1589b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1599b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 1602e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian private int getFinalHeight(ActivityInfo.WindowLayout windowLayout) { 1619b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski int height = mDefaultFreeformHeight; 1622e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian if (windowLayout.height > 0) { 1632e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian height = windowLayout.height; 1649b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1652e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian if (windowLayout.heightFraction > 0) { 1662e751b8c778fd991fcdcec3bc2d1f32a722f436bAndrii Kulian height = (int) (mAvailableRect.height() * windowLayout.heightFraction); 1679b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1689b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return height; 1699b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1709b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 1719b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width, 1729b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski int height) { 1739b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mTmpProposal.set(mAvailableRect.left, mAvailableRect.bottom - height, 1749b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mAvailableRect.left + width, mAvailableRect.bottom); 1759b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT); 1769b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1779b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 1789b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width, 1799b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski int height) { 1809b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.bottom - height, 1819b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mAvailableRect.right, mAvailableRect.bottom); 1829b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT); 1839b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1849b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 1859b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width, 1869b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski int height) { 1879b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mTmpProposal.set(mAvailableRect.left, mAvailableRect.top, 1889b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mAvailableRect.left + width, mAvailableRect.top + height); 1899b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT); 1909b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1919b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 1929b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width, 1939b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski int height) { 1949b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.top, 1959b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mAvailableRect.right, mAvailableRect.top + height); 1969b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT); 1979b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 1989b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 1999b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks, int width, 2009b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski int height) { 2019b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mTmpProposal.set(mDefaultFreeformStartX, mDefaultFreeformStartY, 2029b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mDefaultFreeformStartX + width, mDefaultFreeformStartY + height); 2039b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski position(task, tasks, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN); 2049b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2059b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 2069b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect proposal, 2079b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski boolean allowRestart, int shiftPolicy) { 2089b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mTmpOriginal.set(proposal); 209e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski boolean restarted = false; 2109b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski while (boundsConflict(proposal, tasks)) { 211e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // Unfortunately there is already a task at that spot, so we need to look for some 212e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // other place. 2139b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski shiftStartingPoint(proposal, shiftPolicy); 2149b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski if (shiftedToFar(proposal, shiftPolicy)) { 2159b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski // We don't want the task to go outside of the stack, because it won't look 2169b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski // nice. Depending on the starting point we either restart, or immediately give up. 2179b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski if (!allowRestart) { 2189b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski proposal.set(mTmpOriginal); 2199b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski break; 2209b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2219b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski // We must have started not from the top. Let's restart from there because there 2229b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski // might be some space there. 2239b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski proposal.set(mAvailableRect.left, mAvailableRect.top, 2249b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mAvailableRect.left + proposal.width(), 2259b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski mAvailableRect.top + proposal.height()); 226e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski restarted = true; 227e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 2289b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski if (restarted && (proposal.left > mDefaultFreeformStartX 2299b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski || proposal.top > mDefaultFreeformStartY)) { 230e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // If we restarted and crossed the initial position, let's not struggle anymore. 231e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // The user already must have ton of tasks visible, we can just smack the new 232e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski // one in the center. 2339b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski proposal.set(mTmpOriginal); 234e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski break; 235e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 236e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 237aff7f134a29d0eeac9ec07db4b97c36ecb202ea5Filip Gruszczynski task.updateOverrideConfiguration(proposal); 2389b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2399b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 2409b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private boolean shiftedToFar(Rect start, int shiftPolicy) { 2419b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski switch (shiftPolicy) { 2429b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski case SHIFT_POLICY_HORIZONTAL_LEFT: 2439b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return start.left < mAvailableRect.left; 2449b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski case SHIFT_POLICY_HORIZONTAL_RIGHT: 2459b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return start.right > mAvailableRect.right; 2469b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski default: // SHIFT_POLICY_DIAGONAL_DOWN 2479b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return start.right > mAvailableRect.right || start.bottom > mAvailableRect.bottom; 2489b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 249e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 250e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 2519b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private void shiftStartingPoint(Rect posposal, int shiftPolicy) { 2529b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski switch (shiftPolicy) { 2539b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski case SHIFT_POLICY_HORIZONTAL_LEFT: 2549b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski posposal.offset(-mDefaultFreeformStepHorizontal, 0); 2559b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski break; 2569b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski case SHIFT_POLICY_HORIZONTAL_RIGHT: 2579b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski posposal.offset(mDefaultFreeformStepHorizontal, 0); 2589b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski break; 2599b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski default: // SHIFT_POLICY_DIAGONAL_DOWN: 2609b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski posposal.offset(mDefaultFreeformStepHorizontal, mDefaultFreeformStepVertical); 2619b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski break; 2629b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2639b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2649b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 2659b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static boolean boundsConflict(Rect proposal, ArrayList<TaskRecord> tasks) { 266e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski for (int i = tasks.size() - 1; i >= 0; i--) { 267e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski TaskRecord task = tasks.get(i); 2689b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski if (!task.mActivities.isEmpty() && task.mBounds != null) { 269e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski Rect bounds = task.mBounds; 2709b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski if (closeLeftTopCorner(proposal, bounds) || closeRightTopCorner(proposal, bounds) 2719b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski || closeLeftBottomCorner(proposal, bounds) 2729b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski || closeRightBottomCorner(proposal, bounds)) { 273e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski return true; 274e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 275e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 276e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 277e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski return false; 278e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 279e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski 2809b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final boolean closeLeftTopCorner(Rect first, Rect second) { 2819b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE 2829b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE; 2839b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2849b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 2859b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final boolean closeRightTopCorner(Rect first, Rect second) { 2869b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE 2879b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE; 2889b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2899b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 2909b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final boolean closeLeftBottomCorner(Rect first, Rect second) { 2919b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE 2929b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE; 2939b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2949b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 2959b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski private static final boolean closeRightBottomCorner(Rect first, Rect second) { 2969b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE 2979b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE; 2989b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski } 2999b1ce52f254b4d9c17ebf437f19f45603d3ad5b2Filip Gruszczynski 300e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski void reset() { 301e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski mDefaultStartBoundsConfigurationSet = false; 302e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski } 303e5390e7379303cfef6160f4679bd7b288b57a9f8Filip Gruszczynski} 304