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