TaskStackViewLayoutAlgorithm.java revision a433fa9c17772f563163ff7db177d091d6aebd5b
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 - taskHeight) / 2;
99        } else {
100            int maxScrollHeight = taskHeight + getStackScrollForTaskIndex(tasks.get(tasks.size() - 1));
101            mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
102            mMaxScroll = maxScrollHeight - stackHeight;
103        }
104    }
105
106    /** Update/get the transform */
107    public TaskViewTransform getStackTransform(Task task, int stackScroll, TaskViewTransform transformOut) {
108        // Return early if we have an invalid index
109        if (task == null) {
110            transformOut.reset();
111            return transformOut;
112        }
113
114        // Map the items to an continuous position relative to the specified scroll
115        int numPeekCards = StackPeekNumCards;
116        float overlapHeight = StackOverlapPct * mTaskRect.height();
117        float peekHeight = StackPeekHeightPct * mStackRect.height();
118        float t = (getStackScrollForTaskIndex(task) - stackScroll) / overlapHeight;
119        float boundedT = Math.max(t, -(numPeekCards + 1));
120
121        // Set the scale relative to its position
122        int numFrontScaledCards = 3;
123        float minScale = StackPeekMinScale;
124        float scaleRange = 1f - minScale;
125        float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
126        float scale = Math.max(minScale, Math.min(1f, minScale +
127            ((boundedT + (numPeekCards + 1)) * scaleInc)));
128        float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
129        // Account for the bar offsets being scaled?
130        float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight;
131        transformOut.scale = scale;
132
133        // Set the y translation
134        if (boundedT < 0f) {
135            transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
136                    numPeekCards) * peekHeight - scaleYOffset);
137        } else {
138            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset - scaleBarYOffset);
139        }
140
141        // Set the z translation
142        int minZ = mConfig.taskViewTranslationZMinPx;
143        int incZ = mConfig.taskViewTranslationZIncrementPx;
144        transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
145
146        // Set the alphas
147        transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
148
149        // Update the rect and visibility
150        transformOut.rect.set(mTaskRect);
151        if (t < -(numPeekCards + 1)) {
152            transformOut.visible = false;
153        } else {
154            transformOut.rect.offset(0, transformOut.translationY);
155            Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
156            transformOut.visible = Rect.intersects(mRect, transformOut.rect);
157        }
158        transformOut.t = t;
159        return transformOut;
160    }
161
162    /**
163     * Returns the overlap between one task and the next.
164     */
165    float getTaskOverlapHeight() {
166        return StackOverlapPct * mTaskRect.height();
167    }
168
169    /**
170     * Returns the scroll to such that the task transform at that index will have t=0. (If the scroll
171     * is not bounded)
172     */
173    int getStackScrollForTaskIndex(Task t) {
174        return mTaskOffsetMap.get(t.key);
175    }
176
177    /**
178     * Updates the cache of tasks to offsets.
179     */
180    void updateTaskOffsets(ArrayList<Task> tasks) {
181        mTaskOffsetMap.clear();
182        int offset = 0;
183        int taskCount = tasks.size();
184        for (int i = 0; i < taskCount; i++) {
185            Task t = tasks.get(i);
186            mTaskOffsetMap.put(t.key, offset);
187            if (t.group.isFrontMostTask(t)) {
188                offset += getTaskOverlapHeight();
189            } else {
190                offset += mConfig.taskBarHeight;
191            }
192        }
193    }
194
195}