FileDocCategorySizeDatePackage
TaskStackViewLayoutAlgorithm.javaAPI DocAndroid 5.1 API15921Thu Mar 12 22:22:42 GMT 2015com.android.systemui.recents.views

TaskStackViewLayoutAlgorithm

public class TaskStackViewLayoutAlgorithm extends Object

Fields Summary
static final float
StackPeekMinScale
com.android.systemui.recents.RecentsConfiguration
mConfig
android.graphics.Rect
mViewRect
android.graphics.Rect
mStackVisibleRect
android.graphics.Rect
mStackRect
android.graphics.Rect
mTaskRect
float
mMinScrollP
float
mMaxScrollP
float
mInitialScrollP
int
mWithinAffiliationOffset
int
mBetweenAffiliationOffset
HashMap
mTaskProgressMap
static final float
XScale
static final float
LogBase
static final int
PrecisionSteps
static float[]
xp
static float[]
px
Constructors Summary
public TaskStackViewLayoutAlgorithm(com.android.systemui.recents.RecentsConfiguration config)


       
        mConfig = config;

        // Precompute the path
        initializeCurve();
    
Methods Summary
voidcomputeMinMaxScroll(java.util.ArrayList tasks, boolean launchedWithAltTab, boolean launchedFromHome)
Computes the minimum and maximum scroll progress values. This method may be called before the RecentsConfiguration is set, so we need to pass in the alt-tab state.

        // Clear the progress map
        mTaskProgressMap.clear();

        // Return early if we have no tasks
        if (tasks.isEmpty()) {
            mMinScrollP = mMaxScrollP = 0;
            return;
        }

        // Note that we should account for the scale difference of the offsets at the screen bottom
        int taskHeight = mTaskRect.height();
        float pAtBottomOfStackRect = screenYToCurveProgress(mStackVisibleRect.bottom);
        float pWithinAffiliateTop = screenYToCurveProgress(mStackVisibleRect.bottom -
                mWithinAffiliationOffset);
        float scale = curveProgressToScale(pWithinAffiliateTop);
        int scaleYOffset = (int) (((1f - scale) * taskHeight) / 2);
        pWithinAffiliateTop = screenYToCurveProgress(mStackVisibleRect.bottom -
                mWithinAffiliationOffset + scaleYOffset);
        float pWithinAffiliateOffset = pAtBottomOfStackRect - pWithinAffiliateTop;
        float pBetweenAffiliateOffset = pAtBottomOfStackRect -
                screenYToCurveProgress(mStackVisibleRect.bottom - mBetweenAffiliationOffset);
        float pTaskHeightOffset = pAtBottomOfStackRect -
                screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
        float pNavBarOffset = pAtBottomOfStackRect -
                screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom -
                        mStackRect.bottom));

        // Update the task offsets
        float pAtBackMostCardTop = 0.5f;
        float pAtFrontMostCardTop = pAtBackMostCardTop;
        int taskCount = tasks.size();
        for (int i = 0; i < taskCount; i++) {
            Task task = tasks.get(i);
            mTaskProgressMap.put(task.key, pAtFrontMostCardTop);

            if (i < (taskCount - 1)) {
                // Increment the peek height
                float pPeek = task.group.isFrontMostTask(task) ?
                        pBetweenAffiliateOffset : pWithinAffiliateOffset;
                pAtFrontMostCardTop += pPeek;
            }
        }

        mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
        mMinScrollP = tasks.size() == 1 ? Math.max(mMaxScrollP, 0f) : 0f;
        if (launchedWithAltTab && launchedFromHome) {
            // Center the top most task, since that will be focused first
            mInitialScrollP = mMaxScrollP;
        } else {
            mInitialScrollP = pAtFrontMostCardTop - 0.825f;
        }
        mInitialScrollP = Math.min(mMaxScrollP, Math.max(0, mInitialScrollP));
    
public voidcomputeRects(int windowWidth, int windowHeight, android.graphics.Rect taskStackBounds)
Computes the stack and task rects

        // Compute the stack rects
        mViewRect.set(0, 0, windowWidth, windowHeight);
        mStackRect.set(taskStackBounds);
        mStackVisibleRect.set(taskStackBounds);
        mStackVisibleRect.bottom = mViewRect.bottom;

        int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
        int heightPadding = mConfig.taskStackTopPaddingPx;
        mStackRect.inset(widthPadding, heightPadding);

        // Compute the task rect
        int size = mStackRect.width();
        int left = mStackRect.left + (mStackRect.width() - size) / 2;
        mTaskRect.set(left, mStackRect.top,
                left + size, mStackRect.top + size);

        // Update the affiliation offsets
        float visibleTaskPct = 0.5f;
        mWithinAffiliationOffset = mConfig.taskBarHeight;
        mBetweenAffiliationOffset = (int) (visibleTaskPct * mTaskRect.height());
    
public com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm$VisibilityReportcomputeStackVisibilityReport(java.util.ArrayList tasks)
Computes the maximum number of visible tasks and thumbnails. Requires that computeMinMaxScroll() is called first.

        if (tasks.size() <= 1) {
            return new VisibilityReport(1, 1);
        }

        // Walk backwards in the task stack and count the number of tasks and visible thumbnails
        int taskHeight = mTaskRect.height();
        int numVisibleTasks = 1;
        int numVisibleThumbnails = 1;
        float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
        int prevScreenY = curveProgressToScreenY(progress);
        for (int i = tasks.size() - 2; i >= 0; i--) {
            Task task = tasks.get(i);
            progress = mTaskProgressMap.get(task.key) - mInitialScrollP;
            if (progress < 0) {
                break;
            }
            boolean isFrontMostTaskInGroup = task.group.isFrontMostTask(task);
            if (isFrontMostTaskInGroup) {
                float scaleAtP = curveProgressToScale(progress);
                int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
                int screenY = curveProgressToScreenY(progress) + scaleYOffsetAtP;
                boolean hasVisibleThumbnail = (prevScreenY - screenY) > mConfig.taskBarHeight;
                if (hasVisibleThumbnail) {
                    numVisibleThumbnails++;
                    numVisibleTasks++;
                    prevScreenY = screenY;
                } else {
                    // Once we hit the next front most task that does not have a visible thumbnail,
                    // walk through remaining visible set
                    for (int j = i; j >= 0; j--) {
                        numVisibleTasks++;
                        progress = mTaskProgressMap.get(tasks.get(j).key) - mInitialScrollP;
                        if (progress < 0) {
                            break;
                        }
                    }
                    break;
                }
            } else if (!isFrontMostTaskInGroup) {
                // Affiliated task, no thumbnail
                numVisibleTasks++;
            }
        }
        return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
    
floatcurveProgressToScale(float p)
Converts from the progress along the curve to a scale.

        if (p < 0) return StackPeekMinScale;
        if (p > 1) return 1f;
        float scaleRange = (1f - StackPeekMinScale);
        float scale = StackPeekMinScale + (p * scaleRange);
        return scale;
    
intcurveProgressToScreenY(float p)
Converts from the progress along the curve to a screen coordinate.

        if (p < 0 || p > 1) return mStackVisibleRect.top + (int) (p * mStackVisibleRect.height());
        float pIndex = p * PrecisionSteps;
        int pFloorIndex = (int) Math.floor(pIndex);
        int pCeilIndex = (int) Math.ceil(pIndex);
        float xFraction = 0;
        if (pFloorIndex < PrecisionSteps && (pCeilIndex != pFloorIndex)) {
            float pFraction = (pIndex - pFloorIndex) / (pCeilIndex - pFloorIndex);
            xFraction = (xp[pCeilIndex] - xp[pFloorIndex]) * pFraction;
        }
        float x = xp[pFloorIndex] + xFraction;
        return mStackVisibleRect.top + (int) (x * mStackVisibleRect.height());
    
floatgetStackScrollForTask(com.android.systemui.recents.model.Task t)
Returns the scroll to such task top = 1f;

        if (!mTaskProgressMap.containsKey(t.key)) return 0f;
        return mTaskProgressMap.get(t.key);
    
public TaskViewTransformgetStackTransform(com.android.systemui.recents.model.Task task, float stackScroll, TaskViewTransform transformOut, TaskViewTransform prevTransform)
Update/get the transform

        // Return early if we have an invalid index
        if (task == null || !mTaskProgressMap.containsKey(task.key)) {
            transformOut.reset();
            return transformOut;
        }
        return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut,
                prevTransform);
    
public TaskViewTransformgetStackTransform(float taskProgress, float stackScroll, TaskViewTransform transformOut, TaskViewTransform prevTransform)
Update/get the transform

        float pTaskRelative = taskProgress - stackScroll;
        float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
        // If the task top is outside of the bounds below the screen, then immediately reset it
        if (pTaskRelative > 1f) {
            transformOut.reset();
            transformOut.rect.set(mTaskRect);
            return transformOut;
        }
        // The check for the top is trickier, since we want to show the next task if it is at all
        // visible, even if p < 0.
        if (pTaskRelative < 0f) {
            if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
                transformOut.reset();
                transformOut.rect.set(mTaskRect);
                return transformOut;
            }
        }
        float scale = curveProgressToScale(pBounded);
        int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
        int minZ = mConfig.taskViewTranslationZMinPx;
        int maxZ = mConfig.taskViewTranslationZMaxPx;
        transformOut.scale = scale;
        transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
                scaleYOffset;
        transformOut.translationZ = Math.max(minZ, minZ + (pBounded * (maxZ - minZ)));
        transformOut.rect.set(mTaskRect);
        transformOut.rect.offset(0, transformOut.translationY);
        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
        transformOut.visible = true;
        transformOut.p = pTaskRelative;
        return transformOut;
    
public android.graphics.RectgetUntransformedTaskViewSize()
Returns the untransformed task view size.

        Rect tvSize = new Rect(mTaskRect);
        tvSize.offsetTo(0, 0);
        return tvSize;
    
public static voidinitializeCurve()
Initializes the curve.

        if (xp != null && px != null) return;
        xp = new float[PrecisionSteps + 1];
        px = new float[PrecisionSteps + 1];

        // Approximate f(x)
        float[] fx = new float[PrecisionSteps + 1];
        float step = 1f / PrecisionSteps;
        float x = 0;
        for (int xStep = 0; xStep <= PrecisionSteps; xStep++) {
            fx[xStep] = logFunc(x);
            x += step;
        }
        // Calculate the arc length for x:1->0
        float pLength = 0;
        float[] dx = new float[PrecisionSteps + 1];
        dx[0] = 0;
        for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
            dx[xStep] = (float) Math.sqrt(Math.pow(fx[xStep] - fx[xStep - 1], 2) + Math.pow(step, 2));
            pLength += dx[xStep];
        }
        // Approximate p(x), a function of cumulative progress with x, normalized to 0..1
        float p = 0;
        px[0] = 0f;
        px[PrecisionSteps] = 1f;
        for (int xStep = 1; xStep <= PrecisionSteps; xStep++) {
            p += Math.abs(dx[xStep] / pLength);
            px[xStep] = p;
        }
        // Given p(x), calculate the inverse function x(p). This assumes that x(p) is also a valid
        // function.
        int xStep = 0;
        p = 0;
        xp[0] = 0f;
        xp[PrecisionSteps] = 1f;
        for (int pStep = 0; pStep < PrecisionSteps; pStep++) {
            // Walk forward in px and find the x where px <= p && p < px+1
            while (xStep < PrecisionSteps) {
                if (px[xStep] > p) break;
                xStep++;
            }
            // Now, px[xStep-1] <= p < px[xStep]
            if (xStep == 0) {
                xp[pStep] = 0;
            } else {
                // Find x such that proportionally, x is correct
                float fraction = (p - px[xStep - 1]) / (px[xStep] - px[xStep - 1]);
                x = (xStep - 1 + fraction) * step;
                xp[pStep] = x;
            }
            p += step;
        }
    
floatinvLogFunc(float y)
The inverse of the log function describing the curve.

        return (float) (Math.log((1f - reverse(y)) * (LogBase - 1) + 1) / Math.log(LogBase));
    
static floatlogFunc(float x)
The log function describing the curve.

        return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
    
static floatreverse(float x)
Reverses and scales out x.

        return (-x * XScale) + 1;
    
floatscreenYToCurveProgress(int screenY)
Converts from a screen coordinate to the progress along the curve.

        float x = (float) (screenY - mStackVisibleRect.top) / mStackVisibleRect.height();
        if (x < 0 || x > 1) return x;
        float xIndex = x * PrecisionSteps;
        int xFloorIndex = (int) Math.floor(xIndex);
        int xCeilIndex = (int) Math.ceil(xIndex);
        float pFraction = 0;
        if (xFloorIndex < PrecisionSteps && (xCeilIndex != xFloorIndex)) {
            float xFraction = (xIndex - xFloorIndex) / (xCeilIndex - xFloorIndex);
            pFraction = (px[xCeilIndex] - px[xFloorIndex]) * xFraction;
        }
        return px[xFloorIndex] + pFraction;