TaskStackViewLayoutAlgorithm.java revision 480dd72daf927283997bdb4060091299add66832
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.RecentsConfiguration;
21import com.android.systemui.recents.misc.Utilities;
22import com.android.systemui.recents.model.Task;
23
24import java.util.ArrayList;
25import java.util.HashMap;
26
27/* The layout logic for a TaskStackView */
28public class TaskStackViewLayoutAlgorithm {
29
30    // These are all going to change
31    static final float StackOverlapPct = 0.65f; // The overlap height relative to the task height
32    static final float StackPeekHeightPct = 0.075f; // The height of the peek space relative to the stack height
33    static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
34    static final int StackPeekNumCards = 3; // The number of cards we see in the peek space
35
36    RecentsConfiguration mConfig;
37
38    // The various rects that define the stack view
39    Rect mViewRect = new Rect();
40    Rect mStackRect = new Rect();
41    Rect mStackRectSansPeek = new Rect();
42    Rect mTaskRect = new Rect();
43
44    // The min/max scroll
45    int mMinScroll;
46    int mMaxScroll;
47
48    HashMap<Task.TaskKey, Integer> mTaskOffsetMap = new HashMap<Task.TaskKey, Integer>();
49
50    public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) {
51        mConfig = config;
52    }
53
54    /** Computes the stack and task rects */
55    public void computeRects(ArrayList<Task> tasks, int windowWidth, int windowHeight,
56                             Rect taskStackBounds) {
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        mViewRect.set(0, 0, windowWidth, windowHeight);
63        mStackRect.set(taskStackBounds);
64
65        int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
66        int heightPadding = mConfig.taskStackTopPaddingPx;
67        mStackRect.inset(widthPadding, heightPadding);
68        mStackRectSansPeek.set(mStackRect);
69        mStackRectSansPeek.top += StackPeekHeightPct * windowHeight;
70
71        // Compute the task rect
72        int size = mStackRect.width();
73        int left = mStackRect.left + (mStackRect.width() - size) / 2;
74        mTaskRect.set(left, mStackRectSansPeek.top,
75                left + size, mStackRectSansPeek.top + size);
76
77        // Update the task offsets once the size changes
78        updateTaskOffsets(tasks);
79    }
80
81    void computeMinMaxScroll(ArrayList<Task> tasks) {
82        // Compute the min and max scroll values
83        int numTasks = Math.max(1, tasks.size());
84        int taskHeight = mTaskRect.height();
85        int stackHeight = mStackRectSansPeek.height();
86
87        if (numTasks <= 1) {
88            // If there is only one task, then center the task in the stack rect (sans peek)
89            mMinScroll = mMaxScroll = -(stackHeight -
90                    (taskHeight + mConfig.taskViewLockToAppButtonHeight)) / 2;
91        } else {
92            int maxScrollHeight = getStackScrollForTaskIndex(tasks.get(tasks.size() - 1))
93                    + taskHeight + mConfig.taskViewLockToAppButtonHeight;
94            mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
95            mMaxScroll = maxScrollHeight - stackHeight;
96        }
97    }
98
99    /** Update/get the transform */
100    public TaskViewTransform getStackTransform(Task task, int stackScroll, TaskViewTransform transformOut) {
101        // Return early if we have an invalid index
102        if (task == null) {
103            transformOut.reset();
104            return transformOut;
105        }
106        return getStackTransform(getStackScrollForTaskIndex(task), stackScroll, transformOut);
107    }
108
109    /** Update/get the transform */
110    public TaskViewTransform getStackTransform(int taskStackScroll, int stackScroll, TaskViewTransform transformOut) {
111        // Map the items to an continuous position relative to the specified scroll
112        int numPeekCards = StackPeekNumCards;
113        float overlapHeight = StackOverlapPct * mTaskRect.height();
114        float peekHeight = StackPeekHeightPct * mStackRect.height();
115        float t = (taskStackScroll - stackScroll) / overlapHeight;
116        float boundedT = Math.max(t, -(numPeekCards + 1));
117
118        // Set the scale relative to its position
119        int numFrontScaledCards = 3;
120        float minScale = StackPeekMinScale;
121        float scaleRange = 1f - minScale;
122        float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
123        float scale = Math.max(minScale, Math.min(1f, minScale +
124                ((boundedT + (numPeekCards + 1)) * scaleInc)));
125        float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
126        // Account for the bar offsets being scaled?
127        float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight;
128        transformOut.scale = scale;
129
130        // Set the y translation
131        if (boundedT < 0f) {
132            transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
133                    numPeekCards) * peekHeight - scaleYOffset);
134        } else {
135            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
136        }
137
138        // Set the z translation
139        int minZ = mConfig.taskViewTranslationZMinPx;
140        int incZ = mConfig.taskViewTranslationZIncrementPx;
141        transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
142
143        // Set the alphas
144        // transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
145        transformOut.dismissAlpha = 1f;
146
147        // Update the rect and visibility
148        transformOut.rect.set(mTaskRect);
149        if (t < -(numPeekCards + 1)) {
150            transformOut.visible = false;
151        } else {
152            transformOut.rect.offset(0, transformOut.translationY);
153            Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
154            transformOut.visible = Rect.intersects(mViewRect, transformOut.rect);
155        }
156        transformOut.t = t;
157        return transformOut;
158    }
159
160    /**
161     * Returns the overlap between one task and the next.
162     */
163    float getTaskOverlapHeight() {
164        return StackOverlapPct * mTaskRect.height();
165    }
166
167    /**
168     * Returns the scroll to such that the task transform at that index will have t=0. (If the scroll
169     * is not bounded)
170     */
171    int getStackScrollForTaskIndex(Task t) {
172        return mTaskOffsetMap.get(t.key);
173    }
174
175    /**
176     * Returns the scroll to such that the task transform at that task + index will have t=0.
177     * (If the scroll is not bounded)
178     */
179    int getStackScrollForTaskIndex(Task t, int relativeIndexOffset) {
180        return mTaskOffsetMap.get(t.key) + (int) (relativeIndexOffset * getTaskOverlapHeight());
181    }
182
183    /**
184     * Updates the cache of tasks to offsets.
185     */
186    void updateTaskOffsets(ArrayList<Task> tasks) {
187        mTaskOffsetMap.clear();
188        int offset = 0;
189        int taskCount = tasks.size();
190        for (int i = 0; i < taskCount; i++) {
191            Task t = tasks.get(i);
192            mTaskOffsetMap.put(t.key, offset);
193            if (t.group.isFrontMostTask(t)) {
194                offset += getTaskOverlapHeight();
195            } else {
196                offset += mConfig.taskBarHeight;
197            }
198        }
199    }
200
201}