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.Direction;
296519c1b0fe85c5f25115539e936e4333e8537098Winson Chungimport com.android.systemui.shared.recents.utilities.Utilities;
306519c1b0fe85c5f25115539e936e4333e8537098Winson Chungimport com.android.systemui.shared.recents.model.Task;
31bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
32bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.views.TaskViewTransform;
33bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
34cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggiimport java.util.ArrayList;
35cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi
36bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetpublic class TaskGridLayoutAlgorithm  {
37bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
38bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private final String TAG = "TaskGridLayoutAlgorithm";
39883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He    public static final int MAX_LAYOUT_TASK_COUNT = 8;
40bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
41bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The horizontal padding around the whole recents view. */
42bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingLeftRight;
43bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The vertical padding around the whole recents view. */
44bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingTopBottom;
45bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The padding between task views. */
46bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingTaskView;
47bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
48bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mWindowRect;
49bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Point mScreenSize = new Point();
50bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
51bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mTaskGridRect;
52bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
53bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The height, in pixels, of each task view's title bar. */
54bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mTitleBarHeight;
55bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
56bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The aspect ratio of each task thumbnail, without the title bar. */
57bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private float mAppAspectRatio;
58bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mSystemInsets = new Rect();
59bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
6021f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    /** The thickness of the focused task view frame. */
6121f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    private int mFocusedFrameThickness;
6221f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
630a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    /**
640a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * When the amount of tasks is determined, the size and position of every task view can be
650a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * decided. Each instance of TaskGridRectInfo store the task view information for a certain
660a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * amount of tasks.
670a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     */
680a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    class TaskGridRectInfo {
690a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        Rect size;
700a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int[] xOffsets;
710a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int[] yOffsets;
72c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int tasksPerLine;
73c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int lines;
740a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
75c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        TaskGridRectInfo(int taskCount) {
760a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            size = new Rect();
770a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            xOffsets = new int[taskCount];
780a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            yOffsets = new int[taskCount];
790a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
800a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
81776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            tasksPerLine = getTasksPerLine(layoutTaskCount);
82776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            lines = layoutTaskCount < 4 ? 1 : 2;
830a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
84c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // A couple of special cases.
85c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
86c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            boolean landscapeTaskView = mAppAspectRatio > 1;
87c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // If we're in portrait but task views are landscape, show more lines of fewer tasks.
88c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            if (!landscapeWindow && landscapeTaskView) {
89c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                tasksPerLine = layoutTaskCount < 2 ? 1 : 2;
90c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                lines = layoutTaskCount < 3 ? 1 : (
91c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                        layoutTaskCount < 5 ? 2 : (
92c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                                layoutTaskCount < 7 ? 3 : 4));
93c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            }
94c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // If we're in landscape but task views are portrait, show fewer lines of more tasks.
95c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            if (landscapeWindow && !landscapeTaskView) {
96c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                tasksPerLine = layoutTaskCount < 7 ? layoutTaskCount : 6;
97c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                lines = layoutTaskCount < 7 ? 1 : 2;
98c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            }
99c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet
1000a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int taskWidth, taskHeight;
101c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int maxTaskWidth = (mWindowRect.width() - 2 * mPaddingLeftRight
1020a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
103c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int maxTaskHeight = (mWindowRect.height() - 2 * mPaddingTopBottom
1040a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (lines - 1) * mPaddingTaskView) / lines;
1050a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1060a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
1070a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // Width bound.
1080a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskWidth = maxTaskWidth;
10990f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                // Here we should round the height to the nearest integer.
11090f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight + 0.5);
1110a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            } else {
1120a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // Height bound.
1130a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskHeight = maxTaskHeight;
11490f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                // Here we should round the width to the nearest integer.
11590f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio + 0.5);
1160a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            }
1170a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            size.set(0, 0, taskWidth, taskHeight);
1180a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
119c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int emptySpaceX = mWindowRect.width() - 2 * mPaddingLeftRight
1200a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
121c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int emptySpaceY = mWindowRect.height() - 2 * mPaddingTopBottom
1220a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
1230a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) {
1240a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // We also need to invert the index in order to display the most recent tasks first.
1250a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int taskLayoutIndex = taskCount - taskIndex - 1;
1260a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1270a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int xIndex = taskLayoutIndex % tasksPerLine;
1280a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int yIndex = taskLayoutIndex / tasksPerLine;
129c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                xOffsets[taskIndex] = mWindowRect.left +
1300a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
131c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                yOffsets[taskIndex] = mWindowRect.top +
1320a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
1330a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            }
1340a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        }
135776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet
136776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet        private int getTasksPerLine(int taskCount) {
137776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            switch(taskCount) {
138776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 0:
139776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 0;
140776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 1:
141776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 1;
142776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 2:
143776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 4:
144776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 2;
145776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 3:
146776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 5:
147776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 6:
148776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 3;
149776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 7:
150776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 8:
151776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 4;
152776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                default:
153776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    throw new IllegalArgumentException("Unsupported task count " + taskCount);
154776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            }
155776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet        }
1560a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    }
1570a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1580a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    /**
1590a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * We can find task view sizes and positions from mTaskGridRectInfoList[k - 1] when there
1600a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * are k tasks.
1610a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     */
162c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet    private TaskGridRectInfo[] mTaskGridRectInfoList;
1630a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
164bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public TaskGridLayoutAlgorithm(Context context) {
165bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        reloadOnConfigurationChange(context);
166bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
167bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
168bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void reloadOnConfigurationChange(Context context) {
169bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Resources res = context.getResources();
170bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
17121f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        mFocusedFrameThickness = res.getDimensionPixelSize(
17221f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            R.dimen.recents_grid_task_view_focused_frame_thickness);
17321f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
174bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mTaskGridRect = new Rect();
175bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
176bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
177bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        WindowManager windowManager = (WindowManager) context
178bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet                .getSystemService(Context.WINDOW_SERVICE);
179bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        windowManager.getDefaultDisplay().getRealSize(mScreenSize);
180bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
181bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        updateAppAspectRatio();
182bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
183bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
184883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He    /**
185883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * Returns the proper task view transform of a certain task view, according to its index and the
186883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * amount of task views.
187883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param taskIndex     The index of the task view whose transform we want. It's never greater
188883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     *                      than {@link MAX_LAYOUT_TASK_COUNT}.
189883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param taskCount     The current amount of task views.
190883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param transformOut  The result transform that this method returns.
191883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param stackLayout   The base stack layout algorithm.
192883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @return  The expected transform of the (taskIndex)th task view.
193883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     */
194bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public TaskViewTransform getTransform(int taskIndex, int taskCount,
195bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
196244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        if (taskCount == 0) {
197244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He            transformOut.reset();
198244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He            return transformOut;
199244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        }
200bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
2010a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
2020a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        mTaskGridRect.set(gridInfo.size);
203bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
2040a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int x = gridInfo.xOffsets[taskIndex];
2050a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int y = gridInfo.yOffsets[taskIndex];
206bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float z = stackLayout.mMaxTranslationZ;
207bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
20815fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        // We always set the dim alpha to 0, since we don't want grid task views to dim.
209bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float dimAlpha = 0f;
21015fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        // We always set the alpha of the view outline to 1, to make sure the shadow is visible.
21115fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        float viewOutlineAlpha = 1f;
21215fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He
2130a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // We also need to invert the index in order to display the most recent tasks first.
2140a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int taskLayoutIndex = taskCount - taskIndex - 1;
215244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
216bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
217bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        // Fill out the transform
218bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.scale = 1f;
219bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.alpha = isTaskViewVisible ? 1f : 0f;
220bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.translationZ = z;
221bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.dimAlpha = dimAlpha;
222bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.viewOutlineAlpha = viewOutlineAlpha;
223bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.rect.set(mTaskGridRect);
224bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.rect.offset(x, y);
225bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
226bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        // We only show the 8 most recent tasks.
227bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.visible = isTaskViewVisible;
228bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        return transformOut;
229bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
230bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
231c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    /**
232c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * Return the proper task index to focus for arrow key navigation.
233c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param taskCount             The amount of tasks.
234c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param currentFocusedIndex   The index of the currently focused task.
235c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param direction             The direction we're navigating.
236c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @return  The index of the task that should get the focus.
237c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     */
238c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
239c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
240c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            return -1;
241c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
242c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        if (currentFocusedIndex == -1) {
243c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            return 0;
244c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
245c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int newIndex = currentFocusedIndex;
246c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
247c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
248c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        switch (direction) {
249c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case UP:
250c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex += gridInfo.tasksPerLine;
251c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
252c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
253c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case DOWN:
254c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex -= gridInfo.tasksPerLine;
255c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
256c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
257c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case LEFT:
258c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex++;
259c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
260c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
261c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
262c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case RIGHT:
263c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex--;
264c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                int rightMostIndex =
265c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                    (taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
266c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
267c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
268c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
269c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
270c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        return newIndex;
271c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    }
272c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He
273c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet    public void initialize(Rect windowRect) {
274bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mWindowRect = windowRect;
275c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        // Define paddings in terms of percentage of the total area.
276c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        mPaddingLeftRight = (int) (0.025f * Math.min(mWindowRect.width(), mWindowRect.height()));
277c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        mPaddingTopBottom = (int) (0.1 * mWindowRect.height());
2780a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
2790a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // Pre-calculate the positions and offsets of task views so that we can reuse them directly
2800a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // in the future.
2810a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        mTaskGridRectInfoList = new TaskGridRectInfo[MAX_LAYOUT_TASK_COUNT];
2820a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        for (int i = 0; i < MAX_LAYOUT_TASK_COUNT; i++) {
2830a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            mTaskGridRectInfoList[i] = new TaskGridRectInfo(i + 1);
2840a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        }
285bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
286bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
287bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void setSystemInsets(Rect systemInsets) {
288bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mSystemInsets = systemInsets;
289bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        updateAppAspectRatio();
290bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
291bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
292bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private void updateAppAspectRatio() {
293bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
294bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
295bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mAppAspectRatio = (float) usableWidth / (float) usableHeight;
296bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
297bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
298bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public Rect getStackActionButtonRect() {
299c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        Rect buttonRect = new Rect(mWindowRect);
300bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.right -= mPaddingLeftRight;
301bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.left += mPaddingLeftRight;
302bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
303bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        return buttonRect;
304bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
3058f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He
30621f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    public void updateTaskGridRect(int taskCount) {
30721f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        if (taskCount > 0) {
30821f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
30921f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            mTaskGridRect.set(gridInfo.size);
31021f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        }
31121f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    }
31221f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
3138f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He    public Rect getTaskGridRect() {
3148f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He        return mTaskGridRect;
3158f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He    }
31621f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
31721f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    public int getFocusFrameThickness() {
31821f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        return mFocusedFrameThickness;
31921f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    }
320cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi
321cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi    public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
322cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi        int visibleCount = Math.min(TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT, tasks.size());
323cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi        return new VisibilityReport(visibleCount, visibleCount);
324cdef591e52e691a6f57e367caa5670fdc4ee1a8aJorim Jaggi    }
325bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet}
326