TaskGridLayoutAlgorithm.java revision 776f6bacd67d27e033598f8c8c079bab9d0dd183
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
19bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.content.Context;
20bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.content.res.Resources;
21bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.graphics.Point;
22bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.graphics.Rect;
23bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport android.view.WindowManager;
24bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
25bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.R;
26c0e181394431fb13420f7bd0415231075cb1b469Jiaquan Heimport com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
27c0e181394431fb13420f7bd0415231075cb1b469Jiaquan Heimport com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
28bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.misc.Utilities;
29bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
30bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.views.TaskViewTransform;
31bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
32bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetpublic class TaskGridLayoutAlgorithm  {
33bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
34bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private final String TAG = "TaskGridLayoutAlgorithm";
35883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He    public static final int MAX_LAYOUT_TASK_COUNT = 8;
36bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
37bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The horizontal padding around the whole recents view. */
38bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingLeftRight;
39bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The vertical padding around the whole recents view. */
40bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingTopBottom;
41bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The padding between task views. */
42bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingTaskView;
43bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
44bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mWindowRect;
45bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Point mScreenSize = new Point();
46bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
47bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mTaskGridRect;
48bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
49bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The height, in pixels, of each task view's title bar. */
50bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mTitleBarHeight;
51bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
52bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The aspect ratio of each task thumbnail, without the title bar. */
53bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private float mAppAspectRatio;
54bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mSystemInsets = new Rect();
55bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
5621f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    /** The thickness of the focused task view frame. */
5721f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    private int mFocusedFrameThickness;
5821f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
590a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    /**
600a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * When the amount of tasks is determined, the size and position of every task view can be
610a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * decided. Each instance of TaskGridRectInfo store the task view information for a certain
620a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * amount of tasks.
630a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     */
640a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    class TaskGridRectInfo {
650a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        Rect size;
660a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int[] xOffsets;
670a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int[] yOffsets;
68c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int tasksPerLine;
69c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int lines;
700a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
71c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        TaskGridRectInfo(int taskCount) {
720a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            size = new Rect();
730a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            xOffsets = new int[taskCount];
740a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            yOffsets = new int[taskCount];
750a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
760a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
77776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            tasksPerLine = getTasksPerLine(layoutTaskCount);
78776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            lines = layoutTaskCount < 4 ? 1 : 2;
790a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
80c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // A couple of special cases.
81c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
82c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            boolean landscapeTaskView = mAppAspectRatio > 1;
83c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // If we're in portrait but task views are landscape, show more lines of fewer tasks.
84c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            if (!landscapeWindow && landscapeTaskView) {
85c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                tasksPerLine = layoutTaskCount < 2 ? 1 : 2;
86c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                lines = layoutTaskCount < 3 ? 1 : (
87c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                        layoutTaskCount < 5 ? 2 : (
88c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                                layoutTaskCount < 7 ? 3 : 4));
89c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            }
90c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            // If we're in landscape but task views are portrait, show fewer lines of more tasks.
91c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            if (landscapeWindow && !landscapeTaskView) {
92c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                tasksPerLine = layoutTaskCount < 7 ? layoutTaskCount : 6;
93c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                lines = layoutTaskCount < 7 ? 1 : 2;
94c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            }
95c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet
960a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int taskWidth, taskHeight;
97c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int maxTaskWidth = (mWindowRect.width() - 2 * mPaddingLeftRight
980a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
99c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int maxTaskHeight = (mWindowRect.height() - 2 * mPaddingTopBottom
1000a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (lines - 1) * mPaddingTaskView) / lines;
1010a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1020a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
1030a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // Width bound.
1040a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskWidth = maxTaskWidth;
10590f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                // Here we should round the height to the nearest integer.
10690f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight + 0.5);
1070a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            } else {
1080a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // Height bound.
1090a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskHeight = maxTaskHeight;
11090f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                // Here we should round the width to the nearest integer.
11190f4f5290b971aefcf7cab306b3e95168ed53585Jiaquan He                taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio + 0.5);
1120a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            }
1130a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            size.set(0, 0, taskWidth, taskHeight);
1140a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
115c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int emptySpaceX = mWindowRect.width() - 2 * mPaddingLeftRight
1160a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
117c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet            int emptySpaceY = mWindowRect.height() - 2 * mPaddingTopBottom
1180a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
1190a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) {
1200a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // We also need to invert the index in order to display the most recent tasks first.
1210a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int taskLayoutIndex = taskCount - taskIndex - 1;
1220a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1230a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int xIndex = taskLayoutIndex % tasksPerLine;
1240a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int yIndex = taskLayoutIndex / tasksPerLine;
125c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                xOffsets[taskIndex] = mWindowRect.left +
1260a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
127c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet                yOffsets[taskIndex] = mWindowRect.top +
1280a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
1290a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            }
1300a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        }
131776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet
132776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet        private int getTasksPerLine(int taskCount) {
133776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            switch(taskCount) {
134776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 0:
135776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 0;
136776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 1:
137776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 1;
138776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 2:
139776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 4:
140776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 2;
141776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 3:
142776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 5:
143776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 6:
144776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 3;
145776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 7:
146776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                case 8:
147776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    return 4;
148776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                default:
149776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet                    throw new IllegalArgumentException("Unsupported task count " + taskCount);
150776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet            }
151776f6bacd67d27e033598f8c8c079bab9d0dd183Manu Cornet        }
1520a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    }
1530a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1540a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    /**
1550a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * We can find task view sizes and positions from mTaskGridRectInfoList[k - 1] when there
1560a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * are k tasks.
1570a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     */
158c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet    private TaskGridRectInfo[] mTaskGridRectInfoList;
1590a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
160bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public TaskGridLayoutAlgorithm(Context context) {
161bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        reloadOnConfigurationChange(context);
162bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
163bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
164bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void reloadOnConfigurationChange(Context context) {
165bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Resources res = context.getResources();
166bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
16721f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        mFocusedFrameThickness = res.getDimensionPixelSize(
16821f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            R.dimen.recents_grid_task_view_focused_frame_thickness);
16921f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
170bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mTaskGridRect = new Rect();
171bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
172bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
173bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        WindowManager windowManager = (WindowManager) context
174bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet                .getSystemService(Context.WINDOW_SERVICE);
175bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        windowManager.getDefaultDisplay().getRealSize(mScreenSize);
176bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
177bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        updateAppAspectRatio();
178bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
179bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
180883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He    /**
181883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * Returns the proper task view transform of a certain task view, according to its index and the
182883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * amount of task views.
183883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param taskIndex     The index of the task view whose transform we want. It's never greater
184883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     *                      than {@link MAX_LAYOUT_TASK_COUNT}.
185883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param taskCount     The current amount of task views.
186883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param transformOut  The result transform that this method returns.
187883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @param stackLayout   The base stack layout algorithm.
188883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     * @return  The expected transform of the (taskIndex)th task view.
189883887eac908b4b0145f06c52de00f76e8c833dfJiaquan He     */
190bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public TaskViewTransform getTransform(int taskIndex, int taskCount,
191bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
192244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        if (taskCount == 0) {
193244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He            transformOut.reset();
194244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He            return transformOut;
195244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        }
196bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
1970a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
1980a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        mTaskGridRect.set(gridInfo.size);
199bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
2000a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int x = gridInfo.xOffsets[taskIndex];
2010a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int y = gridInfo.yOffsets[taskIndex];
202bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float z = stackLayout.mMaxTranslationZ;
203bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
20415fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        // We always set the dim alpha to 0, since we don't want grid task views to dim.
205bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float dimAlpha = 0f;
20615fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        // We always set the alpha of the view outline to 1, to make sure the shadow is visible.
20715fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He        float viewOutlineAlpha = 1f;
20815fa3246d8091e6ab3956fd0a833fdd8daa9d6fbJiaquan He
2090a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // We also need to invert the index in order to display the most recent tasks first.
2100a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int taskLayoutIndex = taskCount - taskIndex - 1;
211244bac6c8bb8c7ef621786b42cf26dafaa417829Jiaquan He        boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
212bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
213bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        // Fill out the transform
214bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.scale = 1f;
215bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.alpha = isTaskViewVisible ? 1f : 0f;
216bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.translationZ = z;
217bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.dimAlpha = dimAlpha;
218bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.viewOutlineAlpha = viewOutlineAlpha;
219bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.rect.set(mTaskGridRect);
220bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.rect.offset(x, y);
221bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
222bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        // We only show the 8 most recent tasks.
223bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.visible = isTaskViewVisible;
224bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        return transformOut;
225bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
226bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
227c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    /**
228c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * Return the proper task index to focus for arrow key navigation.
229c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param taskCount             The amount of tasks.
230c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param currentFocusedIndex   The index of the currently focused task.
231c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @param direction             The direction we're navigating.
232c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     * @return  The index of the task that should get the focus.
233c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He     */
234c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
235c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
236c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            return -1;
237c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
238c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        if (currentFocusedIndex == -1) {
239c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            return 0;
240c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
241c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        int newIndex = currentFocusedIndex;
242c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
243c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
244c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        switch (direction) {
245c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case UP:
246c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex += gridInfo.tasksPerLine;
247c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
248c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
249c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case DOWN:
250c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex -= gridInfo.tasksPerLine;
251c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
252c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
253c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case LEFT:
254c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex++;
255c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
256c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
257c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
258c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He            case RIGHT:
259c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex--;
260c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                int rightMostIndex =
261c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                    (taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
262c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
263c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
264c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He                break;
265c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        }
266c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He        return newIndex;
267c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He    }
268c0e181394431fb13420f7bd0415231075cb1b469Jiaquan He
269c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet    public void initialize(Rect windowRect) {
270bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mWindowRect = windowRect;
271c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        // Define paddings in terms of percentage of the total area.
272c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        mPaddingLeftRight = (int) (0.025f * Math.min(mWindowRect.width(), mWindowRect.height()));
273c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        mPaddingTopBottom = (int) (0.1 * mWindowRect.height());
2740a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
2750a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // Pre-calculate the positions and offsets of task views so that we can reuse them directly
2760a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // in the future.
2770a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        mTaskGridRectInfoList = new TaskGridRectInfo[MAX_LAYOUT_TASK_COUNT];
2780a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        for (int i = 0; i < MAX_LAYOUT_TASK_COUNT; i++) {
2790a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            mTaskGridRectInfoList[i] = new TaskGridRectInfo(i + 1);
2800a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        }
281bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
282bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
283bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void setSystemInsets(Rect systemInsets) {
284bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mSystemInsets = systemInsets;
285bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        updateAppAspectRatio();
286bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
287bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
288bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private void updateAppAspectRatio() {
289bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
290bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
291bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mAppAspectRatio = (float) usableWidth / (float) usableHeight;
292bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
293bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
294bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public Rect getStackActionButtonRect() {
295c0432c63de3def1f00f93aedee8050c30c2dc291Manu Cornet        Rect buttonRect = new Rect(mWindowRect);
296bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.right -= mPaddingLeftRight;
297bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.left += mPaddingLeftRight;
298bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
299bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        return buttonRect;
300bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
3018f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He
30221f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    public void updateTaskGridRect(int taskCount) {
30321f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        if (taskCount > 0) {
30421f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
30521f495f07b1b9ace99986a110d01a763847687b1Jiaquan He            mTaskGridRect.set(gridInfo.size);
30621f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        }
30721f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    }
30821f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
3098f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He    public Rect getTaskGridRect() {
3108f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He        return mTaskGridRect;
3118f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He    }
31221f495f07b1b9ace99986a110d01a763847687b1Jiaquan He
31321f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    public int getFocusFrameThickness() {
31421f495f07b1b9ace99986a110d01a763847687b1Jiaquan He        return mFocusedFrameThickness;
31521f495f07b1b9ace99986a110d01a763847687b1Jiaquan He    }
316bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet}
317