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}