Loading packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +12 −8 Original line number Diff line number Diff line Loading @@ -200,7 +200,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements mDummyStackView = new TaskStackView(mContext, new TaskStack()); mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header, null, false); reloadHeaderBarLayout(true /* tryAndBindSearchWidget */); reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */); // When we start, preload the data associated with the previous recent tasks. // We can use a new plan since the caches will be the same. Loading @@ -216,7 +216,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements public void onBootCompleted() { mBootCompleted = true; reloadHeaderBarLayout(true /* tryAndBindSearchWidget */); reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */); } @Override Loading Loading @@ -566,8 +566,9 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements * * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one * is not already bound (can be expensive) * @param stack the stack to initialize the stack layout with */ private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) { private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) { RecentsConfiguration config = Recents.getConfiguration(); SystemServicesProxy ssp = Recents.getSystemServices(); Rect windowRect = ssp.getWindowRect(); Loading @@ -593,7 +594,10 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm(); Rect taskStackBounds = new Rect(mTaskStackBounds); algo.setSystemInsets(systemInsets); algo.initialize(taskStackBounds); if (stack != null) { algo.initialize(taskStackBounds, TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack)); } Rect taskViewBounds = algo.getUntransformedTaskViewBounds(); if (!taskViewBounds.equals(mLastTaskViewBounds)) { mLastTaskViewBounds.set(taskViewBounds); Loading Loading @@ -629,7 +633,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements preloadIcon(topTask); // Update the header bar if necessary reloadHeaderBarLayout(false /* tryAndBindSearchWidget */); reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack); // Update the destination rect mDummyStackView.updateLayoutForStack(stack); Loading Loading @@ -800,9 +804,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements boolean isTopTaskHome, boolean animate) { RecentsTaskLoader loader = Recents.getTaskLoader(); // Update the header bar if necessary reloadHeaderBarLayout(false /* tryAndBindSearchWidget */); // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we // should always preload the tasks now. If we are dragging in recents, reload them as // the stacks might have changed. Loading @@ -815,6 +816,9 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements } TaskStack stack = sInstanceLoadPlan.getTaskStack(); // Update the header bar if necessary reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack); // Prepare the dummy stack for the transition mDummyStackView.updateLayoutForStack(stack); TaskStackLayoutAlgorithm.VisibilityReport stackVr = Loading packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +31 −9 Original line number Diff line number Diff line Loading @@ -206,15 +206,21 @@ public class TaskStack { /** * The various possible dock states when dragging and dropping a task. */ public enum DockState implements DropTarget { NONE(-1, 96, null, null), LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192, new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1)), TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192, new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f)), RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192, new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1)), BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192, public static class DockState implements DropTarget { private static final int DOCK_AREA_ALPHA = 192; public static final DockState NONE = new DockState(-1, 96, null, null); public static final DockState LEFT = new DockState( DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1)); public static final DockState TOP = new DockState( DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f)); public static final DockState RIGHT = new DockState( DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1)); public static final DockState BOTTOM = new DockState( DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, new RectF(0, 0.75f, 1, 1), new RectF(0, 0.75f, 1, 1)); @Override Loading Loading @@ -462,6 +468,22 @@ public class TaskStack { return mStackTaskList.size(); } /** * Returns the number of freeform tasks in the active stack. */ public int getStackTaskFreeformCount() { ArrayList<Task> tasks = mStackTaskList.getTasks(); int freeformCount = 0; int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); if (task.isFreeformTask()) { freeformCount++; } } return freeformCount; } /** * Returns the task in stack tasks which is the launch target. */ Loading packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java +13 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.recents.views; import android.graphics.Bitmap; import android.graphics.Rect; import android.util.Log; import com.android.systemui.recents.misc.Utilities; Loading Loading @@ -101,12 +102,18 @@ public class FreeformWorkspaceLayoutAlgorithm { int x = taskIndex % mFreeformCellXCount; int y = taskIndex / mFreeformCellXCount; Bitmap thumbnail = task.thumbnail; float thumbnailScale = 1f; float thumbnailWidth = mFreeformCellWidth; float thumbnailHeight = mFreeformCellHeight; if (thumbnail != null) { int bitmapWidth = task.thumbnail.getWidth(); int bitmapHeight = task.thumbnail.getHeight(); float thumbnailScale = Math.min((float) mFreeformCellWidth / bitmapWidth, thumbnailScale = Math.min((float) mFreeformCellWidth / bitmapWidth, (float) mFreeformCellHeight / bitmapHeight); float thumbnailWidth = bitmapWidth * thumbnailScale; float thumbnailHeight = bitmapHeight * thumbnailScale; thumbnailWidth = bitmapWidth * thumbnailScale; thumbnailHeight = bitmapHeight * thumbnailScale; } int scaleXOffset = (int) (((1f - thumbnailScale) * thumbnailWidth) / 2); int scaleYOffset = (int) (((1f - thumbnailScale) * thumbnailHeight) / 2); transformOut.scale = thumbnailScale * 0.9f; Loading packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +1 −1 Original line number Diff line number Diff line Loading @@ -257,7 +257,7 @@ public class RecentsTransitionHelper { TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm(); Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect); offscreenTaskRect.offsetTo(offscreenTaskRect.left, layoutAlgorithm.mCurrentStackRect.bottom); layoutAlgorithm.mStackRect.bottom); // If this is a full screen stack, the transition will be towards the single, full screen // task. We only need the transition spec for this task. Loading packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +90 −44 Original line number Diff line number Diff line Loading @@ -113,6 +113,71 @@ public class TaskStackLayoutAlgorithm { public static final float STATE_FOCUSED = 1f; public static final float STATE_UNFOCUSED = 0f; /** * The various stack/freeform states. */ public static class StackState { public static final StackState FREEFORM_ONLY = new StackState(1f); public static final StackState STACK_ONLY = new StackState(0f); public static final StackState SPLIT = new StackState(0.5f); public final float freeformHeightPct; /** * @param freeformHeightPct the percentage of the stack height (not including paddings) to * allocate to the freeform workspace */ StackState(float freeformHeightPct) { this.freeformHeightPct = freeformHeightPct; } /** * Resolves the stack state for the layout given a task stack. */ public static StackState getStackStateForStack(TaskStack stack) { SystemServicesProxy ssp = Recents.getSystemServices(); boolean hasFreeformWorkspaces = ssp.hasFreeformWorkspaceSupport(); int taskCount = stack.getStackTaskCount(); int freeformCount = stack.getStackTaskFreeformCount(); int stackCount = taskCount - freeformCount; if (hasFreeformWorkspaces && stackCount > 0 && freeformCount > 0) { return SPLIT; } else if (hasFreeformWorkspaces && freeformCount > 0) { return FREEFORM_ONLY; } else { return STACK_ONLY; } } /** * Computes the freeform and stack rect for this state. * * @param freeformRectOut the freeform rect to be written out * @param stackRectOut the stack rect, we only write out the top of the stack * @param taskStackBounds the full rect that the freeform rect can take up */ public void computeRects(Rect freeformRectOut, Rect stackRectOut, Rect taskStackBounds, int widthPadding, int heightPadding, int stackBottomOffset) { int availableHeight = taskStackBounds.height() - stackBottomOffset; int ffPaddedHeight = (int) (availableHeight * freeformHeightPct); int ffHeight = Math.max(0, ffPaddedHeight - (2 * heightPadding)); freeformRectOut.set(taskStackBounds.left + widthPadding, taskStackBounds.top + heightPadding, taskStackBounds.right - widthPadding, taskStackBounds.top + heightPadding + ffHeight); stackRectOut.set(taskStackBounds.left + widthPadding, taskStackBounds.top, taskStackBounds.right - widthPadding, taskStackBounds.bottom); if (ffPaddedHeight > 0) { stackRectOut.top += ffPaddedHeight; } else { stackRectOut.top += heightPadding; } } } /** * A Property wrapper around the <code>focusState</code> functionality handled by the * {@link TaskStackLayoutAlgorithm#setFocusState(float)} and Loading Loading @@ -146,20 +211,15 @@ public class TaskStackLayoutAlgorithm { Context mContext; private TaskStackView mStackView; private Interpolator mFastOutSlowInInterpolator; private StackState mState = StackState.SPLIT; // The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot. public Rect mTaskRect = new 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 = new Rect(); public Rect mStackRect = new Rect(); // This is the current system insets public Rect mSystemInsets = new Rect(); // This is the bounds of the history button above the stack rect Loading Loading @@ -273,41 +333,29 @@ public class TaskStackLayoutAlgorithm { * 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) { public void initialize(Rect taskStackBounds, StackState state) { SystemServicesProxy ssp = Recents.getSystemServices(); RecentsDebugFlags debugFlags = Recents.getDebugFlags(); RecentsConfiguration config = Recents.getConfiguration(); int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width()); int heightPadding = mContext.getResources().getDimensionPixelSize( R.dimen.recents_stack_top_padding); Rect lastStackRect = new Rect(mCurrentStackRect); Rect lastStackRect = new Rect(mStackRect); // The freeform height is the visible height (not including system insets) - padding above // freeform and below stack - gap between the freeform and stack mStackTopOffset = mFocusedPeekHeight + heightPadding; mStackBottomOffset = mSystemInsets.bottom + heightPadding; 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); mCurrentStackRect = ssp.hasFreeformWorkspaceSupport() ? mFreeformStackRect : mStackRect; mHistoryButtonRect.set(mCurrentStackRect.left, mCurrentStackRect.top - heightPadding, mCurrentStackRect.right, mCurrentStackRect.top + mFocusedPeekHeight); state.computeRects(mFreeformRect, mStackRect, taskStackBounds, widthPadding, heightPadding, mStackBottomOffset); mHistoryButtonRect.set(mStackRect.left, mStackRect.top - heightPadding, mStackRect.right, mStackRect.top + mFocusedPeekHeight); // Anchor the task rect to the top-center of the non-freeform stack rect float aspect = (float) (taskStackBounds.width() - mSystemInsets.left - mSystemInsets.right) / (taskStackBounds.height() - mSystemInsets.bottom); int width = mStackRect.width(); int minHeight = mCurrentStackRect.height() - mFocusedPeekHeight - mStackBottomOffset; int minHeight = mStackRect.height() - mFocusedPeekHeight - mStackBottomOffset; int height = debugFlags.isFullscreenThumbnailsEnabled() ? (int) Math.min(width / aspect, minHeight) : width; Loading @@ -315,7 +363,7 @@ public class TaskStackLayoutAlgorithm { mStackRect.left + width, mStackRect.top + height); // Short circuit here if the stack rects haven't changed so we don't do all the work below if (lastStackRect.equals(mCurrentStackRect)) { if (lastStackRect.equals(mStackRect)) { return; } Loading @@ -328,9 +376,7 @@ public class TaskStackLayoutAlgorithm { if (DEBUG) { Log.d(TAG, "initialize"); Log.d(TAG, "\tmFreeformRect: " + mFreeformRect); Log.d(TAG, "\tmFreeformStackRect: " + mFreeformStackRect); Log.d(TAG, "\tmStackRect: " + mStackRect); Log.d(TAG, "\tmCurrentStackRect: " + mCurrentStackRect); Log.d(TAG, "\tmTaskRect: " + mTaskRect); Log.d(TAG, "\tmSystemInsets: " + mSystemInsets); } Loading Loading @@ -386,7 +432,7 @@ public class TaskStackLayoutAlgorithm { mMinScrollP = mMaxScrollP = 0; } else { float bottomOffsetPct = (float) (mStackBottomOffset + mTaskRect.height()) / mCurrentStackRect.height(); mStackRect.height(); float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct); mMinScrollP = 0; mMaxScrollP = Math.max(mMinScrollP, Loading @@ -408,7 +454,7 @@ public class TaskStackLayoutAlgorithm { mInitialScrollP = Math.max(mMinScrollP, mNumStackTasks - 2); } } else { float offsetPct = (float) (mTaskRect.height() / 2) / mCurrentStackRect.height(); float offsetPct = (float) (mTaskRect.height() / 2) / mStackRect.height(); float normX = mUnfocusedCurveInterpolator.getX(offsetPct); mInitialScrollP = (mNumStackTasks - 1) - mUnfocusedRange.getAbsoluteX(normX); } Loading @@ -428,7 +474,7 @@ public class TaskStackLayoutAlgorithm { public void updateFocusStateOnScroll(int yMovement) { Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator); if (mFocusState > STATE_UNFOCUSED) { float delta = (float) yMovement / (UNFOCUS_MULTIPLIER * mCurrentStackRect.height()); float delta = (float) yMovement / (UNFOCUS_MULTIPLIER * mStackRect.height()); mFocusState -= Math.min(mFocusState, Math.abs(delta)); } } Loading Loading @@ -558,7 +604,7 @@ public class TaskStackLayoutAlgorithm { float p = mUnfocusedRange.getNormalizedX(taskProgress); float yp = mUnfocusedCurveInterpolator.getInterpolation(p); float unfocusedP = p; int unFocusedY = (int) (Math.max(0f, (1f - yp)) * mCurrentStackRect.height()); int unFocusedY = (int) (Math.max(0f, (1f - yp)) * mStackRect.height()); boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress); int focusedY = 0; boolean focusedVisible = true; Loading @@ -566,7 +612,7 @@ public class TaskStackLayoutAlgorithm { mFocusedRange.offset(stackScroll); p = mFocusedRange.getNormalizedX(taskProgress); yp = mFocusedCurveInterpolator.getInterpolation(p); focusedY = (int) (Math.max(0f, (1f - yp)) * mCurrentStackRect.height()); focusedY = (int) (Math.max(0f, (1f - yp)) * mStackRect.height()); focusedVisible = mFocusedRange.isInRange(taskProgress); } Loading @@ -583,8 +629,8 @@ public class TaskStackLayoutAlgorithm { // When there is exactly one task, then decouple the task from the stack and just move // in screen space p = (mMinScrollP - stackScroll) / mNumStackTasks; int centerYOffset = (mCurrentStackRect.top - mTaskRect.top) + (mCurrentStackRect.height() - mTaskRect.height()) / 2; int centerYOffset = (mStackRect.top - mTaskRect.top) + (mStackRect.height() - mTaskRect.height()) / 2; y = centerYOffset + getYForDeltaP(p, 0); z = mMaxTranslationZ; relP = 1f; Loading @@ -592,7 +638,7 @@ public class TaskStackLayoutAlgorithm { } else { // Otherwise, update the task to the stack layout y = unFocusedY + (int) (mFocusState * (focusedY - unFocusedY)); y += (mCurrentStackRect.top - mTaskRect.top); y += (mStackRect.top - mTaskRect.top); z = Math.max(mMinTranslationZ, Math.min(mMaxTranslationZ, mMinTranslationZ + (p * (mMaxTranslationZ - mMinTranslationZ)))); relP = unfocusedP; Loading @@ -600,7 +646,7 @@ public class TaskStackLayoutAlgorithm { // Fill out the transform transformOut.scale = 1f; transformOut.translationX = (mCurrentStackRect.width() - mTaskRect.width()) / 2; transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2; transformOut.translationY = y; transformOut.translationZ = z; transformOut.rect.set(mTaskRect); Loading Loading @@ -633,7 +679,7 @@ public class TaskStackLayoutAlgorithm { * screen along the arc-length proportionally (1/arclength). */ public float getDeltaPForY(int downY, int y) { float deltaP = (float) (y - downY) / mCurrentStackRect.height() * float deltaP = (float) (y - downY) / mStackRect.height() * mUnfocusedCurveInterpolator.getArcLength(); return -deltaP; } Loading @@ -643,7 +689,7 @@ public class TaskStackLayoutAlgorithm { * of the curve, map back to the screen y. */ public int getYForDeltaP(float downScrollP, float p) { int y = (int) ((p - downScrollP) * mCurrentStackRect.height() * int y = (int) ((p - downScrollP) * mStackRect.height() * (1f / mUnfocusedCurveInterpolator.getArcLength())); return -y; } Loading @@ -658,12 +704,12 @@ public class TaskStackLayoutAlgorithm { // Initialize the focused curve. This curve is a piecewise curve composed of several // quadradic beziers that goes from (0,1) through (0.5, peek height offset), // (0.667, next task offset), (0.833, bottom task offset), and (1,0). float peekHeightPct = (float) mFocusedPeekHeight / mCurrentStackRect.height(); float peekHeightPct = (float) mFocusedPeekHeight / mStackRect.height(); Path p = new Path(); p.moveTo(0f, 1f); p.lineTo(0.5f, 1f - peekHeightPct); p.lineTo(0.66666667f, (float) (taskBarHeight * 3) / mCurrentStackRect.height()); p.lineTo(0.83333333f, (float) (taskBarHeight / 2) / mCurrentStackRect.height()); p.lineTo(0.66666667f, (float) (taskBarHeight * 3) / mStackRect.height()); p.lineTo(0.83333333f, (float) (taskBarHeight / 2) / mStackRect.height()); p.lineTo(1f, 0f); return p; } Loading @@ -680,7 +726,7 @@ public class TaskStackLayoutAlgorithm { // there is a tangent at (0.5, peek height offset). float cpoint1X = 0.4f; float cpoint1Y = 1f; float peekHeightPct = (float) mFocusedPeekHeight / mCurrentStackRect.height(); float peekHeightPct = (float) mFocusedPeekHeight / mStackRect.height(); float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X); float b = 1f - slope * cpoint1X; float cpoint2X = 0.75f; Loading Loading
packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +12 −8 Original line number Diff line number Diff line Loading @@ -200,7 +200,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements mDummyStackView = new TaskStackView(mContext, new TaskStack()); mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header, null, false); reloadHeaderBarLayout(true /* tryAndBindSearchWidget */); reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */); // When we start, preload the data associated with the previous recent tasks. // We can use a new plan since the caches will be the same. Loading @@ -216,7 +216,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements public void onBootCompleted() { mBootCompleted = true; reloadHeaderBarLayout(true /* tryAndBindSearchWidget */); reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */); } @Override Loading Loading @@ -566,8 +566,9 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements * * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one * is not already bound (can be expensive) * @param stack the stack to initialize the stack layout with */ private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) { private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) { RecentsConfiguration config = Recents.getConfiguration(); SystemServicesProxy ssp = Recents.getSystemServices(); Rect windowRect = ssp.getWindowRect(); Loading @@ -593,7 +594,10 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm(); Rect taskStackBounds = new Rect(mTaskStackBounds); algo.setSystemInsets(systemInsets); algo.initialize(taskStackBounds); if (stack != null) { algo.initialize(taskStackBounds, TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack)); } Rect taskViewBounds = algo.getUntransformedTaskViewBounds(); if (!taskViewBounds.equals(mLastTaskViewBounds)) { mLastTaskViewBounds.set(taskViewBounds); Loading Loading @@ -629,7 +633,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements preloadIcon(topTask); // Update the header bar if necessary reloadHeaderBarLayout(false /* tryAndBindSearchWidget */); reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack); // Update the destination rect mDummyStackView.updateLayoutForStack(stack); Loading Loading @@ -800,9 +804,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements boolean isTopTaskHome, boolean animate) { RecentsTaskLoader loader = Recents.getTaskLoader(); // Update the header bar if necessary reloadHeaderBarLayout(false /* tryAndBindSearchWidget */); // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we // should always preload the tasks now. If we are dragging in recents, reload them as // the stacks might have changed. Loading @@ -815,6 +816,9 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements } TaskStack stack = sInstanceLoadPlan.getTaskStack(); // Update the header bar if necessary reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack); // Prepare the dummy stack for the transition mDummyStackView.updateLayoutForStack(stack); TaskStackLayoutAlgorithm.VisibilityReport stackVr = Loading
packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +31 −9 Original line number Diff line number Diff line Loading @@ -206,15 +206,21 @@ public class TaskStack { /** * The various possible dock states when dragging and dropping a task. */ public enum DockState implements DropTarget { NONE(-1, 96, null, null), LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192, new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1)), TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192, new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f)), RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192, new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1)), BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192, public static class DockState implements DropTarget { private static final int DOCK_AREA_ALPHA = 192; public static final DockState NONE = new DockState(-1, 96, null, null); public static final DockState LEFT = new DockState( DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1)); public static final DockState TOP = new DockState( DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f)); public static final DockState RIGHT = new DockState( DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1)); public static final DockState BOTTOM = new DockState( DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, new RectF(0, 0.75f, 1, 1), new RectF(0, 0.75f, 1, 1)); @Override Loading Loading @@ -462,6 +468,22 @@ public class TaskStack { return mStackTaskList.size(); } /** * Returns the number of freeform tasks in the active stack. */ public int getStackTaskFreeformCount() { ArrayList<Task> tasks = mStackTaskList.getTasks(); int freeformCount = 0; int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); if (task.isFreeformTask()) { freeformCount++; } } return freeformCount; } /** * Returns the task in stack tasks which is the launch target. */ Loading
packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java +13 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.recents.views; import android.graphics.Bitmap; import android.graphics.Rect; import android.util.Log; import com.android.systemui.recents.misc.Utilities; Loading Loading @@ -101,12 +102,18 @@ public class FreeformWorkspaceLayoutAlgorithm { int x = taskIndex % mFreeformCellXCount; int y = taskIndex / mFreeformCellXCount; Bitmap thumbnail = task.thumbnail; float thumbnailScale = 1f; float thumbnailWidth = mFreeformCellWidth; float thumbnailHeight = mFreeformCellHeight; if (thumbnail != null) { int bitmapWidth = task.thumbnail.getWidth(); int bitmapHeight = task.thumbnail.getHeight(); float thumbnailScale = Math.min((float) mFreeformCellWidth / bitmapWidth, thumbnailScale = Math.min((float) mFreeformCellWidth / bitmapWidth, (float) mFreeformCellHeight / bitmapHeight); float thumbnailWidth = bitmapWidth * thumbnailScale; float thumbnailHeight = bitmapHeight * thumbnailScale; thumbnailWidth = bitmapWidth * thumbnailScale; thumbnailHeight = bitmapHeight * thumbnailScale; } int scaleXOffset = (int) (((1f - thumbnailScale) * thumbnailWidth) / 2); int scaleYOffset = (int) (((1f - thumbnailScale) * thumbnailHeight) / 2); transformOut.scale = thumbnailScale * 0.9f; Loading
packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +1 −1 Original line number Diff line number Diff line Loading @@ -257,7 +257,7 @@ public class RecentsTransitionHelper { TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm(); Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect); offscreenTaskRect.offsetTo(offscreenTaskRect.left, layoutAlgorithm.mCurrentStackRect.bottom); layoutAlgorithm.mStackRect.bottom); // If this is a full screen stack, the transition will be towards the single, full screen // task. We only need the transition spec for this task. Loading
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +90 −44 Original line number Diff line number Diff line Loading @@ -113,6 +113,71 @@ public class TaskStackLayoutAlgorithm { public static final float STATE_FOCUSED = 1f; public static final float STATE_UNFOCUSED = 0f; /** * The various stack/freeform states. */ public static class StackState { public static final StackState FREEFORM_ONLY = new StackState(1f); public static final StackState STACK_ONLY = new StackState(0f); public static final StackState SPLIT = new StackState(0.5f); public final float freeformHeightPct; /** * @param freeformHeightPct the percentage of the stack height (not including paddings) to * allocate to the freeform workspace */ StackState(float freeformHeightPct) { this.freeformHeightPct = freeformHeightPct; } /** * Resolves the stack state for the layout given a task stack. */ public static StackState getStackStateForStack(TaskStack stack) { SystemServicesProxy ssp = Recents.getSystemServices(); boolean hasFreeformWorkspaces = ssp.hasFreeformWorkspaceSupport(); int taskCount = stack.getStackTaskCount(); int freeformCount = stack.getStackTaskFreeformCount(); int stackCount = taskCount - freeformCount; if (hasFreeformWorkspaces && stackCount > 0 && freeformCount > 0) { return SPLIT; } else if (hasFreeformWorkspaces && freeformCount > 0) { return FREEFORM_ONLY; } else { return STACK_ONLY; } } /** * Computes the freeform and stack rect for this state. * * @param freeformRectOut the freeform rect to be written out * @param stackRectOut the stack rect, we only write out the top of the stack * @param taskStackBounds the full rect that the freeform rect can take up */ public void computeRects(Rect freeformRectOut, Rect stackRectOut, Rect taskStackBounds, int widthPadding, int heightPadding, int stackBottomOffset) { int availableHeight = taskStackBounds.height() - stackBottomOffset; int ffPaddedHeight = (int) (availableHeight * freeformHeightPct); int ffHeight = Math.max(0, ffPaddedHeight - (2 * heightPadding)); freeformRectOut.set(taskStackBounds.left + widthPadding, taskStackBounds.top + heightPadding, taskStackBounds.right - widthPadding, taskStackBounds.top + heightPadding + ffHeight); stackRectOut.set(taskStackBounds.left + widthPadding, taskStackBounds.top, taskStackBounds.right - widthPadding, taskStackBounds.bottom); if (ffPaddedHeight > 0) { stackRectOut.top += ffPaddedHeight; } else { stackRectOut.top += heightPadding; } } } /** * A Property wrapper around the <code>focusState</code> functionality handled by the * {@link TaskStackLayoutAlgorithm#setFocusState(float)} and Loading Loading @@ -146,20 +211,15 @@ public class TaskStackLayoutAlgorithm { Context mContext; private TaskStackView mStackView; private Interpolator mFastOutSlowInInterpolator; private StackState mState = StackState.SPLIT; // The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot. public Rect mTaskRect = new 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 = new Rect(); public Rect mStackRect = new Rect(); // This is the current system insets public Rect mSystemInsets = new Rect(); // This is the bounds of the history button above the stack rect Loading Loading @@ -273,41 +333,29 @@ public class TaskStackLayoutAlgorithm { * 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) { public void initialize(Rect taskStackBounds, StackState state) { SystemServicesProxy ssp = Recents.getSystemServices(); RecentsDebugFlags debugFlags = Recents.getDebugFlags(); RecentsConfiguration config = Recents.getConfiguration(); int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width()); int heightPadding = mContext.getResources().getDimensionPixelSize( R.dimen.recents_stack_top_padding); Rect lastStackRect = new Rect(mCurrentStackRect); Rect lastStackRect = new Rect(mStackRect); // The freeform height is the visible height (not including system insets) - padding above // freeform and below stack - gap between the freeform and stack mStackTopOffset = mFocusedPeekHeight + heightPadding; mStackBottomOffset = mSystemInsets.bottom + heightPadding; 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); mCurrentStackRect = ssp.hasFreeformWorkspaceSupport() ? mFreeformStackRect : mStackRect; mHistoryButtonRect.set(mCurrentStackRect.left, mCurrentStackRect.top - heightPadding, mCurrentStackRect.right, mCurrentStackRect.top + mFocusedPeekHeight); state.computeRects(mFreeformRect, mStackRect, taskStackBounds, widthPadding, heightPadding, mStackBottomOffset); mHistoryButtonRect.set(mStackRect.left, mStackRect.top - heightPadding, mStackRect.right, mStackRect.top + mFocusedPeekHeight); // Anchor the task rect to the top-center of the non-freeform stack rect float aspect = (float) (taskStackBounds.width() - mSystemInsets.left - mSystemInsets.right) / (taskStackBounds.height() - mSystemInsets.bottom); int width = mStackRect.width(); int minHeight = mCurrentStackRect.height() - mFocusedPeekHeight - mStackBottomOffset; int minHeight = mStackRect.height() - mFocusedPeekHeight - mStackBottomOffset; int height = debugFlags.isFullscreenThumbnailsEnabled() ? (int) Math.min(width / aspect, minHeight) : width; Loading @@ -315,7 +363,7 @@ public class TaskStackLayoutAlgorithm { mStackRect.left + width, mStackRect.top + height); // Short circuit here if the stack rects haven't changed so we don't do all the work below if (lastStackRect.equals(mCurrentStackRect)) { if (lastStackRect.equals(mStackRect)) { return; } Loading @@ -328,9 +376,7 @@ public class TaskStackLayoutAlgorithm { if (DEBUG) { Log.d(TAG, "initialize"); Log.d(TAG, "\tmFreeformRect: " + mFreeformRect); Log.d(TAG, "\tmFreeformStackRect: " + mFreeformStackRect); Log.d(TAG, "\tmStackRect: " + mStackRect); Log.d(TAG, "\tmCurrentStackRect: " + mCurrentStackRect); Log.d(TAG, "\tmTaskRect: " + mTaskRect); Log.d(TAG, "\tmSystemInsets: " + mSystemInsets); } Loading Loading @@ -386,7 +432,7 @@ public class TaskStackLayoutAlgorithm { mMinScrollP = mMaxScrollP = 0; } else { float bottomOffsetPct = (float) (mStackBottomOffset + mTaskRect.height()) / mCurrentStackRect.height(); mStackRect.height(); float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct); mMinScrollP = 0; mMaxScrollP = Math.max(mMinScrollP, Loading @@ -408,7 +454,7 @@ public class TaskStackLayoutAlgorithm { mInitialScrollP = Math.max(mMinScrollP, mNumStackTasks - 2); } } else { float offsetPct = (float) (mTaskRect.height() / 2) / mCurrentStackRect.height(); float offsetPct = (float) (mTaskRect.height() / 2) / mStackRect.height(); float normX = mUnfocusedCurveInterpolator.getX(offsetPct); mInitialScrollP = (mNumStackTasks - 1) - mUnfocusedRange.getAbsoluteX(normX); } Loading @@ -428,7 +474,7 @@ public class TaskStackLayoutAlgorithm { public void updateFocusStateOnScroll(int yMovement) { Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator); if (mFocusState > STATE_UNFOCUSED) { float delta = (float) yMovement / (UNFOCUS_MULTIPLIER * mCurrentStackRect.height()); float delta = (float) yMovement / (UNFOCUS_MULTIPLIER * mStackRect.height()); mFocusState -= Math.min(mFocusState, Math.abs(delta)); } } Loading Loading @@ -558,7 +604,7 @@ public class TaskStackLayoutAlgorithm { float p = mUnfocusedRange.getNormalizedX(taskProgress); float yp = mUnfocusedCurveInterpolator.getInterpolation(p); float unfocusedP = p; int unFocusedY = (int) (Math.max(0f, (1f - yp)) * mCurrentStackRect.height()); int unFocusedY = (int) (Math.max(0f, (1f - yp)) * mStackRect.height()); boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress); int focusedY = 0; boolean focusedVisible = true; Loading @@ -566,7 +612,7 @@ public class TaskStackLayoutAlgorithm { mFocusedRange.offset(stackScroll); p = mFocusedRange.getNormalizedX(taskProgress); yp = mFocusedCurveInterpolator.getInterpolation(p); focusedY = (int) (Math.max(0f, (1f - yp)) * mCurrentStackRect.height()); focusedY = (int) (Math.max(0f, (1f - yp)) * mStackRect.height()); focusedVisible = mFocusedRange.isInRange(taskProgress); } Loading @@ -583,8 +629,8 @@ public class TaskStackLayoutAlgorithm { // When there is exactly one task, then decouple the task from the stack and just move // in screen space p = (mMinScrollP - stackScroll) / mNumStackTasks; int centerYOffset = (mCurrentStackRect.top - mTaskRect.top) + (mCurrentStackRect.height() - mTaskRect.height()) / 2; int centerYOffset = (mStackRect.top - mTaskRect.top) + (mStackRect.height() - mTaskRect.height()) / 2; y = centerYOffset + getYForDeltaP(p, 0); z = mMaxTranslationZ; relP = 1f; Loading @@ -592,7 +638,7 @@ public class TaskStackLayoutAlgorithm { } else { // Otherwise, update the task to the stack layout y = unFocusedY + (int) (mFocusState * (focusedY - unFocusedY)); y += (mCurrentStackRect.top - mTaskRect.top); y += (mStackRect.top - mTaskRect.top); z = Math.max(mMinTranslationZ, Math.min(mMaxTranslationZ, mMinTranslationZ + (p * (mMaxTranslationZ - mMinTranslationZ)))); relP = unfocusedP; Loading @@ -600,7 +646,7 @@ public class TaskStackLayoutAlgorithm { // Fill out the transform transformOut.scale = 1f; transformOut.translationX = (mCurrentStackRect.width() - mTaskRect.width()) / 2; transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2; transformOut.translationY = y; transformOut.translationZ = z; transformOut.rect.set(mTaskRect); Loading Loading @@ -633,7 +679,7 @@ public class TaskStackLayoutAlgorithm { * screen along the arc-length proportionally (1/arclength). */ public float getDeltaPForY(int downY, int y) { float deltaP = (float) (y - downY) / mCurrentStackRect.height() * float deltaP = (float) (y - downY) / mStackRect.height() * mUnfocusedCurveInterpolator.getArcLength(); return -deltaP; } Loading @@ -643,7 +689,7 @@ public class TaskStackLayoutAlgorithm { * of the curve, map back to the screen y. */ public int getYForDeltaP(float downScrollP, float p) { int y = (int) ((p - downScrollP) * mCurrentStackRect.height() * int y = (int) ((p - downScrollP) * mStackRect.height() * (1f / mUnfocusedCurveInterpolator.getArcLength())); return -y; } Loading @@ -658,12 +704,12 @@ public class TaskStackLayoutAlgorithm { // Initialize the focused curve. This curve is a piecewise curve composed of several // quadradic beziers that goes from (0,1) through (0.5, peek height offset), // (0.667, next task offset), (0.833, bottom task offset), and (1,0). float peekHeightPct = (float) mFocusedPeekHeight / mCurrentStackRect.height(); float peekHeightPct = (float) mFocusedPeekHeight / mStackRect.height(); Path p = new Path(); p.moveTo(0f, 1f); p.lineTo(0.5f, 1f - peekHeightPct); p.lineTo(0.66666667f, (float) (taskBarHeight * 3) / mCurrentStackRect.height()); p.lineTo(0.83333333f, (float) (taskBarHeight / 2) / mCurrentStackRect.height()); p.lineTo(0.66666667f, (float) (taskBarHeight * 3) / mStackRect.height()); p.lineTo(0.83333333f, (float) (taskBarHeight / 2) / mStackRect.height()); p.lineTo(1f, 0f); return p; } Loading @@ -680,7 +726,7 @@ public class TaskStackLayoutAlgorithm { // there is a tangent at (0.5, peek height offset). float cpoint1X = 0.4f; float cpoint1Y = 1f; float peekHeightPct = (float) mFocusedPeekHeight / mCurrentStackRect.height(); float peekHeightPct = (float) mFocusedPeekHeight / mStackRect.height(); float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X); float b = 1f - slope * cpoint1X; float cpoint2X = 0.75f; Loading