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