TaskGridLayoutAlgorithm.java revision 8f34df423d9f65c692d9bcf59d224e0f938f78ac
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;
26bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.misc.Utilities;
27bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
28bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetimport com.android.systemui.recents.views.TaskViewTransform;
29bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
30bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornetpublic class TaskGridLayoutAlgorithm  {
31bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
32bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private final String TAG = "TaskGridLayoutAlgorithm";
33bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private final int MAX_LAYOUT_TASK_COUNT = 8;
34bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
35bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The horizontal padding around the whole recents view. */
36bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingLeftRight;
37bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The vertical padding around the whole recents view. */
38bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingTopBottom;
39bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The padding between task views. */
40bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mPaddingTaskView;
41bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
42bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mDisplayRect;
43bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mWindowRect;
44bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Point mScreenSize = new Point();
45bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
46bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mTaskGridRect;
47bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
48bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The height, in pixels, of each task view's title bar. */
49bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private int mTitleBarHeight;
50bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
51bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    /** The aspect ratio of each task thumbnail, without the title bar. */
52bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private float mAppAspectRatio;
53bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private Rect mSystemInsets = new Rect();
54bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
550a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    /**
560a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * When the amount of tasks is determined, the size and position of every task view can be
570a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * decided. Each instance of TaskGridRectInfo store the task view information for a certain
580a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * amount of tasks.
590a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     */
600a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    class TaskGridRectInfo {
610a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        Rect size;
620a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int[] xOffsets;
630a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int[] yOffsets;
640a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
650a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        public TaskGridRectInfo(int taskCount) {
660a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            size = new Rect();
670a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            xOffsets = new int[taskCount];
680a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            yOffsets = new int[taskCount];
690a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
700a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
710a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
720a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int tasksPerLine = layoutTaskCount < 2 ? 1 : (
730a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                layoutTaskCount < 5 ? 2 : (
740a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    layoutTaskCount < 7 ? 3 : 4));
750a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int lines = layoutTaskCount < 3 ? 1 : 2;
760a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
770a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int taskWidth, taskHeight;
780a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int maxTaskWidth = (mDisplayRect.width() - 2 * mPaddingLeftRight
790a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
800a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int maxTaskHeight = (mDisplayRect.height() - 2 * mPaddingTopBottom
810a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (lines - 1) * mPaddingTaskView) / lines;
820a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
830a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
840a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // Width bound.
850a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskWidth = maxTaskWidth;
860a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight);
870a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            } else {
880a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // Height bound.
890a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskHeight = maxTaskHeight;
900a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio);
910a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            }
920a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            size.set(0, 0, taskWidth, taskHeight);
930a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
940a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int emptySpaceX = mDisplayRect.width() - 2 * mPaddingLeftRight
950a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
960a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            int emptySpaceY = mDisplayRect.height() - 2 * mPaddingTopBottom
970a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
980a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) {
990a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                // We also need to invert the index in order to display the most recent tasks first.
1000a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int taskLayoutIndex = taskCount - taskIndex - 1;
1010a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1020a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int xIndex = taskLayoutIndex % tasksPerLine;
1030a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                int yIndex = taskLayoutIndex / tasksPerLine;
1040a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                xOffsets[taskIndex] =
1050a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
1060a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                yOffsets[taskIndex] =
1070a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He                    emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
1080a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            }
1090a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        }
1100a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    }
1110a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1120a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    /**
1130a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * We can find task view sizes and positions from mTaskGridRectInfoList[k - 1] when there
1140a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     * are k tasks.
1150a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He     */
1160a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He    TaskGridRectInfo[] mTaskGridRectInfoList;
1170a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
118bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public TaskGridLayoutAlgorithm(Context context) {
119bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        reloadOnConfigurationChange(context);
120bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
121bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
122bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void reloadOnConfigurationChange(Context context) {
123bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Resources res = context.getResources();
124bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mPaddingLeftRight = res.getDimensionPixelSize(R.dimen.recents_grid_padding_left_right);
125bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mPaddingTopBottom = res.getDimensionPixelSize(R.dimen.recents_grid_padding_top_bottom);
126bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
127bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
128bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mTaskGridRect = new Rect();
129bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
130bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
131bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        WindowManager windowManager = (WindowManager) context
132bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet                .getSystemService(Context.WINDOW_SERVICE);
133bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        windowManager.getDefaultDisplay().getRealSize(mScreenSize);
134bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
135bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        updateAppAspectRatio();
136bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
137bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
138bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public TaskViewTransform getTransform(int taskIndex, int taskCount,
139bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
140bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
1410a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
1420a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        mTaskGridRect.set(gridInfo.size);
143bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
1440a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int x = gridInfo.xOffsets[taskIndex];
1450a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int y = gridInfo.yOffsets[taskIndex];
146bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float z = stackLayout.mMaxTranslationZ;
147bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
148bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float dimAlpha = 0f;
149bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        float viewOutlineAlpha = 0f;
1500a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // We also need to invert the index in order to display the most recent tasks first.
1510a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        int taskLayoutIndex = taskCount - taskIndex - 1;
152bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        boolean isTaskViewVisible = (taskLayoutIndex < MAX_LAYOUT_TASK_COUNT);
153bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
154bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        // Fill out the transform
155bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.scale = 1f;
156bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.alpha = isTaskViewVisible ? 1f : 0f;
157bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.translationZ = z;
158bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.dimAlpha = dimAlpha;
159bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.viewOutlineAlpha = viewOutlineAlpha;
160bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.rect.set(mTaskGridRect);
161bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.rect.offset(x, y);
162bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
163bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        // We only show the 8 most recent tasks.
164bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        transformOut.visible = isTaskViewVisible;
165bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        return transformOut;
166bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
167bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
168bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void initialize(Rect displayRect, Rect windowRect) {
169bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mDisplayRect = displayRect;
170bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mWindowRect = windowRect;
1710a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He
1720a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // Pre-calculate the positions and offsets of task views so that we can reuse them directly
1730a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        // in the future.
1740a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        mTaskGridRectInfoList = new TaskGridRectInfo[MAX_LAYOUT_TASK_COUNT];
1750a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        for (int i = 0; i < MAX_LAYOUT_TASK_COUNT; i++) {
1760a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He            mTaskGridRectInfoList[i] = new TaskGridRectInfo(i + 1);
1770a5e2d6b93af0638f9dccb3bad43266b8fc261adJiaquan He        }
178bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
179bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
180bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public void setSystemInsets(Rect systemInsets) {
181bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mSystemInsets = systemInsets;
182bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        updateAppAspectRatio();
183bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
184bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
185bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    private void updateAppAspectRatio() {
186bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
187bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
188bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        mAppAspectRatio = (float) usableWidth / (float) usableHeight;
189bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
190bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet
191bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    public Rect getStackActionButtonRect() {
192bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        Rect buttonRect = new Rect(mDisplayRect);
193bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.right -= mPaddingLeftRight;
194bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.left += mPaddingLeftRight;
195bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
196bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet        return buttonRect;
197bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet    }
1988f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He
1998f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He    public Rect getTaskGridRect() {
2008f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He        return mTaskGridRect;
2018f34df423d9f65c692d9bcf59d224e0f938f78acJiaquan He    }
202bf8e290376b8ee6d04c330b22f5275e82103def3Manu Cornet}
203