TaskStackViewLayoutAlgorithm.java revision 82c8c5e9872b984ba6f43b01da0c7731fd5bf762
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents.views;
18
19import android.graphics.Rect;
20import com.android.systemui.recents.Constants;
21import com.android.systemui.recents.RecentsConfiguration;
22import com.android.systemui.recents.misc.Utilities;
23import com.android.systemui.recents.model.Task;
24
25import java.util.ArrayList;
26import java.util.HashMap;
27
28/* The layout logic for a TaskStackView */
29public class TaskStackViewLayoutAlgorithm {
30
31    // These are all going to change
32    static final float StackOverlapPct = 0.65f; // The overlap height relative to the task height
33    static final float StackPeekHeightPct = 0.1f; // The height of the peek space relative to the stack height
34    static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
35    static final int StackPeekNumCards = 3; // The number of cards we see in the peek space
36
37    RecentsConfiguration mConfig;
38
39    // The various rects that define the stack view
40    Rect mRect = new Rect();
41    Rect mStackRect = new Rect();
42    Rect mStackRectSansPeek = new Rect();
43    Rect mTaskRect = new Rect();
44
45    // The min/max scroll
46    int mMinScroll;
47    int mMaxScroll;
48
49    HashMap<Task.TaskKey, Integer> mTaskOffsetMap = new HashMap<Task.TaskKey, Integer>();
50
51    public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) {
52        mConfig = config;
53    }
54
55    /** Computes the stack and task rects */
56    public void computeRects(ArrayList<Task> tasks, int width, int height, int insetLeft, int insetBottom) {
57        // Note: We let the stack view be the full height because we want the cards to go under the
58        //       navigation bar if possible.  However, the stack rects which we use to calculate
59        //       max scroll, etc. need to take the nav bar into account
60
61        // Compute the stack rects
62        mRect.set(0, 0, width, height);
63        mStackRect.set(mRect);
64        mStackRect.left += insetLeft;
65        mStackRect.bottom -= insetBottom;
66
67        int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
68        int heightPadding = mConfig.taskStackTopPaddingPx;
69        if (Constants.DebugFlags.App.EnableSearchLayout) {
70            mStackRect.top += heightPadding;
71            mStackRect.left += widthPadding;
72            mStackRect.right -= widthPadding;
73            mStackRect.bottom -= heightPadding;
74        } else {
75            mStackRect.inset(widthPadding, heightPadding);
76        }
77        mStackRectSansPeek.set(mStackRect);
78        mStackRectSansPeek.top += StackPeekHeightPct * mStackRect.height();
79
80        // Compute the task rect
81        int size = mStackRect.width();
82        int left = mStackRect.left + (mStackRect.width() - size) / 2;
83        mTaskRect.set(left, mStackRectSansPeek.top,
84                left + size, mStackRectSansPeek.top + size);
85
86        // Update the task offsets once the size changes
87        updateTaskOffsets(tasks);
88    }
89
90    void computeMinMaxScroll(ArrayList<Task> tasks) {
91        // Compute the min and max scroll values
92        int numTasks = Math.max(1, tasks.size());
93        int taskHeight = mTaskRect.height();
94        int stackHeight = mStackRectSansPeek.height();
95
96        if (numTasks <= 1) {
97            // If there is only one task, then center the task in the stack rect (sans peek)
98            mMinScroll = mMaxScroll = -(stackHeight -
99                    (taskHeight + mConfig.taskViewLockToAppButtonHeight)) / 2;
100        } else {
101            int maxScrollHeight = getStackScrollForTaskIndex(tasks.get(tasks.size() - 1))
102                    + taskHeight + mConfig.taskViewLockToAppButtonHeight;
103            mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
104            mMaxScroll = maxScrollHeight - stackHeight;
105        }
106    }
107
108    /** Update/get the transform */
109    public TaskViewTransform getStackTransform(Task task, int stackScroll, TaskViewTransform transformOut) {
110        // Return early if we have an invalid index
111        if (task == null) {
112            transformOut.reset();
113            return transformOut;
114        }
115
116        // Map the items to an continuous position relative to the specified scroll
117        int numPeekCards = StackPeekNumCards;
118        float overlapHeight = StackOverlapPct * mTaskRect.height();
119        float peekHeight = StackPeekHeightPct * mStackRect.height();
120        float t = (getStackScrollForTaskIndex(task) - stackScroll) / overlapHeight;
121        float boundedT = Math.max(t, -(numPeekCards + 1));
122
123        // Set the scale relative to its position
124        int numFrontScaledCards = 3;
125        float minScale = StackPeekMinScale;
126        float scaleRange = 1f - minScale;
127        float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
128        float scale = Math.max(minScale, Math.min(1f, minScale +
129            ((boundedT + (numPeekCards + 1)) * scaleInc)));
130        float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
131        // Account for the bar offsets being scaled?
132        float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight;
133        transformOut.scale = scale;
134
135        // Set the y translation
136        if (boundedT < 0f) {
137            transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
138                    numPeekCards) * peekHeight - scaleYOffset);
139        } else {
140            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
141        }
142
143        // Set the z translation
144        int minZ = mConfig.taskViewTranslationZMinPx;
145        int incZ = mConfig.taskViewTranslationZIncrementPx;
146        transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
147
148        // Set the alphas
149        // transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
150        transformOut.dismissAlpha = 1f;
151
152        // Update the rect and visibility
153        transformOut.rect.set(mTaskRect);
154        if (t < -(numPeekCards + 1)) {
155            transformOut.visible = false;
156        } else {
157            transformOut.rect.offset(0, transformOut.translationY);
158            Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
159            transformOut.visible = Rect.intersects(mRect, transformOut.rect);
160        }
161        transformOut.t = t;
162        return transformOut;
163    }
164
165    /**
166     * Returns the overlap between one task and the next.
167     */
168    float getTaskOverlapHeight() {
169        return StackOverlapPct * mTaskRect.height();
170    }
171
172    /**
173     * Returns the scroll to such that the task transform at that index will have t=0. (If the scroll
174     * is not bounded)
175     */
176    int getStackScrollForTaskIndex(Task t) {
177        return mTaskOffsetMap.get(t.key);
178    }
179
180    /**
181     * Updates the cache of tasks to offsets.
182     */
183    void updateTaskOffsets(ArrayList<Task> tasks) {
184        mTaskOffsetMap.clear();
185        int offset = 0;
186        int taskCount = tasks.size();
187        for (int i = 0; i < taskCount; i++) {
188            Task t = tasks.get(i);
189            mTaskOffsetMap.put(t.key, offset);
190            if (t.group.isFrontMostTask(t)) {
191                offset += getTaskOverlapHeight();
192            } else {
193                offset += mConfig.taskBarHeight;
194            }
195        }
196    }
197
198}