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