Updating freeform layout to be static at the top of recents.
Change-Id: I5118d03c115080e05447d325097419b9a1c6f8b4
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
index ea6821f..515c3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
@@ -189,6 +189,7 @@
public float computePOffsetForScaledHeight(int height, Rect bounds) {
int top = bounds.top;
int bottom = bounds.bottom;
+ height = Math.min(height, bottom - top);
if (bounds.height() == 0) {
return 0;
@@ -231,6 +232,7 @@
public float computePOffsetForHeight(int height, Rect bounds) {
int top = bounds.top;
int bottom = bounds.bottom;
+ height = Math.min(height, bottom - top);
if (bounds.height() == 0) {
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index bdd3440..2fe5e98 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -235,6 +235,15 @@
return null;
}
+ /**
+ * Returns whether this device has freeform workspaces.
+ */
+ public boolean hasFreeformWorkspaceSupport() {
+ if (mPm == null) return false;
+
+ return mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+ }
+
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
MutableBoolean isHomeTopMost) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index ce993c5..a97a2a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -17,10 +17,11 @@
package com.android.systemui.recents.views;
import android.util.Log;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
-import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* The layout logic for the contents of the freeform workspace.
@@ -33,6 +34,7 @@
// The number of cells in the freeform workspace
private int mFreeformCellXCount;
private int mFreeformCellYCount;
+
// The width and height of the cells in the freeform workspace
private int mFreeformCellWidth;
private int mFreeformCellHeight;
@@ -44,22 +46,26 @@
* Updates the layout for each of the freeform workspace tasks. This is called after the stack
* layout is updated.
*/
- public void update(ArrayList<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) {
+ public void update(List<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) {
+ mTaskIndexMap.clear();
+
int numFreeformTasks = stackLayout.mNumFreeformTasks;
if (!freeformTasks.isEmpty()) {
// Calculate the cell width/height depending on the number of freeform tasks
mFreeformCellXCount = Math.max(2, (int) Math.ceil(Math.sqrt(numFreeformTasks)));
- mFreeformCellYCount = Math.max(2, (int) Math.ceil((float) numFreeformTasks / mFreeformCellXCount));
- mFreeformCellWidth = stackLayout.mFreeformRect.width() / mFreeformCellXCount;
+ mFreeformCellYCount = Math.max(2, (int) Math.ceil((float) numFreeformTasks /
+ mFreeformCellXCount));
// For now, make the cells square
+ mFreeformCellWidth = Math.min(stackLayout.mFreeformRect.width() / mFreeformCellXCount,
+ stackLayout.mFreeformRect.height() / mFreeformCellYCount);
mFreeformCellHeight = mFreeformCellWidth;
// Put each of the tasks in the progress map at a fixed index (does not need to actually
// map to a scroll position, just by index)
int taskCount = freeformTasks.size();
- for (int i = 0; i < taskCount; i++) {
+ for (int i = taskCount - 1; i >= 0; i--) {
Task task = freeformTasks.get(i);
- mTaskIndexMap.put(task.key, i);
+ mTaskIndexMap.put(task.key, taskCount - i - 1);
}
if (DEBUG) {
@@ -74,24 +80,23 @@
/**
* Returns whether the transform is available for the given task.
*/
- public boolean isTransformAvailable(Task task, float stackScroll,
- TaskStackLayoutAlgorithm stackLayout) {
- if (stackLayout.mNumFreeformTasks == 0 || task == null ||
- !mTaskIndexMap.containsKey(task.key)) {
+ public boolean isTransformAvailable(Task task, TaskStackLayoutAlgorithm stackLayout) {
+ if (stackLayout.mNumFreeformTasks == 0 || task == null) {
return false;
}
- return stackScroll > stackLayout.mStackEndScrollP;
+ return mTaskIndexMap.containsKey(task.key);
}
/**
* Returns the transform for the given task. Any rect returned will be offset by the actual
* transform for the freeform workspace.
*/
- public TaskViewTransform getTransform(Task task, float stackScroll,
- TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
- if (Float.compare(stackScroll, stackLayout.mStackEndScrollP) > 0) {
+ public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut,
+ TaskStackLayoutAlgorithm stackLayout) {
+ if (mTaskIndexMap.containsKey(task.key)) {
// This is a freeform task, so lay it out in the freeform workspace
int taskIndex = mTaskIndexMap.get(task.key);
+ int topOffset = (stackLayout.mFreeformRect.top - stackLayout.mTaskRect.top);
int x = taskIndex % mFreeformCellXCount;
int y = taskIndex / mFreeformCellXCount;
float scale = (float) mFreeformCellWidth / stackLayout.mTaskRect.width();
@@ -99,8 +104,13 @@
int scaleYOffset = (int) (((1f - scale) * stackLayout.mTaskRect.height()) / 2);
transformOut.scale = scale * 0.9f;
transformOut.translationX = x * mFreeformCellWidth - scaleXOffset;
- transformOut.translationY = y * mFreeformCellHeight - scaleYOffset;
+ transformOut.translationY = topOffset + y * mFreeformCellHeight - scaleYOffset;
+ transformOut.translationZ = stackLayout.mMaxTranslationZ;
+ transformOut.rect.set(stackLayout.mTaskRect);
+ transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = true;
+ transformOut.p = 0;
return transformOut;
}
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 45d626e..a0713d7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -24,12 +24,14 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.ParametricCurve;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedList;
/**
@@ -44,14 +46,8 @@
private static final float STACK_PEEK_MIN_SCALE = 0.85f;
// The scale of the last task
private static final float SINGLE_TASK_SCALE = 0.95f;
- // The percentage of the height of the stack that we want to show the last task at
- private static final float VISIBLE_LAST_TASK_HEIGHT_PCT = 0.45f;
// The percentage of height of task to show between tasks
private static final float VISIBLE_TASK_HEIGHT_BETWEEN_TASKS = 0.5f;
- // The percentage between the maxStackScroll and the maxScroll where a given scroll will still
- // snap back to the maxStackScroll instead of to the maxScroll (which shows the freeform
- // workspace)
- private static final float SNAP_TO_MAX_STACK_SCROLL_FACTOR = 0.3f;
// A report of the visibility state of the stack
public class VisibilityReport {
@@ -67,14 +63,36 @@
Context mContext;
- // This is the view bounds inset exactly by the search bar, but without the bottom inset
- // see RecentsConfiguration.getTaskStackBounds()
- public Rect mStackRect = new Rect();
- // This is the task view bounds for layout (untransformed), the rect is top-aligned to the top
- // of the stack rect
+ /*
+ +-------------------+
+ | SEARCH |
+ +-------------------+
+ |+-----------------+|
+ || FREEFORM ||
+ || ||
+ || ||
+ |+-----------------+|
+ | +-----------+ |
+ | +---------------+ |
+ | | | |
+ |+-----------------+|
+ || STACK ||
+ +-------------------+
+ */
+
+ // The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
public Rect mTaskRect = new Rect();
- // The bounds of the freeform workspace, the rect is top-aligned to the top of the stack rect
+ // The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
public Rect mFreeformRect = new Rect();
+ // The freeform stack bounds, inset from the top by the search bar and freeform workspace, and
+ // runs to the bottom of the screen
+ private Rect mFreeformStackRect = new Rect();
+ // The stack bounds, inset from the top by the search bar, and runs to
+ // the bottom of the screen
+ private Rect mStackRect = new Rect();
+ // The current stack rect, can either by mFreeformStackRect or mStackRect depending on whether
+ // there is a freeform workspace
+ public Rect mCurrentStackRect;
// This is the current system insets
public Rect mSystemInsets = new Rect();
@@ -83,13 +101,6 @@
// The largest scroll progress, at this value, the front most task will be visible above the
// navigation bar
float mMaxScrollP;
- // The scroll progress at which bottom of the first task of the stack is aligned with the bottom
- // of the stack
- float mStackEndScrollP;
- // The scroll progress that we actually want to scroll the user to when they want to go to the
- // end of the stack (it accounts for the nav bar, so that the bottom of the task is offset from
- // the bottom of the stack)
- float mPreferredStackEndScrollP;
// The initial progress that the scroller is set when you first enter recents
float mInitialScrollP;
// The task progress for the front-most task in the stack
@@ -109,13 +120,6 @@
// The relative progress to ensure that the offset from the bottom of the stack to the bottom
// of the task is respected
float mStackBottomPOffset;
- // The freeform workspace gap
- int mFreeformWorkspaceGapOffset;
- float mFreeformWorkspaceGapPOffset;
- // The relative progress to ensure that the freeform workspace height + gap + stack bottom
- // padding is respected
- int mFreeformWorkspaceOffset;
- float mFreeformWorkspacePOffset;
// The last computed task counts
int mNumStackTasks;
@@ -130,9 +134,6 @@
// The freeform workspace layout
FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
- // Temporary task view transform
- TaskViewTransform mTmpTransform = new TaskViewTransform();
-
// Log function
static ParametricCurve sCurve;
@@ -190,53 +191,57 @@
}
/**
- * Computes the stack and task rects.
+ * Computes the stack and task rects. The given task stack bounds is the whole bounds not
+ * including the search bar.
*/
public void initialize(Rect taskStackBounds) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
RecentsConfiguration config = Recents.getConfiguration();
int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
int heightPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_stack_top_padding);
- // Compute the stack rect, inset from the given task stack bounds
- mStackRect.set(taskStackBounds.left + widthPadding, taskStackBounds.top + heightPadding,
- taskStackBounds.right - widthPadding, taskStackBounds.bottom);
+ // The freeform height is the visible height (not including system insets) - padding above
+ // freeform and below stack - gap between the freeform and stack
mStackBottomOffset = mSystemInsets.bottom + heightPadding;
-
- // Compute the task rect, align it to the top-center square in the stack rect
+ int ffHeight = (taskStackBounds.height() - 2 * heightPadding - mStackBottomOffset) / 2;
+ mFreeformRect.set(taskStackBounds.left + widthPadding,
+ taskStackBounds.top + heightPadding,
+ taskStackBounds.right - widthPadding,
+ taskStackBounds.top + heightPadding + ffHeight);
+ mFreeformStackRect.set(taskStackBounds.left + widthPadding,
+ taskStackBounds.top + heightPadding + ffHeight + heightPadding,
+ taskStackBounds.right - widthPadding,
+ taskStackBounds.bottom);
+ mStackRect.set(taskStackBounds.left + widthPadding,
+ taskStackBounds.top + heightPadding,
+ taskStackBounds.right - widthPadding,
+ taskStackBounds.bottom);
+ // Anchor the task rect to the top-center of the non-freeform stack rect
int size = Math.min(mStackRect.width(), mStackRect.height() - mStackBottomOffset);
- int xOffset = (mStackRect.width() - size) / 2;
- mTaskRect.set(mStackRect.left + xOffset, mStackRect.top,
- mStackRect.right - xOffset, mStackRect.top + size);
-
- // Compute the freeform rect, align it to the top-left of the stack rect
- mFreeformRect.set(mStackRect);
- mFreeformRect.bottom = taskStackBounds.bottom - mStackBottomOffset;
+ mTaskRect.set(mStackRect.left, mStackRect.top,
+ mStackRect.left + size, mStackRect.top + size);
+ mCurrentStackRect = ssp.hasFreeformWorkspaceSupport() ? mFreeformStackRect : mStackRect;
// Compute the progress offsets
int withinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_bar_height);
int betweenAffiliationOffset = (int) (VISIBLE_TASK_HEIGHT_BETWEEN_TASKS * mTaskRect.height());
mWithinAffiliationPOffset = sCurve.computePOffsetForScaledHeight(withinAffiliationOffset,
- mStackRect);
+ mCurrentStackRect);
mBetweenAffiliationPOffset = sCurve.computePOffsetForScaledHeight(betweenAffiliationOffset,
- mStackRect);
+ mCurrentStackRect);
mTaskHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height(),
- mStackRect);
+ mCurrentStackRect);
mTaskHalfHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height() / 2,
- mStackRect);
- mStackBottomPOffset = sCurve.computePOffsetForHeight(mStackBottomOffset, mStackRect);
- mFreeformWorkspaceGapOffset = mStackBottomOffset;
- mFreeformWorkspaceGapPOffset = sCurve.computePOffsetForHeight(mFreeformWorkspaceGapOffset,
- mStackRect);
- mFreeformWorkspaceOffset = mFreeformWorkspaceGapOffset + mFreeformRect.height() +
- mStackBottomOffset;
- mFreeformWorkspacePOffset = sCurve.computePOffsetForHeight(mFreeformWorkspaceOffset,
- mStackRect);
+ mCurrentStackRect);
+ mStackBottomPOffset = sCurve.computePOffsetForHeight(mStackBottomOffset, mCurrentStackRect);
if (DEBUG) {
Log.d(TAG, "initialize");
Log.d(TAG, "\tarclength: " + sCurve.getArcLength());
+ Log.d(TAG, "\tmFreeformRect: " + mFreeformRect);
+ Log.d(TAG, "\tmFreeformStackRect: " + mFreeformStackRect);
Log.d(TAG, "\tmStackRect: " + mStackRect);
Log.d(TAG, "\tmTaskRect: " + mTaskRect);
Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
@@ -246,20 +251,9 @@
Log.d(TAG, "\tmTaskHeightPOffset: " + mTaskHeightPOffset);
Log.d(TAG, "\tmTaskHalfHeightPOffset: " + mTaskHalfHeightPOffset);
Log.d(TAG, "\tmStackBottomPOffset: " + mStackBottomPOffset);
- Log.d(TAG, "\tmFreeformWorkspacePOffset: " + mFreeformWorkspacePOffset);
- Log.d(TAG, "\tmFreeformWorkspaceGapPOffset: " + mFreeformWorkspaceGapPOffset);
- Log.d(TAG, "\ty at p=0: " + sCurve.pToX(0f, mStackRect));
- Log.d(TAG, "\ty at p=1: " + sCurve.pToX(1f, mStackRect));
-
- for (int height = 0; height <= 2000; height += 50) {
- float p = sCurve.computePOffsetForScaledHeight(height, mStackRect);
- float p2 = sCurve.computePOffsetForHeight(height, mStackRect);
- Log.d(TAG, "offset: " + height + ", " +
- p + " => " + (mStackRect.bottom - sCurve.pToX(1f - p, mStackRect)) /
- sCurve.pToScale(1f - p) + ", " +
- p2 + " => " + (mStackRect.bottom - sCurve.pToX(1f - p2, mStackRect)));
- }
+ Log.d(TAG, "\ty at p=0: " + sCurve.pToX(0f, mCurrentStackRect));
+ Log.d(TAG, "\ty at p=1: " + sCurve.pToX(1f, mCurrentStackRect));
}
}
@@ -279,7 +273,7 @@
ArrayList<Task> tasks = stack.getTasks();
if (tasks.isEmpty()) {
mFrontMostTaskP = 0;
- mMinScrollP = mMaxScrollP = mStackEndScrollP = mPreferredStackEndScrollP = 0;
+ mMinScrollP = mMaxScrollP = 0;
mNumStackTasks = mNumFreeformTasks = 0;
return;
}
@@ -307,6 +301,10 @@
Task task = stackTasks.get(i);
mTaskProgressMap.put(task.key, pAtFrontMostTaskTop);
+ if (DEBUG) {
+ Log.d(TAG, "Update: " + task.activityLabel + " p: " + pAtFrontMostTaskTop);
+ }
+
if (i < (taskCount - 1)) {
// Increment the peek height
float pPeek = task.group.isFrontMostTask(task) ?
@@ -316,37 +314,24 @@
}
mFrontMostTaskP = pAtFrontMostTaskTop;
- // Set the stack end scroll progress to the point at which the bottom of the front-most
- // task is aligned to the bottom of the stack
- mStackEndScrollP = alignToStackBottom(pAtFrontMostTaskTop, mTaskHeightPOffset);
if (mNumStackTasks > 1) {
- // Set the preferred stack end scroll progress to the point where the bottom of the
- // front-most task is offset by the navbar and padding from the bottom of the stack
- mPreferredStackEndScrollP = mStackEndScrollP + mStackBottomPOffset;
-
+ // Set the stack end scroll progress to the point at which the bottom of the front-most
+ // task is aligned to the bottom of the stack
+ mMaxScrollP = alignToStackBottom(pAtFrontMostTaskTop,
+ mStackBottomPOffset + mTaskHeightPOffset);
// Basically align the back-most task such that the last two tasks would be visible
- mMinScrollP = alignToStackBottom(pAtBackMostTaskTop, 2 *
- mBetweenAffiliationPOffset);
+ mMinScrollP = alignToStackBottom(pAtBackMostTaskTop,
+ mStackBottomPOffset + mTaskHeightPOffset);
} else {
// When there is a single item, then just make all the stack progresses the same
- mPreferredStackEndScrollP = mStackEndScrollP;
- mMinScrollP = mStackEndScrollP;
+ mMinScrollP = mMaxScrollP = 0;
}
- } else {
- // TODO: In the case where there is only freeform tasks, then the scrolls should be
- // set to zero
}
if (!freeformTasks.isEmpty()) {
- // The max scroll includes the freeform workspace offset. As the scroll progress exceeds
- // mStackEndScrollP up to mMaxScrollP, the stack will translate upwards and the freeform
- // workspace will be visible
mFreeformLayoutAlgorithm.update(freeformTasks, this);
- mMaxScrollP = mStackEndScrollP + mFreeformWorkspacePOffset;
- mInitialScrollP = isInitialStateFreeform(stack) ?
- mMaxScrollP : mPreferredStackEndScrollP;
+ mInitialScrollP = mMaxScrollP;
} else {
- mMaxScrollP = mPreferredStackEndScrollP;
mInitialScrollP = Math.max(mMinScrollP, mMaxScrollP - mTaskHalfHeightPOffset);
}
@@ -354,7 +339,6 @@
Log.d(TAG, "mNumStackTasks: " + mNumStackTasks);
Log.d(TAG, "mNumFreeformTasks: " + mNumFreeformTasks);
Log.d(TAG, "mMinScrollP: " + mMinScrollP);
- Log.d(TAG, "mStackEndScrollP: " + mStackEndScrollP);
Log.d(TAG, "mMaxScrollP: " + mMaxScrollP);
}
}
@@ -369,28 +353,28 @@
return new VisibilityReport(1, 1);
}
- // If there are freeform tasks, then they will be the only ones visible
- int freeformTaskCount = 0;
- for (Task t : tasks) {
- if (t.isFreeformTask()) {
- freeformTaskCount++;
- }
- }
- if (freeformTaskCount > 0) {
- return new VisibilityReport(freeformTaskCount, freeformTaskCount);
+ // Quick return when there are no stack tasks
+ if (mNumStackTasks == 0) {
+ return new VisibilityReport(Math.max(mNumFreeformTasks, 1),
+ Math.max(mNumFreeformTasks, 1));
}
// Otherwise, walk backwards in the stack and count the number of tasks and visible
- // thumbnails
+ // thumbnails and add that to the total freeform task count
int taskHeight = mTaskRect.height();
int taskBarHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_bar_height);
- int numVisibleTasks = 1;
- int numVisibleThumbnails = 1;
- float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
- int prevScreenY = sCurve.pToX(progress, mStackRect);
+ int numVisibleTasks = Math.max(mNumFreeformTasks, 1);
+ int numVisibleThumbnails = Math.max(mNumFreeformTasks, 1);
+ Task firstNonFreeformTask = tasks.get(tasks.size() - mNumFreeformTasks - 1);
+ float progress = mTaskProgressMap.get(firstNonFreeformTask.key) - mInitialScrollP;
+ int prevScreenY = sCurve.pToX(progress, mCurrentStackRect);
for (int i = tasks.size() - 2; i >= 0; i--) {
Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ continue;
+ }
+
progress = mTaskProgressMap.get(task.key) - mInitialScrollP;
if (progress < 0) {
break;
@@ -399,7 +383,7 @@
if (isFrontMostTaskInGroup) {
float scaleAtP = sCurve.pToScale(progress);
int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
- int screenY = sCurve.pToX(progress, mStackRect) + scaleYOffsetAtP;
+ int screenY = sCurve.pToX(progress, mCurrentStackRect) + scaleYOffsetAtP;
boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
if (hasVisibleThumbnail) {
numVisibleThumbnails++;
@@ -431,17 +415,8 @@
*/
public TaskViewTransform getStackTransform(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform prevTransform) {
- if (mFreeformLayoutAlgorithm.isTransformAvailable(task, stackScroll, this)) {
- mFreeformLayoutAlgorithm.getTransform(task, stackScroll, transformOut, this);
- if (transformOut.visible) {
- getFreeformWorkspaceBounds(stackScroll, mTmpTransform);
- transformOut.translationY += mTmpTransform.translationY;
- transformOut.translationZ = mMaxTranslationZ;
- transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(0, transformOut.translationY);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.p = 0;
- }
+ if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
+ mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
return transformOut;
} else {
// Return early if we have an invalid index
@@ -463,32 +438,25 @@
// modulate some values directly
float pTaskRelative = mMinScrollP - stackScroll;
float scale = (mNumFreeformTasks > 0) ? 1f : SINGLE_TASK_SCALE;
- int topOffset = (mStackRect.height() - mTaskRect.height()) / 2;
+ int topOffset = (mCurrentStackRect.top - mTaskRect.top) +
+ (mCurrentStackRect.height() - mTaskRect.height()) / 2;
transformOut.scale = scale;
- transformOut.translationX = 0;
- transformOut.translationY = (int) (topOffset + (pTaskRelative * mStackRect.height()));
+ transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2;
+ transformOut.translationY = (int) (topOffset + (pTaskRelative * mCurrentStackRect.height()));
transformOut.translationZ = mMaxTranslationZ;
transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(0, transformOut.translationY);
+ transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = true;
transformOut.p = pTaskRelative;
return transformOut;
} else {
- // Once we scroll past the preferred stack end scroll, then we should start translating
- // the cards in screen space and lock their final state at the end stack progress
- int overscrollYOffset = 0;
- if (mNumFreeformTasks > 0 && stackScroll > mStackEndScrollP) {
- float stackOverscroll = (stackScroll - mPreferredStackEndScrollP) /
- (mFreeformWorkspacePOffset - mFreeformWorkspaceGapPOffset);
- overscrollYOffset = (int) (Math.max(0, stackOverscroll) *
- (mFreeformWorkspaceOffset - mFreeformWorkspaceGapPOffset));
- stackScroll = Math.min(mPreferredStackEndScrollP, stackScroll);
- }
-
float pTaskRelative = taskProgress - stackScroll;
float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
+ if (DEBUG) {
+ Log.d(TAG, "getStackTransform (normal): " + taskProgress + ", " + stackScroll);
+ }
// If the task top is outside of the bounds below the screen, then immediately reset it
if (pTaskRelative > 1f) {
@@ -508,18 +476,18 @@
float scale = sCurve.pToScale(pBounded);
int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
transformOut.scale = scale;
- transformOut.translationX = 0;
- transformOut.translationY = sCurve.pToX(pBounded, mStackRect) - mStackRect.top -
- scaleYOffset - overscrollYOffset;
+ transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2;
+ transformOut.translationY = (mCurrentStackRect.top - mTaskRect.top) +
+ (sCurve.pToX(pBounded, mCurrentStackRect) - mCurrentStackRect.top) -
+ scaleYOffset;
transformOut.translationZ = Math.max(mMinTranslationZ,
mMinTranslationZ + (pBounded * (mMaxTranslationZ - mMinTranslationZ)));
transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(0, transformOut.translationY);
+ transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = true;
transformOut.p = pTaskRelative;
if (DEBUG) {
- Log.d(TAG, "getStackTransform (normal): " + taskProgress + ", " + stackScroll);
Log.d(TAG, "\t" + transformOut);
}
@@ -528,61 +496,6 @@
}
/**
- * Returns whether this stack should be initialized to show the freeform workspace or not.
- */
- public boolean isInitialStateFreeform(TaskStack stack) {
- Task launchTarget = stack.getLaunchTarget();
- if (launchTarget != null) {
- return launchTarget.isFreeformTask();
- }
- Task frontTask = stack.getFrontMostTask();
- if (frontTask != null) {
- return frontTask.isFreeformTask();
- }
- return false;
- }
-
- /**
- * Update/get the transform
- */
- public TaskViewTransform getFreeformWorkspaceBounds(float stackScroll,
- TaskViewTransform transformOut) {
- transformOut.reset();
- if (mNumFreeformTasks == 0) {
- return transformOut;
- }
-
- if (stackScroll > mStackEndScrollP) {
- // mStackEndScroll is the point at which the first stack task is bottom aligned with the
- // stack, so we offset from on the stack rect height.
- float stackOverscroll = (Math.max(0, stackScroll - mStackEndScrollP)) /
- mFreeformWorkspacePOffset;
- int overscrollYOffset = (int) (stackOverscroll * mFreeformWorkspaceOffset);
- transformOut.scale = 1f;
- transformOut.alpha = 1f;
- transformOut.translationY = mStackRect.height() + mFreeformWorkspaceGapOffset -
- overscrollYOffset;
- transformOut.rect.set(mFreeformRect);
- transformOut.rect.offset(0, transformOut.translationY);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = true;
- }
- return transformOut;
- }
-
- /**
- * Returns the preferred maximum scroll position for a stack at the given {@param scroll}.
- */
- public float getPreferredMaxScrollPosition(float scroll) {
- float maxStackScrollBounds = mStackEndScrollP + SNAP_TO_MAX_STACK_SCROLL_FACTOR *
- (mMaxScrollP - mStackEndScrollP);
- if (scroll < maxStackScrollBounds) {
- return mPreferredStackEndScrollP;
- }
- return mMaxScrollP;
- }
-
- /**
* Returns the untransformed task view bounds.
*/
public Rect getUntransformedTaskViewBounds() {
@@ -604,7 +517,7 @@
* screen along the arc-length proportionally (1/arclength).
*/
public float getDeltaPForY(int downY, int y) {
- float deltaP = (float) (y - downY) / mStackRect.height() * (1f / sCurve.getArcLength());
+ float deltaP = (float) (y - downY) / mCurrentStackRect.height() * (1f / sCurve.getArcLength());
return -deltaP;
}
@@ -613,7 +526,7 @@
* of the curve, map back to the screen y.
*/
public int getYForDeltaP(float downScrollP, float p) {
- int y = (int) ((p - downScrollP) * mStackRect.height() * sCurve.getArcLength());
+ int y = (int) ((p - downScrollP) * mCurrentStackRect.height() * sCurve.getArcLength());
return -y;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index e7f07f7..14d75a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -281,6 +281,7 @@
/**
* Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
+ * This call ignores freeform tasks.
*/
private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
ArrayList<Task> tasks,
@@ -306,8 +307,16 @@
// Update the stack transforms
TaskViewTransform prevTransform = null;
for (int i = taskCount - 1; i >= 0; i--) {
- TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(tasks.get(i),
- stackScroll, taskTransforms.get(i), prevTransform);
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ continue;
+ }
+
+ TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(task, stackScroll,
+ taskTransforms.get(i), prevTransform);
+ if (DEBUG) {
+ Log.d(TAG, "updateStackTransform: " + i + ", " + transform.visible);
+ }
if (transform.visible) {
if (frontMostVisibleIndex < 0) {
frontMostVisibleIndex = i;
@@ -327,7 +336,7 @@
if (boundTranslationsToRect) {
transform.translationY = Math.min(transform.translationY,
- mLayoutAlgorithm.mStackRect.bottom);
+ mLayoutAlgorithm.mCurrentStackRect.bottom);
}
prevTransform = transform;
}
@@ -344,13 +353,13 @@
// Get all the task transforms
ArrayList<Task> tasks = mStack.getTasks();
float stackScroll = mStackScroller.getStackScroll();
- int[] visibleRange = mTmpVisibleRange;
- boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
- stackScroll, visibleRange, false);
+ int[] visibleStackRange = mTmpVisibleRange;
+ boolean isValidVisibleStackRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
+ stackScroll, visibleStackRange, false);
boolean hasStackBackTransform = false;
boolean hasStackFrontTransform = false;
if (DEBUG) {
- Log.d(TAG, "visibleRange: " + visibleRange[0] + " to " + visibleRange[1]);
+ Log.d(TAG, "visibleRange: " + visibleStackRange[0] + " to " + visibleStackRange[1]);
}
// Return all the invisible children to the pool
@@ -363,7 +372,8 @@
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
- if (visibleRange[1] <= taskIndex && taskIndex <= visibleRange[0]) {
+ if (task.isFreeformTask() ||
+ visibleStackRange[1] <= taskIndex && taskIndex <= visibleStackRange[0]) {
mTmpTaskViewMap.put(task, tv);
} else {
if (tv.isFocusedTask()) {
@@ -371,16 +381,43 @@
lastFocusedTaskIndex = taskIndex;
resetFocusedTask();
}
+ if (DEBUG) {
+ Log.d(TAG, "returning to pool: " + task.key);
+ }
mViewPool.returnViewToPool(tv);
}
}
+ // Pick up all the freeform tasks
+ int firstVisStackIndex = isValidVisibleStackRange ? visibleStackRange[0] : 0;
+ for (int i = mStack.getTaskCount() - 1; i > firstVisStackIndex; i--) {
+ Task task = tasks.get(i);
+ if (!task.isFreeformTask()) {
+ continue;
+ }
+ TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(task, stackScroll,
+ mCurrentTaskTransforms.get(i), null);
+ TaskView tv = mTmpTaskViewMap.get(task);
+ if (tv == null) {
+ if (DEBUG) {
+ Log.d(TAG, "picking up from pool: " + task.key);
+ }
+ tv = mViewPool.pickUpViewFromPool(task, task);
+ if (mLayersDisabled) {
+ tv.disableLayersForOneFrame();
+ }
+ }
+
+ // Animate the task into place
+ tv.updateViewPropertiesToTaskTransform(transform,
+ mStackViewsAnimationDuration, mRequestUpdateClippingListener);
+ }
+
// Pick up all the newly visible children and update all the existing children
- for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
+ for (int i = visibleStackRange[0]; isValidVisibleStackRange && i >= visibleStackRange[1]; i--) {
Task task = tasks.get(i);
TaskViewTransform transform = mCurrentTaskTransforms.get(i);
TaskView tv = mTmpTaskViewMap.get(task);
- int taskIndex = mStack.indexOfTask(task);
if (tv == null) {
tv = mViewPool.pickUpViewFromPool(task, task);
@@ -409,29 +446,19 @@
}
// Animate the task into place
- tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
+ tv.updateViewPropertiesToTaskTransform(transform,
mStackViewsAnimationDuration, mRequestUpdateClippingListener);
}
// Update the focus if the previous focused task was returned to the view pool
if (lastFocusedTaskIndex != -1) {
- if (lastFocusedTaskIndex < visibleRange[1]) {
- setFocusedTask(visibleRange[1], false, wasLastFocusedTaskAnimated);
+ if (lastFocusedTaskIndex < visibleStackRange[1]) {
+ setFocusedTask(visibleStackRange[1], false, wasLastFocusedTaskAnimated);
} else {
- setFocusedTask(visibleRange[0], false, wasLastFocusedTaskAnimated);
+ setFocusedTask(visibleStackRange[0], false, wasLastFocusedTaskAnimated);
}
}
- // Update the freeform workspace
- mLayoutAlgorithm.getFreeformWorkspaceBounds(stackScroll, mTmpTransform);
- if (mTmpTransform.visible) {
- mTmpTransform.rect.roundOut(mTmpRect);
- mFreeformWorkspaceBackground.setAlpha(255);
- mFreeformWorkspaceBackground.setBounds(mTmpRect);
- } else {
- mFreeformWorkspaceBackground.setAlpha(0);
- }
-
// Reset the request-synchronize params
mStackViewsAnimationDuration = 0;
mStackViewsDirty = false;
@@ -491,6 +518,14 @@
// Compute the min and max scroll values
mLayoutAlgorithm.update(mStack);
+ // Update the freeform workspace
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ mTmpRect.set(mLayoutAlgorithm.mFreeformRect);
+ mFreeformWorkspaceBackground.setAlpha(255);
+ mFreeformWorkspaceBackground.setBounds(mTmpRect);
+ }
+
// Debug logging
if (boundScrollToNewMinMax) {
mStackScroller.boundScroll();
@@ -762,7 +797,7 @@
/** Handler for the first layout. */
void onFirstLayout() {
- int offscreenY = mLayoutAlgorithm.mStackRect.bottom;
+ int offscreenY = mLayoutAlgorithm.mCurrentStackRect.bottom;
// Find the launch target task
Task launchTargetTask = mStack.getLaunchTarget();
@@ -863,7 +898,7 @@
mStackScroller.stopScroller();
mStackScroller.stopBoundScrollAnimation();
// Animate all the task views out of view
- ctx.offscreenTranslationY = mLayoutAlgorithm.mStackRect.bottom;
+ ctx.offscreenTranslationY = mLayoutAlgorithm.mCurrentStackRect.bottom;
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
@@ -929,6 +964,18 @@
}
}
+ /**
+ * Launches the freeform tasks.
+ */
+ public boolean launchFreeformTasks() {
+ Task frontTask = mStack.getFrontMostTask();
+ if (frontTask != null && frontTask.isFreeformTask()) {
+ onTaskViewClicked(getChildViewForTask(frontTask), frontTask, false);
+ return true;
+ }
+ return false;
+ }
+
/**** TaskStackCallbacks Implementation ****/
@Override
@@ -975,6 +1022,19 @@
// Animate all the tasks into place
requestSynchronizeStackViewsWithModel(200);
+ } else {
+ // Remove the view associated with this task, we can't rely on updateTransforms
+ // to work here because the task is no longer in the list
+ TaskView tv = getChildViewForTask(removedTask);
+ if (tv != null) {
+ mViewPool.returnViewToPool(tv);
+ }
+
+ // Update the min/max scroll and animate other task views into their new positions
+ updateMinMaxScroll(true);
+
+ // Animate all the tasks into place
+ requestSynchronizeStackViewsWithModel(200);
}
// Update the new front most task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 6b92aed..3a2ed0f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -122,7 +122,7 @@
/** Returns the bounded stack scroll */
float getBoundedStackScroll(float scroll) {
return Math.max(mLayoutAlgorithm.mMinScrollP,
- Math.min(mLayoutAlgorithm.getPreferredMaxScrollPosition(scroll), scroll));
+ Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
}
/** Returns the amount that the absolute value of how much the scroll is out of bounds. */
@@ -194,7 +194,7 @@
// TODO: Remove
@Deprecated
int progressToScrollRange(float p) {
- return (int) (p * mLayoutAlgorithm.mStackRect.height());
+ return (int) (p * mLayoutAlgorithm.mCurrentStackRect.height());
}
/** Called from the view draw, computes the next scroll. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 9e6fb7b..59c9708 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -19,6 +19,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -29,9 +30,11 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -222,49 +225,12 @@
int activePointerIndex = ev.findPointerIndex(mActivePointerId);
int y = (int) ev.getY(activePointerIndex);
int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
- float curScrollP = mScroller.getStackScroll();
if (mIsScrolling) {
- boolean hasFreeformTasks = mSv.mStack.hasFreeformTasks();
- if (hasFreeformTasks && velocity > 0 &&
- curScrollP > layoutAlgorithm.mStackEndScrollP) {
- // Snap to workspace
- float finalY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
- layoutAlgorithm.mPreferredStackEndScrollP);
- mScrollFlingAnimator = ValueAnimator.ofInt(y, (int) finalY);
- mScrollFlingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float deltaP = layoutAlgorithm.getDeltaPForY(mDownY,
- (Integer) animation.getAnimatedValue());
- float scroll = mDownScrollP + deltaP;
- mScroller.setStackScroll(scroll);
- }
- });
- mFlingAnimUtils.apply(mScrollFlingAnimator, y, finalY, velocity);
- mScrollFlingAnimator.start();
- } else if (hasFreeformTasks && velocity < 0 &&
- curScrollP > (layoutAlgorithm.mStackEndScrollP -
- layoutAlgorithm.mTaskHalfHeightPOffset)) {
- // Snap to stack
- float finalY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
- layoutAlgorithm.mMaxScrollP);
- mScrollFlingAnimator = ValueAnimator.ofInt(y, (int) finalY);
- mScrollFlingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float deltaP = layoutAlgorithm.getDeltaPForY(mDownY,
- (Integer) animation.getAnimatedValue());
- float scroll = mDownScrollP + deltaP;
- mScroller.setStackScroll(scroll);
- }
- });
- mFlingAnimUtils.apply(mScrollFlingAnimator, y, finalY, velocity);
- mScrollFlingAnimator.start();
- } else if (mScroller.isScrollOutOfBounds()) {
+ if (mScroller.isScrollOutOfBounds()) {
mScroller.animateBoundScroll();
} else if (Math.abs(velocity) > mMinimumVelocity) {
float minY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
- layoutAlgorithm.mPreferredStackEndScrollP);
+ layoutAlgorithm.mMaxScrollP);
float maxY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
layoutAlgorithm.mMinScrollP);
mScroller.fling(mDownScrollP, mDownY, y, velocity, (int) minY, (int) maxY,
@@ -313,6 +279,17 @@
return;
}
+ // If tapping on the freeform workspace background, just launch the first freeform task
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ Rect freeformRect = mSv.mLayoutAlgorithm.mFreeformRect;
+ if (freeformRect.top <= y && y <= freeformRect.bottom) {
+ if (mSv.launchFreeformTasks()) {
+ return;
+ }
+ }
+ }
+
// The user intentionally tapped on the background, which is like a tap on the "desktop".
// Hide recents and transition to the launcher.
EventBus.getDefault().send(new HideRecentsEvent(false, true));