TaskStackViewLayoutAlgorithm.java revision 93748a11cba1b44edbc2e888c997533461355594
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 151 // Update the rect and visibility 152 transformOut.rect.set(mTaskRect); 153 if (t < -(numPeekCards + 1)) { 154 transformOut.visible = false; 155 } else { 156 transformOut.rect.offset(0, transformOut.translationY); 157 Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale); 158 transformOut.visible = Rect.intersects(mRect, transformOut.rect); 159 } 160 transformOut.t = t; 161 return transformOut; 162 } 163 164 /** 165 * Returns the overlap between one task and the next. 166 */ 167 float getTaskOverlapHeight() { 168 return StackOverlapPct * mTaskRect.height(); 169 } 170 171 /** 172 * Returns the scroll to such that the task transform at that index will have t=0. (If the scroll 173 * is not bounded) 174 */ 175 int getStackScrollForTaskIndex(Task t) { 176 return mTaskOffsetMap.get(t.key); 177 } 178 179 /** 180 * Updates the cache of tasks to offsets. 181 */ 182 void updateTaskOffsets(ArrayList<Task> tasks) { 183 mTaskOffsetMap.clear(); 184 int offset = 0; 185 int taskCount = tasks.size(); 186 for (int i = 0; i < taskCount; i++) { 187 Task t = tasks.get(i); 188 mTaskOffsetMap.put(t.key, offset); 189 if (t.group.isFrontMostTask(t)) { 190 offset += getTaskOverlapHeight(); 191 } else { 192 offset += mConfig.taskBarHeight; 193 } 194 } 195 } 196 197}