| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.systemui.recents.views; |
| |
| import android.graphics.Rect; |
| import com.android.systemui.recents.Constants; |
| import com.android.systemui.recents.RecentsConfiguration; |
| import com.android.systemui.recents.misc.Utilities; |
| import com.android.systemui.recents.model.Task; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| |
| /* The layout logic for a TaskStackView */ |
| public class TaskStackViewLayoutAlgorithm { |
| |
| // These are all going to change |
| static final float StackOverlapPct = 0.65f; // The overlap height relative to the task height |
| static final float StackPeekHeightPct = 0.1f; // The height of the peek space relative to the stack height |
| static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area |
| static final int StackPeekNumCards = 3; // The number of cards we see in the peek space |
| |
| RecentsConfiguration mConfig; |
| |
| // The various rects that define the stack view |
| Rect mRect = new Rect(); |
| Rect mStackRect = new Rect(); |
| Rect mStackRectSansPeek = new Rect(); |
| Rect mTaskRect = new Rect(); |
| |
| // The min/max scroll |
| int mMinScroll; |
| int mMaxScroll; |
| |
| HashMap<Task.TaskKey, Integer> mTaskOffsetMap = new HashMap<Task.TaskKey, Integer>(); |
| |
| public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) { |
| mConfig = config; |
| } |
| |
| /** Computes the stack and task rects */ |
| public void computeRects(ArrayList<Task> tasks, int width, int height, int insetLeft, int insetBottom) { |
| // Note: We let the stack view be the full height because we want the cards to go under the |
| // navigation bar if possible. However, the stack rects which we use to calculate |
| // max scroll, etc. need to take the nav bar into account |
| |
| // Compute the stack rects |
| mRect.set(0, 0, width, height); |
| mStackRect.set(mRect); |
| mStackRect.left += insetLeft; |
| mStackRect.bottom -= insetBottom; |
| |
| int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width()); |
| int heightPadding = mConfig.taskStackTopPaddingPx; |
| if (Constants.DebugFlags.App.EnableSearchLayout) { |
| mStackRect.top += heightPadding; |
| mStackRect.left += widthPadding; |
| mStackRect.right -= widthPadding; |
| mStackRect.bottom -= heightPadding; |
| } else { |
| mStackRect.inset(widthPadding, heightPadding); |
| } |
| mStackRectSansPeek.set(mStackRect); |
| mStackRectSansPeek.top += StackPeekHeightPct * mStackRect.height(); |
| |
| // Compute the task rect |
| int size = mStackRect.width(); |
| int left = mStackRect.left + (mStackRect.width() - size) / 2; |
| mTaskRect.set(left, mStackRectSansPeek.top, |
| left + size, mStackRectSansPeek.top + size); |
| |
| // Update the task offsets once the size changes |
| updateTaskOffsets(tasks); |
| } |
| |
| void computeMinMaxScroll(ArrayList<Task> tasks) { |
| // Compute the min and max scroll values |
| int numTasks = Math.max(1, tasks.size()); |
| int taskHeight = mTaskRect.height(); |
| int stackHeight = mStackRectSansPeek.height(); |
| |
| if (numTasks <= 1) { |
| // If there is only one task, then center the task in the stack rect (sans peek) |
| mMinScroll = mMaxScroll = -(stackHeight - |
| (taskHeight + mConfig.taskViewLockToAppButtonHeight)) / 2; |
| } else { |
| int maxScrollHeight = getStackScrollForTaskIndex(tasks.get(tasks.size() - 1)) |
| + taskHeight + mConfig.taskViewLockToAppButtonHeight; |
| mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight; |
| mMaxScroll = maxScrollHeight - stackHeight; |
| } |
| } |
| |
| /** Update/get the transform */ |
| public TaskViewTransform getStackTransform(Task task, int stackScroll, TaskViewTransform transformOut) { |
| // Return early if we have an invalid index |
| if (task == null) { |
| transformOut.reset(); |
| return transformOut; |
| } |
| |
| // Map the items to an continuous position relative to the specified scroll |
| int numPeekCards = StackPeekNumCards; |
| float overlapHeight = StackOverlapPct * mTaskRect.height(); |
| float peekHeight = StackPeekHeightPct * mStackRect.height(); |
| float t = (getStackScrollForTaskIndex(task) - stackScroll) / overlapHeight; |
| float boundedT = Math.max(t, -(numPeekCards + 1)); |
| |
| // Set the scale relative to its position |
| int numFrontScaledCards = 3; |
| float minScale = StackPeekMinScale; |
| float scaleRange = 1f - minScale; |
| float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards); |
| float scale = Math.max(minScale, Math.min(1f, minScale + |
| ((boundedT + (numPeekCards + 1)) * scaleInc))); |
| float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2; |
| // Account for the bar offsets being scaled? |
| float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight; |
| transformOut.scale = scale; |
| |
| // Set the y translation |
| if (boundedT < 0f) { |
| transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) / |
| numPeekCards) * peekHeight - scaleYOffset); |
| } else { |
| transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset); |
| } |
| |
| // Set the z translation |
| int minZ = mConfig.taskViewTranslationZMinPx; |
| int incZ = mConfig.taskViewTranslationZIncrementPx; |
| transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ)); |
| |
| // Set the alphas |
| transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f; |
| |
| // Update the rect and visibility |
| transformOut.rect.set(mTaskRect); |
| if (t < -(numPeekCards + 1)) { |
| transformOut.visible = false; |
| } else { |
| transformOut.rect.offset(0, transformOut.translationY); |
| Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale); |
| transformOut.visible = Rect.intersects(mRect, transformOut.rect); |
| } |
| transformOut.t = t; |
| return transformOut; |
| } |
| |
| /** |
| * Returns the overlap between one task and the next. |
| */ |
| float getTaskOverlapHeight() { |
| return StackOverlapPct * mTaskRect.height(); |
| } |
| |
| /** |
| * Returns the scroll to such that the task transform at that index will have t=0. (If the scroll |
| * is not bounded) |
| */ |
| int getStackScrollForTaskIndex(Task t) { |
| return mTaskOffsetMap.get(t.key); |
| } |
| |
| /** |
| * Updates the cache of tasks to offsets. |
| */ |
| void updateTaskOffsets(ArrayList<Task> tasks) { |
| mTaskOffsetMap.clear(); |
| int offset = 0; |
| int taskCount = tasks.size(); |
| for (int i = 0; i < taskCount; i++) { |
| Task t = tasks.get(i); |
| mTaskOffsetMap.put(t.key, offset); |
| if (t.group.isFrontMostTask(t)) { |
| offset += getTaskOverlapHeight(); |
| } else { |
| offset += mConfig.taskBarHeight; |
| } |
| } |
| } |
| |
| } |