1bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet/*
2bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * Copyright (C) 2016 The Android Open Source Project
3bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet *
4bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * Licensed under the Apache License, Version 2.0 (the "License");
5bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * you may not use this file except in compliance with the License.
6bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * You may obtain a copy of the License at
7bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet *
8bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet *      http://www.apache.org/licenses/LICENSE-2.0
9bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet *
10bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * Unless required by applicable law or agreed to in writing, software
11bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * distributed under the License is distributed on an "AS IS" BASIS,
12bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * See the License for the specific language governing permissions and
14bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet * limitations under the License.
15bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet */
16bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
17bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetpackage com.android.systemui.recents.views.grid;
18bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
19cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggiimport static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.*;
20cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi
21bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.content.Context;
22bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.content.res.Resources;
23bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.graphics.Point;
24bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.graphics.Rect;
25bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.view.WindowManager;
26bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
27bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.R;
28c0e181394431fb13420f7bd0415231075cb1b469Jiaquan Heimport com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
29c0e181394431fb13420f7bd0415231075cb1b469Jiaquan Heimport com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
30bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.misc.Utilities;
31cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggiimport com.android.systemui.recents.model.Task;
32bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
33bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.views.TaskViewTransform;
34bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
35cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggiimport java.util.ArrayList;
36cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi
37bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetpublic class TaskGridLayoutAlgorithm  {
38bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
39bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private final String TAG = "TaskGridLayoutAlgorithm";
40883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He    public static final int MAX_LAYOUT_TASK_COUNT = 8;
41bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
42bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The horizontal padding around the whole recents view. */
43bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingLeftRight;
44bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The vertical padding around the whole recents view. */
45bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingTopBottom;
46bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The padding between task views. */
47bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingTaskView;
48bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
49bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mWindowRect;
50bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Point mScreenSize = new Point();
51bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
52bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mTaskGridRect;
53bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
54bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The height, in pixels, of each task view's title bar. */
55bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mTitleBarHeight;
56bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
57bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The aspect ratio of each task thumbnail, without the title bar. */
58bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private float mAppAspectRatio;
59bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mSystemInsets = new Rect();
60bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
6121f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    /** The thickness of the focused task view frame. */
6221f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    private int mFocusedFrameThickness;
6321f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
640a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    /**
650a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * When the amount of tasks is determined, the size and position of every task view can be
660a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * decided. Each instance of TaskGridRectInfo store the task view information for a certain
670a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * amount of tasks.
680a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     */
690a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    class TaskGridRectInfo {
700a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        Rect size;
710a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int[] xOffsets;
720a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int[] yOffsets;
73c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int tasksPerLine;
74c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int lines;
750a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
76c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        TaskGridRectInfo(int taskCount) {
770a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            size = new Rect();
780a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            xOffsets = new int[taskCount];
790a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            yOffsets = new int[taskCount];
800a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
810a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
82776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            tasksPerLine = getTasksPerLine(layoutTaskCount);
83776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            lines = layoutTaskCount < 4 ? 1 : 2;
840a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
85c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // A couple of special cases.
86c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
87c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            boolean landscapeTaskView = mAppAspectRatio > 1;
88c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // If we're in portrait but task views are landscape, show more lines of fewer tasks.
89c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            if (!landscapeWindow && landscapeTaskView) {
90c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                tasksPerLine = layoutTaskCount < 2 ? 1 : 2;
91c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                lines = layoutTaskCount < 3 ? 1 : (
92c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                        layoutTaskCount < 5 ? 2 : (
93c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                                layoutTaskCount < 7 ? 3 : 4));
94c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            }
95c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // If we're in landscape but task views are portrait, show fewer lines of more tasks.
96c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            if (landscapeWindow && !landscapeTaskView) {
97c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                tasksPerLine = layoutTaskCount < 7 ? layoutTaskCount : 6;
98c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                lines = layoutTaskCount < 7 ? 1 : 2;
99c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            }
100c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet
1010a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int taskWidth, taskHeight;
102c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int maxTaskWidth = (mWindowRect.width() - 2 * mPaddingLeftRight
1030a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
104c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int maxTaskHeight = (mWindowRect.height() - 2 * mPaddingTopBottom
1050a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (lines - 1) * mPaddingTaskView) / lines;
1060a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1070a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
1080a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // Width bound.
1090a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskWidth = maxTaskWidth;
11090f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                // Here we should round the height to the nearest integer.
11190f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight + 0.5);
1120a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            } else {
1130a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // Height bound.
1140a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskHeight = maxTaskHeight;
11590f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                // Here we should round the width to the nearest integer.
11690f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio + 0.5);
1170a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            }
1180a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            size.set(0, 0, taskWidth, taskHeight);
1190a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
120c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int emptySpaceX = mWindowRect.width() - 2 * mPaddingLeftRight
1210a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
122c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int emptySpaceY = mWindowRect.height() - 2 * mPaddingTopBottom
1230a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
1240a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) {
1250a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // We also need to invert the index in order to display the most recent tasks first.
1260a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int taskLayoutIndex = taskCount - taskIndex - 1;
1270a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1280a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int xIndex = taskLayoutIndex % tasksPerLine;
1290a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int yIndex = taskLayoutIndex / tasksPerLine;
130c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                xOffsets[taskIndex] = mWindowRect.left +
1310a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
132c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                yOffsets[taskIndex] = mWindowRect.top +
1330a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
1340a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            }
1350a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        }
136776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet
137776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet        private int getTasksPerLine(int taskCount) {
138776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            switch(taskCount) {
139776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 0:
140776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 0;
141776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 1:
142776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 1;
143776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 2:
144776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 4:
145776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 2;
146776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 3:
147776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 5:
148776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 6:
149776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 3;
150776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 7:
151776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 8:
152776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 4;
153776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                default:
154776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    throw new IllegalArgumentException("Unsupported task count " + taskCount);
155776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            }
156776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet        }
1570a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    }
1580a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1590a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    /**
1600a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * We can find task view sizes and positions from mTaskGridRectInfoList[k - 1] when there
1610a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * are k tasks.
1620a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     */
163c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet    private TaskGridRectInfo[] mTaskGridRectInfoList;
1640a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
165bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public TaskGridLayoutAlgorithm(Context context) {
166bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        reloadOnConfigurationChange(context);
167bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
168bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
169bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void reloadOnConfigurationChange(Context context) {
170bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Resources res = context.getResources();
171bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
17221f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        mFocusedFrameThickness = res.getDimensionPixelSize(
17321f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            R.dimen.recents_grid_task_view_focused_frame_thickness);
17421f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
175bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mTaskGridRect = new Rect();
176bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
177bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
178bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        WindowManager windowManager = (WindowManager) context
179bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet                .getSystemService(Context.WINDOW_SERVICE);
180bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        windowManager.getDefaultDisplay().getRealSize(mScreenSize);
181bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
182bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        updateAppAspectRatio();
183bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
184bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
185883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He    /**
186883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * Returns the proper task view transform of a certain task view, according to its index and the
187883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * amount of task views.
188883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param taskIndex     The index of the task view whose transform we want. It's never greater
189883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     *                      than {@link MAX_LAYOUT_TASK_COUNT}.
190883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param taskCount     The current amount of task views.
191883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param transformOut  The result transform that this method returns.
192883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param stackLayout   The base stack layout algorithm.
193883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @return  The expected transform of the (taskIndex)th task view.
194883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     */
195bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public TaskViewTransform getTransform(int taskIndex, int taskCount,
196bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
197244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        if (taskCount == 0) {
198244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He            transformOut.reset();
199244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He            return transformOut;
200244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        }
201bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
2020a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
2030a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        mTaskGridRect.set(gridInfo.size);
204bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
2050a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int x = gridInfo.xOffsets[taskIndex];
2060a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int y = gridInfo.yOffsets[taskIndex];
207bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float z = stackLayout.mMaxTranslationZ;
208bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
20915fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        // We always set the dim alpha to 0, since we don't want grid task views to dim.
210bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float dimAlpha = 0f;
21115fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        // We always set the alpha of the view outline to 1, to make sure the shadow is visible.
21215fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        float viewOutlineAlpha = 1f;
21315fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He
2140a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // We also need to invert the index in order to display the most recent tasks first.
2150a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int taskLayoutIndex = taskCount - taskIndex - 1;
216244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
217bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
218bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        // Fill out the transform
219bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.scale = 1f;
220bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.alpha = isTaskViewVisible ? 1f : 0f;
221bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.translationZ = z;
222bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.dimAlpha = dimAlpha;
223bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.viewOutlineAlpha = viewOutlineAlpha;
224bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.rect.set(mTaskGridRect);
225bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.rect.offset(x, y);
226bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
227bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        // We only show the 8 most recent tasks.
228bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.visible = isTaskViewVisible;
229bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        return transformOut;
230bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
231bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
232c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    /**
233c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * Return the proper task index to focus for arrow key navigation.
234c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param taskCount             The amount of tasks.
235c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param currentFocusedIndex   The index of the currently focused task.
236c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param direction             The direction we're navigating.
237c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @return  The index of the task that should get the focus.
238c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     */
239c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
240c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
241c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            return -1;
242c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
243c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        if (currentFocusedIndex == -1) {
244c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            return 0;
245c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
246c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int newIndex = currentFocusedIndex;
247c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
248c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
249c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        switch (direction) {
250c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case UP:
251c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex += gridInfo.tasksPerLine;
252c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
253c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
254c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case DOWN:
255c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex -= gridInfo.tasksPerLine;
256c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
257c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
258c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case LEFT:
259c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex++;
260c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
261c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
262c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
263c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case RIGHT:
264c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex--;
265c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                int rightMostIndex =
266c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                    (taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
267c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
268c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
269c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
270c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
271c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        return newIndex;
272c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    }
273c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He
274c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet    public void initialize(Rect windowRect) {
275bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mWindowRect = windowRect;
276c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        // Define paddings in terms of percentage of the total area.
277c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        mPaddingLeftRight = (int) (0.025f * Math.min(mWindowRect.width(), mWindowRect.height()));
278c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        mPaddingTopBottom = (int) (0.1 * mWindowRect.height());
2790a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
2800a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // Pre-calculate the positions and offsets of task views so that we can reuse them directly
2810a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // in the future.
2820a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        mTaskGridRectInfoList = new TaskGridRectInfo[MAX_LAYOUT_TASK_COUNT];
2830a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        for (int i = 0; i < MAX_LAYOUT_TASK_COUNT; i++) {
2840a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            mTaskGridRectInfoList[i] = new TaskGridRectInfo(i + 1);
2850a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        }
286bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
287bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
288bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void setSystemInsets(Rect systemInsets) {
289bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mSystemInsets = systemInsets;
290bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        updateAppAspectRatio();
291bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
292bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
293bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private void updateAppAspectRatio() {
294bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
295bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
296bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mAppAspectRatio = (float) usableWidth / (float) usableHeight;
297bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
298bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
299bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public Rect getStackActionButtonRect() {
300c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        Rect buttonRect = new Rect(mWindowRect);
301bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.right -= mPaddingLeftRight;
302bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.left += mPaddingLeftRight;
303bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
304bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        return buttonRect;
305bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
3068f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He
30721f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    public void updateTaskGridRect(int taskCount) {
30821f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        if (taskCount > 0) {
30921f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
31021f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            mTaskGridRect.set(gridInfo.size);
31121f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        }
31221f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    }
31321f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
3148f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He    public Rect getTaskGridRect() {
3158f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He        return mTaskGridRect;
3168f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He    }
31721f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
31821f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    public int getFocusFrameThickness() {
31921f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        return mFocusedFrameThickness;
32021f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    }
321cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi
322cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi    public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
323cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi        int visibleCount = Math.min(TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT, tasks.size());
324cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi        return new VisibilityReport(visibleCount, visibleCount);
325cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi    }
326bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet}
327