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}