Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0d1c44a authored by Winson's avatar Winson
Browse files

Adding notion of stack state to the layout.

- This allows us to choose which layout to show, either freeform, stack,
  or a combination of the two
- Fixing crash with null bitmap

Change-Id: I659e66d89f7205ccb0c2ba22c57aee95c8d4b3ab
parent ec52d798
Loading
Loading
Loading
Loading
+12 −8
Original line number Original line Diff line number Diff line
@@ -200,7 +200,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
        mDummyStackView = new TaskStackView(mContext, new TaskStack());
        mDummyStackView = new TaskStackView(mContext, new TaskStack());
        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
                null, false);
                null, false);
        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);


        // When we start, preload the data associated with the previous recent tasks.
        // 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.
        // We can use a new plan since the caches will be the same.
@@ -216,7 +216,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements


    public void onBootCompleted() {
    public void onBootCompleted() {
        mBootCompleted = true;
        mBootCompleted = true;
        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
    }
    }


    @Override
    @Override
@@ -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
     * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
     *                               is not already bound (can be expensive)
     *                               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();
        RecentsConfiguration config = Recents.getConfiguration();
        SystemServicesProxy ssp = Recents.getSystemServices();
        SystemServicesProxy ssp = Recents.getSystemServices();
        Rect windowRect = ssp.getWindowRect();
        Rect windowRect = ssp.getWindowRect();
@@ -593,7 +594,10 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
        TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
        TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
        Rect taskStackBounds = new Rect(mTaskStackBounds);
        Rect taskStackBounds = new Rect(mTaskStackBounds);
        algo.setSystemInsets(systemInsets);
        algo.setSystemInsets(systemInsets);
        algo.initialize(taskStackBounds);
        if (stack != null) {
            algo.initialize(taskStackBounds,
                    TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
        }
        Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
        Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
        if (!taskViewBounds.equals(mLastTaskViewBounds)) {
        if (!taskViewBounds.equals(mLastTaskViewBounds)) {
            mLastTaskViewBounds.set(taskViewBounds);
            mLastTaskViewBounds.set(taskViewBounds);
@@ -629,7 +633,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
        preloadIcon(topTask);
        preloadIcon(topTask);


        // Update the header bar if necessary
        // Update the header bar if necessary
        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);


        // Update the destination rect
        // Update the destination rect
        mDummyStackView.updateLayoutForStack(stack);
        mDummyStackView.updateLayoutForStack(stack);
@@ -800,9 +804,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
            boolean isTopTaskHome, boolean animate) {
            boolean isTopTaskHome, boolean animate) {
        RecentsTaskLoader loader = Recents.getTaskLoader();
        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
        // 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
        // should always preload the tasks now. If we are dragging in recents, reload them as
        // the stacks might have changed.
        // the stacks might have changed.
@@ -815,6 +816,9 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
        }
        }
        TaskStack stack = sInstanceLoadPlan.getTaskStack();
        TaskStack stack = sInstanceLoadPlan.getTaskStack();


        // Update the header bar if necessary
        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);

        // Prepare the dummy stack for the transition
        // Prepare the dummy stack for the transition
        mDummyStackView.updateLayoutForStack(stack);
        mDummyStackView.updateLayoutForStack(stack);
        TaskStackLayoutAlgorithm.VisibilityReport stackVr =
        TaskStackLayoutAlgorithm.VisibilityReport stackVr =
+31 −9
Original line number Original line Diff line number Diff line
@@ -206,15 +206,21 @@ public class TaskStack {
    /**
    /**
     * The various possible dock states when dragging and dropping a task.
     * The various possible dock states when dragging and dropping a task.
     */
     */
    public enum DockState implements DropTarget {
    public static class DockState implements DropTarget {
        NONE(-1, 96, null, null),

        LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192,
        private static final int DOCK_AREA_ALPHA = 192;
                new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1)),
        public static final DockState NONE = new DockState(-1, 96, null, null);
        TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192,
        public static final DockState LEFT = new DockState(
                new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f)),
                DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
        RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192,
                new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1));
                new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1)),
        public static final DockState TOP = new DockState(
        BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192,
                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));
                new RectF(0, 0.75f, 1, 1), new RectF(0, 0.75f, 1, 1));


        @Override
        @Override
@@ -462,6 +468,22 @@ public class TaskStack {
        return mStackTaskList.size();
        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.
     * Returns the task in stack tasks which is the launch target.
     */
     */
+13 −6
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.systemui.recents.views;
package com.android.systemui.recents.views;


import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Rect;
import android.util.Log;
import android.util.Log;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.misc.Utilities;
@@ -101,12 +102,18 @@ public class FreeformWorkspaceLayoutAlgorithm {
            int x = taskIndex % mFreeformCellXCount;
            int x = taskIndex % mFreeformCellXCount;
            int y = 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 bitmapWidth = task.thumbnail.getWidth();
                int bitmapHeight = task.thumbnail.getHeight();
                int bitmapHeight = task.thumbnail.getHeight();
            float thumbnailScale = Math.min((float) mFreeformCellWidth / bitmapWidth,
                thumbnailScale = Math.min((float) mFreeformCellWidth / bitmapWidth,
                        (float) mFreeformCellHeight / bitmapHeight);
                        (float) mFreeformCellHeight / bitmapHeight);
            float thumbnailWidth = bitmapWidth * thumbnailScale;
                thumbnailWidth = bitmapWidth * thumbnailScale;
            float thumbnailHeight = bitmapHeight * thumbnailScale;
                thumbnailHeight = bitmapHeight * thumbnailScale;
            }
            int scaleXOffset = (int) (((1f - thumbnailScale) * thumbnailWidth) / 2);
            int scaleXOffset = (int) (((1f - thumbnailScale) * thumbnailWidth) / 2);
            int scaleYOffset = (int) (((1f - thumbnailScale) * thumbnailHeight) / 2);
            int scaleYOffset = (int) (((1f - thumbnailScale) * thumbnailHeight) / 2);
            transformOut.scale = thumbnailScale * 0.9f;
            transformOut.scale = thumbnailScale * 0.9f;
+1 −1
Original line number Original line Diff line number Diff line
@@ -257,7 +257,7 @@ public class RecentsTransitionHelper {
        TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm();
        TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm();
        Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect);
        Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect);
        offscreenTaskRect.offsetTo(offscreenTaskRect.left,
        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
        // 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.
        // task. We only need the transition spec for this task.
+90 −44
Original line number Original line Diff line number Diff line
@@ -113,6 +113,71 @@ public class TaskStackLayoutAlgorithm {
    public static final float STATE_FOCUSED = 1f;
    public static final float STATE_FOCUSED = 1f;
    public static final float STATE_UNFOCUSED = 0f;
    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
     * A Property wrapper around the <code>focusState</code> functionality handled by the
     * {@link TaskStackLayoutAlgorithm#setFocusState(float)} and
     * {@link TaskStackLayoutAlgorithm#setFocusState(float)} and
@@ -146,20 +211,15 @@ public class TaskStackLayoutAlgorithm {
    Context mContext;
    Context mContext;
    private TaskStackView mStackView;
    private TaskStackView mStackView;
    private Interpolator mFastOutSlowInInterpolator;
    private Interpolator mFastOutSlowInInterpolator;
    private StackState mState = StackState.SPLIT;


    // The task bounds (untransformed) for layout.  This rect is anchored at mTaskRoot.
    // The task bounds (untransformed) for layout.  This rect is anchored at mTaskRoot.
    public Rect mTaskRect = new Rect();
    public Rect mTaskRect = new Rect();
    // The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
    // The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
    public Rect mFreeformRect = new Rect();
    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 stack bounds, inset from the top by the search bar, and runs to
    // the bottom of the screen
    // the bottom of the screen
    private Rect mStackRect = new Rect();
    public 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();
    // This is the current system insets
    // This is the current system insets
    public Rect mSystemInsets = new Rect();
    public Rect mSystemInsets = new Rect();
    // This is the bounds of the history button above the stack rect
    // This is the bounds of the history button above the stack rect
@@ -273,41 +333,29 @@ public class TaskStackLayoutAlgorithm {
     * Computes the stack and task rects.  The given task stack bounds is the whole bounds not
     * Computes the stack and task rects.  The given task stack bounds is the whole bounds not
     * including the search bar.
     * including the search bar.
     */
     */
    public void initialize(Rect taskStackBounds) {
    public void initialize(Rect taskStackBounds, StackState state) {
        SystemServicesProxy ssp = Recents.getSystemServices();
        SystemServicesProxy ssp = Recents.getSystemServices();
        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
        RecentsConfiguration config = Recents.getConfiguration();
        RecentsConfiguration config = Recents.getConfiguration();
        int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
        int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
        int heightPadding = mContext.getResources().getDimensionPixelSize(
        int heightPadding = mContext.getResources().getDimensionPixelSize(
                R.dimen.recents_stack_top_padding);
                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
        // The freeform height is the visible height (not including system insets) - padding above
        // freeform and below stack - gap between the freeform and stack
        // freeform and below stack - gap between the freeform and stack
        mStackTopOffset = mFocusedPeekHeight + heightPadding;
        mStackTopOffset = mFocusedPeekHeight + heightPadding;
        mStackBottomOffset = mSystemInsets.bottom + heightPadding;
        mStackBottomOffset = mSystemInsets.bottom + heightPadding;
        int ffHeight = (taskStackBounds.height() - 2 * heightPadding - mStackBottomOffset) / 2;
        state.computeRects(mFreeformRect, mStackRect, taskStackBounds, widthPadding, heightPadding,
        mFreeformRect.set(taskStackBounds.left + widthPadding,
                mStackBottomOffset);
                taskStackBounds.top + heightPadding,
        mHistoryButtonRect.set(mStackRect.left, mStackRect.top - heightPadding,
                taskStackBounds.right - widthPadding,
                mStackRect.right, mStackRect.top + mFocusedPeekHeight);
                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);


        // Anchor the task rect to the top-center of the non-freeform stack rect
        // Anchor the task rect to the top-center of the non-freeform stack rect
        float aspect = (float) (taskStackBounds.width() - mSystemInsets.left - mSystemInsets.right)
        float aspect = (float) (taskStackBounds.width() - mSystemInsets.left - mSystemInsets.right)
                / (taskStackBounds.height() - mSystemInsets.bottom);
                / (taskStackBounds.height() - mSystemInsets.bottom);
        int width = mStackRect.width();
        int width = mStackRect.width();
        int minHeight = mCurrentStackRect.height() - mFocusedPeekHeight - mStackBottomOffset;
        int minHeight = mStackRect.height() - mFocusedPeekHeight - mStackBottomOffset;
        int height = debugFlags.isFullscreenThumbnailsEnabled()
        int height = debugFlags.isFullscreenThumbnailsEnabled()
                ? (int) Math.min(width / aspect, minHeight)
                ? (int) Math.min(width / aspect, minHeight)
                : width;
                : width;
@@ -315,7 +363,7 @@ public class TaskStackLayoutAlgorithm {
                mStackRect.left + width, mStackRect.top + height);
                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
        // 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;
            return;
        }
        }


@@ -328,9 +376,7 @@ public class TaskStackLayoutAlgorithm {
        if (DEBUG) {
        if (DEBUG) {
            Log.d(TAG, "initialize");
            Log.d(TAG, "initialize");
            Log.d(TAG, "\tmFreeformRect: " + mFreeformRect);
            Log.d(TAG, "\tmFreeformRect: " + mFreeformRect);
            Log.d(TAG, "\tmFreeformStackRect: " + mFreeformStackRect);
            Log.d(TAG, "\tmStackRect: " + mStackRect);
            Log.d(TAG, "\tmStackRect: " + mStackRect);
            Log.d(TAG, "\tmCurrentStackRect: " + mCurrentStackRect);
            Log.d(TAG, "\tmTaskRect: " + mTaskRect);
            Log.d(TAG, "\tmTaskRect: " + mTaskRect);
            Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
            Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
        }
        }
@@ -386,7 +432,7 @@ public class TaskStackLayoutAlgorithm {
                mMinScrollP = mMaxScrollP = 0;
                mMinScrollP = mMaxScrollP = 0;
            } else {
            } else {
                float bottomOffsetPct = (float) (mStackBottomOffset + mTaskRect.height()) /
                float bottomOffsetPct = (float) (mStackBottomOffset + mTaskRect.height()) /
                        mCurrentStackRect.height();
                        mStackRect.height();
                float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct);
                float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct);
                mMinScrollP = 0;
                mMinScrollP = 0;
                mMaxScrollP = Math.max(mMinScrollP,
                mMaxScrollP = Math.max(mMinScrollP,
@@ -408,7 +454,7 @@ public class TaskStackLayoutAlgorithm {
                    mInitialScrollP = Math.max(mMinScrollP, mNumStackTasks - 2);
                    mInitialScrollP = Math.max(mMinScrollP, mNumStackTasks - 2);
                }
                }
            } else {
            } else {
                float offsetPct = (float) (mTaskRect.height() / 2) / mCurrentStackRect.height();
                float offsetPct = (float) (mTaskRect.height() / 2) / mStackRect.height();
                float normX = mUnfocusedCurveInterpolator.getX(offsetPct);
                float normX = mUnfocusedCurveInterpolator.getX(offsetPct);
                mInitialScrollP = (mNumStackTasks - 1) - mUnfocusedRange.getAbsoluteX(normX);
                mInitialScrollP = (mNumStackTasks - 1) - mUnfocusedRange.getAbsoluteX(normX);
            }
            }
@@ -428,7 +474,7 @@ public class TaskStackLayoutAlgorithm {
    public void updateFocusStateOnScroll(int yMovement) {
    public void updateFocusStateOnScroll(int yMovement) {
        Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator);
        Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator);
        if (mFocusState > STATE_UNFOCUSED) {
        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));
            mFocusState -= Math.min(mFocusState, Math.abs(delta));
        }
        }
    }
    }
@@ -558,7 +604,7 @@ public class TaskStackLayoutAlgorithm {
        float p = mUnfocusedRange.getNormalizedX(taskProgress);
        float p = mUnfocusedRange.getNormalizedX(taskProgress);
        float yp = mUnfocusedCurveInterpolator.getInterpolation(p);
        float yp = mUnfocusedCurveInterpolator.getInterpolation(p);
        float unfocusedP = 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);
        boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
        int focusedY = 0;
        int focusedY = 0;
        boolean focusedVisible = true;
        boolean focusedVisible = true;
@@ -566,7 +612,7 @@ public class TaskStackLayoutAlgorithm {
            mFocusedRange.offset(stackScroll);
            mFocusedRange.offset(stackScroll);
            p = mFocusedRange.getNormalizedX(taskProgress);
            p = mFocusedRange.getNormalizedX(taskProgress);
            yp = mFocusedCurveInterpolator.getInterpolation(p);
            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);
            focusedVisible = mFocusedRange.isInRange(taskProgress);
        }
        }


@@ -583,8 +629,8 @@ public class TaskStackLayoutAlgorithm {
            // When there is exactly one task, then decouple the task from the stack and just move
            // When there is exactly one task, then decouple the task from the stack and just move
            // in screen space
            // in screen space
            p = (mMinScrollP - stackScroll) / mNumStackTasks;
            p = (mMinScrollP - stackScroll) / mNumStackTasks;
            int centerYOffset = (mCurrentStackRect.top - mTaskRect.top) +
            int centerYOffset = (mStackRect.top - mTaskRect.top) +
                    (mCurrentStackRect.height() - mTaskRect.height()) / 2;
                    (mStackRect.height() - mTaskRect.height()) / 2;
            y = centerYOffset + getYForDeltaP(p, 0);
            y = centerYOffset + getYForDeltaP(p, 0);
            z = mMaxTranslationZ;
            z = mMaxTranslationZ;
            relP = 1f;
            relP = 1f;
@@ -592,7 +638,7 @@ public class TaskStackLayoutAlgorithm {
        } else {
        } else {
            // Otherwise, update the task to the stack layout
            // Otherwise, update the task to the stack layout
            y = unFocusedY + (int) (mFocusState * (focusedY - unFocusedY));
            y = unFocusedY + (int) (mFocusState * (focusedY - unFocusedY));
            y += (mCurrentStackRect.top - mTaskRect.top);
            y += (mStackRect.top - mTaskRect.top);
            z = Math.max(mMinTranslationZ, Math.min(mMaxTranslationZ,
            z = Math.max(mMinTranslationZ, Math.min(mMaxTranslationZ,
                    mMinTranslationZ + (p * (mMaxTranslationZ - mMinTranslationZ))));
                    mMinTranslationZ + (p * (mMaxTranslationZ - mMinTranslationZ))));
            relP = unfocusedP;
            relP = unfocusedP;
@@ -600,7 +646,7 @@ public class TaskStackLayoutAlgorithm {


        // Fill out the transform
        // Fill out the transform
        transformOut.scale = 1f;
        transformOut.scale = 1f;
        transformOut.translationX = (mCurrentStackRect.width() - mTaskRect.width()) / 2;
        transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2;
        transformOut.translationY = y;
        transformOut.translationY = y;
        transformOut.translationZ = z;
        transformOut.translationZ = z;
        transformOut.rect.set(mTaskRect);
        transformOut.rect.set(mTaskRect);
@@ -633,7 +679,7 @@ public class TaskStackLayoutAlgorithm {
     * screen along the arc-length proportionally (1/arclength).
     * screen along the arc-length proportionally (1/arclength).
     */
     */
    public float getDeltaPForY(int downY, int y) {
    public float getDeltaPForY(int downY, int y) {
        float deltaP = (float) (y - downY) / mCurrentStackRect.height() *
        float deltaP = (float) (y - downY) / mStackRect.height() *
                mUnfocusedCurveInterpolator.getArcLength();
                mUnfocusedCurveInterpolator.getArcLength();
        return -deltaP;
        return -deltaP;
    }
    }
@@ -643,7 +689,7 @@ public class TaskStackLayoutAlgorithm {
     * of the curve, map back to the screen y.
     * of the curve, map back to the screen y.
     */
     */
    public int getYForDeltaP(float downScrollP, float p) {
    public int getYForDeltaP(float downScrollP, float p) {
        int y = (int) ((p - downScrollP) * mCurrentStackRect.height() *
        int y = (int) ((p - downScrollP) * mStackRect.height() *
                (1f / mUnfocusedCurveInterpolator.getArcLength()));
                (1f / mUnfocusedCurveInterpolator.getArcLength()));
        return -y;
        return -y;
    }
    }
@@ -658,12 +704,12 @@ public class TaskStackLayoutAlgorithm {
        // Initialize the focused curve. This curve is a piecewise curve composed of several
        // 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),
        // 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).
        // (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();
        Path p = new Path();
        p.moveTo(0f, 1f);
        p.moveTo(0f, 1f);
        p.lineTo(0.5f, 1f - peekHeightPct);
        p.lineTo(0.5f, 1f - peekHeightPct);
        p.lineTo(0.66666667f, (float) (taskBarHeight * 3) / mCurrentStackRect.height());
        p.lineTo(0.66666667f, (float) (taskBarHeight * 3) / mStackRect.height());
        p.lineTo(0.83333333f, (float) (taskBarHeight / 2) / mCurrentStackRect.height());
        p.lineTo(0.83333333f, (float) (taskBarHeight / 2) / mStackRect.height());
        p.lineTo(1f, 0f);
        p.lineTo(1f, 0f);
        return p;
        return p;
    }
    }
@@ -680,7 +726,7 @@ public class TaskStackLayoutAlgorithm {
        // there is a tangent at (0.5, peek height offset).
        // there is a tangent at (0.5, peek height offset).
        float cpoint1X = 0.4f;
        float cpoint1X = 0.4f;
        float cpoint1Y = 1f;
        float cpoint1Y = 1f;
        float peekHeightPct = (float) mFocusedPeekHeight / mCurrentStackRect.height();
        float peekHeightPct = (float) mFocusedPeekHeight / mStackRect.height();
        float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
        float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
        float b = 1f - slope * cpoint1X;
        float b = 1f - slope * cpoint1X;
        float cpoint2X = 0.75f;
        float cpoint2X = 0.75f;
Loading