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

Commit dfaf9a3c authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Adding notion of stack state to the layout."

parents b60206a5 f0d1c44a
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