FreeformWorkspaceLayoutAlgorithm.java revision 1bcf3c4742da5a1d9c04c73efac5c2418142c262
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.content.Context; 20import android.graphics.RectF; 21import android.util.ArrayMap; 22 23import com.android.systemui.R; 24import com.android.systemui.recents.model.Task; 25 26import java.util.Collections; 27import java.util.List; 28 29/** 30 * The layout logic for the contents of the freeform workspace. 31 */ 32public class FreeformWorkspaceLayoutAlgorithm { 33 34 // Optimization, allows for quick lookup of task -> rect 35 private ArrayMap<Task.TaskKey, RectF> mTaskRectMap = new ArrayMap<>(); 36 37 private int mTaskPadding; 38 39 public FreeformWorkspaceLayoutAlgorithm(Context context) { 40 // This is applied to the edges of each task 41 mTaskPadding = context.getResources().getDimensionPixelSize( 42 R.dimen.recents_freeform_workspace_task_padding) / 2; 43 } 44 45 /** 46 * Updates the layout for each of the freeform workspace tasks. This is called after the stack 47 * layout is updated. 48 */ 49 public void update(List<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) { 50 Collections.reverse(freeformTasks); 51 mTaskRectMap.clear(); 52 53 int numFreeformTasks = stackLayout.mNumFreeformTasks; 54 if (!freeformTasks.isEmpty()) { 55 56 // Normalize the widths so that we can calculate the best layout below 57 int workspaceWidth = stackLayout.mFreeformRect.width(); 58 int workspaceHeight = stackLayout.mFreeformRect.height(); 59 float normalizedWorkspaceWidth = (float) workspaceWidth / workspaceHeight; 60 float normalizedWorkspaceHeight = 1f; 61 float[] normalizedTaskWidths = new float[numFreeformTasks]; 62 for (int i = 0; i < numFreeformTasks; i++) { 63 Task task = freeformTasks.get(i); 64 float rowTaskWidth; 65 if (task.bounds != null) { 66 rowTaskWidth = (float) task.bounds.width() / task.bounds.height(); 67 } else { 68 // If this is a stack task that was dragged into the freeform workspace, then 69 // the task will not yet have an associated bounds, so assume the full workspace 70 // width for the time being 71 rowTaskWidth = normalizedWorkspaceWidth; 72 } 73 // Bound the task width to the workspace width so that at the worst case, it will 74 // fit its own row 75 normalizedTaskWidths[i] = Math.min(rowTaskWidth, 76 normalizedWorkspaceWidth); 77 } 78 79 // Determine the scale to best fit each of the tasks in the workspace 80 float rowScale = 0.85f; 81 float rowWidth = 0f; 82 float maxRowWidth = 0f; 83 int rowCount = 1; 84 for (int i = 0; i < numFreeformTasks;) { 85 float width = normalizedTaskWidths[i] * rowScale; 86 if (rowWidth + width > normalizedWorkspaceWidth) { 87 // That is too long for this row, create new row 88 if ((rowCount + 1) * rowScale > normalizedWorkspaceHeight) { 89 // The new row is too high, so we need to try fitting again. Update the 90 // scale to be the smaller of the scale needed to fit the task in the 91 // previous row, or the scale needed to fit the new row 92 rowScale = Math.min(normalizedWorkspaceWidth / (rowWidth + width), 93 normalizedWorkspaceHeight / (rowCount + 1)); 94 rowCount = 1; 95 rowWidth = 0; 96 i = 0; 97 } else { 98 // The new row fits, so continue 99 rowWidth = width; 100 rowCount++; 101 i++; 102 } 103 } else { 104 // Task is OK in this row 105 rowWidth += width; 106 i++; 107 } 108 maxRowWidth = Math.max(rowWidth, maxRowWidth); 109 } 110 111 // Normalize each of the actual rects to that scale 112 float defaultRowLeft = ((1f - (maxRowWidth / normalizedWorkspaceWidth)) * 113 workspaceWidth) / 2f; 114 float rowLeft = defaultRowLeft; 115 float rowTop = ((1f - (rowScale * rowCount)) * workspaceHeight) / 2f; 116 float rowHeight = rowScale * workspaceHeight; 117 for (int i = 0; i < numFreeformTasks; i++) { 118 Task task = freeformTasks.get(i); 119 float width = rowHeight * normalizedTaskWidths[i]; 120 if (rowLeft + width > workspaceWidth) { 121 // This goes on the next line 122 rowTop += rowHeight; 123 rowLeft = defaultRowLeft; 124 } 125 RectF rect = new RectF(rowLeft, rowTop, rowLeft + width, rowTop + rowHeight); 126 rect.inset(mTaskPadding, mTaskPadding); 127 rowLeft += width; 128 mTaskRectMap.put(task.key, rect); 129 } 130 } 131 } 132 133 /** 134 * Returns whether the transform is available for the given task. 135 */ 136 public boolean isTransformAvailable(Task task, TaskStackLayoutAlgorithm stackLayout) { 137 if (stackLayout.mNumFreeformTasks == 0 || task == null) { 138 return false; 139 } 140 return mTaskRectMap.containsKey(task.key); 141 } 142 143 /** 144 * Returns the transform for the given task. Any rect returned will be offset by the actual 145 * transform for the freeform workspace. 146 */ 147 public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut, 148 TaskStackLayoutAlgorithm stackLayout) { 149 if (mTaskRectMap.containsKey(task.key)) { 150 final RectF ffRect = mTaskRectMap.get(task.key); 151 152 transformOut.scale = 1f; 153 transformOut.alpha = 1f; 154 transformOut.translationZ = stackLayout.mMaxTranslationZ; 155 transformOut.dimAlpha = 0f; 156 transformOut.rect.set(ffRect); 157 transformOut.rect.offset(stackLayout.mFreeformRect.left, stackLayout.mFreeformRect.top); 158 transformOut.visible = true; 159 transformOut.relativeTaskProgress = 0f; 160 return transformOut; 161 } 162 return null; 163 } 164} 165