FileDocCategorySizeDatePackage
PagedView.javaAPI DocAndroid 5.1 API113353Thu Mar 12 22:22:42 GMT 2015com.android.keyguard

PagedView

public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener
An abstraction of the original Workspace which supports browsing through a sequential list of "pages"

Fields Summary
private static final int
WARP_SNAP_DURATION
private static final String
TAG
private static final boolean
DEBUG
private static final boolean
DEBUG_WARP
protected static final int
INVALID_PAGE
private static final int
WARP_PEEK_ANIMATION_DURATION
private static final float
WARP_ANIMATE_AMOUNT
private static final int
MIN_LENGTH_FOR_FLING
protected static final int
PAGE_SNAP_ANIMATION_DURATION
protected static final int
SLOW_PAGE_SNAP_ANIMATION_DURATION
protected static final float
NANOTIME_DIV
private static final float
OVERSCROLL_ACCELERATE_FACTOR
private static final float
OVERSCROLL_DAMP_FACTOR
private static final float
RETURN_TO_ORIGINAL_PAGE_THRESHOLD
private static final float
SIGNIFICANT_MOVE_THRESHOLD
private static final int
FLING_THRESHOLD_VELOCITY
private static final int
MIN_SNAP_VELOCITY
private static final int
MIN_FLING_VELOCITY
private static final boolean
DISABLE_TOUCH_INTERACTION
private static final boolean
DISABLE_TOUCH_SIDE_PAGES
private static final boolean
DISABLE_FLING_TO_DELETE
static final int
AUTOMATIC_PAGE_SPACING
protected int
mFlingThresholdVelocity
protected int
mMinFlingVelocity
protected int
mMinSnapVelocity
protected float
mDensity
protected float
mSmoothingTime
protected float
mTouchX
protected boolean
mFirstLayout
protected int
mCurrentPage
protected int
mChildCountOnLastMeasure
protected int
mNextPage
protected int
mMaxScrollX
protected android.widget.Scroller
mScroller
private VelocityTracker
mVelocityTracker
private float
mParentDownMotionX
private float
mParentDownMotionY
private float
mDownMotionX
private float
mDownMotionY
private float
mDownScrollX
protected float
mLastMotionX
protected float
mLastMotionXRemainder
protected float
mLastMotionY
protected float
mTotalMotionX
private int
mLastScreenCenter
private int[]
mChildOffsets
private int[]
mChildRelativeOffsets
private int[]
mChildOffsetsWithLayoutScale
private String
mDeleteString
protected static final int
TOUCH_STATE_REST
protected static final int
TOUCH_STATE_SCROLLING
protected static final int
TOUCH_STATE_PREV_PAGE
protected static final int
TOUCH_STATE_NEXT_PAGE
protected static final int
TOUCH_STATE_REORDERING
protected static final int
TOUCH_STATE_READY
protected static final float
ALPHA_QUANTIZE_LEVEL
protected static final float
TOUCH_SLOP_SCALE
protected int
mTouchState
protected boolean
mForceScreenScrolled
protected OnLongClickListener
mLongClickListener
protected int
mTouchSlop
private int
mPagingTouchSlop
private int
mMaximumVelocity
private int
mMinimumWidth
protected int
mPageSpacing
protected int
mCellCountX
protected int
mCellCountY
protected boolean
mAllowOverScroll
protected int
mUnboundedScrollX
protected int[]
mTempVisiblePagesRange
protected boolean
mForceDrawAllChildrenNextFrame
protected int
mOverScrollX
protected float
mLayoutScale
protected static final int
INVALID_POINTER
protected int
mActivePointerId
private PageSwitchListener
mPageSwitchListener
protected ArrayList
mDirtyPageContent
protected boolean
mContentIsRefreshable
protected boolean
mFadeInAdjacentScreens
protected boolean
mUsePagingTouchSlop
protected boolean
mDeferScrollUpdate
protected boolean
mIsPageMoving
protected boolean
mIsDataReady
private android.animation.ValueAnimator
mScrollIndicatorAnimator
private View
mScrollIndicator
private int
mScrollIndicatorPaddingLeft
private int
mScrollIndicatorPaddingRight
private boolean
mShouldShowScrollIndicator
private boolean
mShouldShowScrollIndicatorImmediately
protected static final int
sScrollIndicatorFadeInDuration
protected static final int
sScrollIndicatorFadeOutDuration
protected static final int
sScrollIndicatorFlashDuration
private android.graphics.Rect
mViewport
private int
REORDERING_DROP_REPOSITION_DURATION
protected int
REORDERING_REORDER_REPOSITION_DURATION
protected int
REORDERING_ZOOM_IN_OUT_DURATION
private int
REORDERING_SIDE_PAGE_HOVER_TIMEOUT
private float
REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE
private long
REORDERING_DELETE_DROP_TARGET_FADE_DURATION
private float
mMinScale
protected View
mDragView
protected android.animation.AnimatorSet
mZoomInOutAnim
private Runnable
mSidePageHoverRunnable
private int
mSidePageHoverIndex
private boolean
mReorderingStarted
private boolean
mIsReordering
private int
NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT
private int
mPostReorderingPreZoomInRemainingAnimationCount
private Runnable
mPostReorderingPreZoomInRunnable
private boolean
mOnlyAllowEdgeSwipes
private boolean
mDownEventOnEdge
private int
mEdgeSwipeRegionSize
private android.graphics.Matrix
mTmpInvMatrix
private float[]
mTmpPoint
private android.graphics.Rect
mTmpRect
private android.graphics.Rect
mAltTmpRect
private int
FLING_TO_DELETE_FADE_OUT_DURATION
private float
FLING_TO_DELETE_FRICTION
private float
FLING_TO_DELETE_MAX_FLING_DEGREES
protected int
mFlingToDeleteThresholdVelocity
private boolean
mDeferringForDelete
private int
DELETE_SLIDE_IN_SIDE_PAGE_DURATION
private int
DRAG_TO_DELETE_FADE_OUT_DURATION
private View
mDeleteDropTarget
private boolean
mTopAlignPageWhenShrinkingForBouncer
private int
mPageSwapIndex
private int
mPageWarpIndex
private boolean
mWarpPageExposed
private ViewPropertyAnimator
mWarpAnimation
private boolean
mIsCameraEvent
private float
mWarpPeekAmount
private boolean
mOnPageEndWarpCalled
private boolean
mOnPageBeginWarpCalled
Runnable
hideScrollingIndicatorRunnable
android.animation.AnimatorListenerAdapter
mOnScreenAnimationListener
android.animation.AnimatorListenerAdapter
mOffScreenAnimationListener
Constructors Summary
public PagedView(android.content.Context context)


       
            
            
    

       
        this(context, null);
    
public PagedView(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
    
public PagedView(android.content.Context context, android.util.AttributeSet attrs, int defStyle)

        super(context, attrs, defStyle);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.PagedView, defStyle, 0);
        setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
        mScrollIndicatorPaddingLeft =
            a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
        mScrollIndicatorPaddingRight =
                a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
        a.recycle();

        Resources r = getResources();
        mEdgeSwipeRegionSize = r.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
        mTopAlignPageWhenShrinkingForBouncer =
                r.getBoolean(R.bool.kg_top_align_page_shrink_on_bouncer_visible);

        setHapticFeedbackEnabled(false);
        init();
    
Methods Summary
protected voidacceleratedOverScroll(float amount)

        int screenSize = getViewportWidth();

        // We want to reach the max over scroll effect when the user has
        // over scrolled half the size of the screen
        float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);

        if (f == 0) return;

        // Clamp this factor, f, to -1 < f < 1
        if (Math.abs(f) >= 1) {
            f /= Math.abs(f);
        }

        int overScrollAmount = (int) Math.round(f * screenSize);
        if (amount < 0) {
            mOverScrollX = overScrollAmount;
            super.scrollTo(0, getScrollY());
        } else {
            mOverScrollX = mMaxScrollX + overScrollAmount;
            super.scrollTo(mMaxScrollX, getScrollY());
        }
        invalidate();
    
private voidacquireVelocityTrackerAndAddMovement(MotionEvent ev)

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);
    
public voidaddFocusables(java.util.ArrayList views, int direction, int focusableMode)

        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
            getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
        }
        if (direction == View.FOCUS_LEFT) {
            if (mCurrentPage > 0) {
                getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode);
            }
        } else if (direction == View.FOCUS_RIGHT){
            if (mCurrentPage < getPageCount() - 1) {
                getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode);
            }
        }
    
voidanimateDragViewToOriginalPosition()

        if (mDragView != null) {
            AnimatorSet anim = new AnimatorSet();
            anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
            anim.playTogether(
                    ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
                    ObjectAnimator.ofFloat(mDragView, "translationY", 0f));
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    onPostReorderingAnimationCompleted();
                }
            });
            anim.start();
        }
    
private voidanimateWarpPageOffScreen(java.lang.String reason, boolean animate)

        if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOffScreen(" + reason + " anim:" + animate + ")");
        if (mWarpPageExposed) {
            dispatchOnPageEndWarp();
            KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex());
            if (DEBUG_WARP) Log.v(TAG, "moving page off screen: Tx=" + v.getTranslationX());
            AccelerateInterpolator interp = new AccelerateInterpolator(1.5f);
            int totalOffset = getCurrentWarpOffset();
            v.animate().translationX(totalOffset)
                    .setInterpolator(interp)
                    .setDuration(animate ? WARP_PEEK_ANIMATION_DURATION : 0)
                    .setListener(mOffScreenAnimationListener);
        } else {
            if (DEBUG_WARP) Log.e(TAG, "animateWarpPageOffScreen(): not warping", new Exception());
        }
    
private voidanimateWarpPageOnScreen(java.lang.String reason)

        if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOnScreen(" + reason + ")");
        if (!mWarpPageExposed) {
            mWarpPageExposed = true;
            dispatchOnPageBeginWarp();
            KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex());
            if (DEBUG_WARP) Log.v(TAG, "moving page on screen: Tx=" + v.getTranslationX());
            DecelerateInterpolator interp = new DecelerateInterpolator(1.5f);
            int totalOffset = getCurrentWarpOffset();
            v.setTranslationX(totalOffset);
            mWarpAnimation = v.animate();
            mWarpAnimation.translationX(mWarpPeekAmount+totalOffset)
                    .setInterpolator(interp)
                    .setDuration(WARP_PEEK_ANIMATION_DURATION)
                    .setListener(mOnScreenAnimationListener);
        }
    
voidbeginCameraEvent()

        mIsCameraEvent = true;
    
voidboundByReorderablePages(boolean isReordering, int[] range)

        // Do nothing
    
protected voidcancelScrollingIndicatorAnimations()

        if (mScrollIndicatorAnimator != null) {
            mScrollIndicatorAnimator.cancel();
        }
    
private voidcancelWarpAnimation(java.lang.String msg, boolean abortAnimation)


          
        if (DEBUG_WARP) Log.v(TAG, "cancelWarpAnimation(" + msg + ",abort=" + abortAnimation + ")");
        if (abortAnimation) {
            // We're done with the animation and moving to a new page.  Let the scroller
            // take over the animation.
            KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex());
            v.animate().cancel();
            // Make the scroll amount match the current warp position.
            scrollBy(Math.round(-v.getTranslationX()), 0);
            v.setTranslationX(0);
        } else {
            animateWarpPageOffScreen("canceled", true);
        }
    
public voidcomputeScroll()

        computeScrollHelper();
    
protected booleancomputeScrollHelper()

        if (mScroller.computeScrollOffset()) {
            // Don't bother scrolling if the page does not need to be moved
            if (getScrollX() != mScroller.getCurrX()
                || getScrollY() != mScroller.getCurrY()
                || mOverScrollX != mScroller.getCurrX()) {
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            }
            invalidate();
            return true;
        } else if (mNextPage != INVALID_PAGE) {
            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
            mNextPage = INVALID_PAGE;
            notifyPageSwitched();

            // We don't want to trigger a page end moving unless the page has settled
            // and the user has stopped scrolling
            if (mTouchState == TOUCH_STATE_REST) {
                pageEndMoving();
            }

            onPostReorderingAnimationCompleted();
            return true;
        }
        return false;
    
private java.lang.RunnablecreatePostDeleteAnimationRunnable(View dragView)

        return new Runnable() {
            @Override
            public void run() {
                int dragViewIndex = indexOfChild(dragView);

                // For each of the pages around the drag view, animate them from the previous
                // position to the new position in the layout (as a result of the drag view moving
                // in the layout)
                // NOTE: We can make an assumption here because we have side-bound pages that we
                //       will always have pages to animate in from the left
                getVisiblePages(mTempVisiblePagesRange);
                boundByReorderablePages(true, mTempVisiblePagesRange);
                boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]);
                boolean slideFromLeft = (isLastWidgetPage ||
                        dragViewIndex > mTempVisiblePagesRange[0]);

                // Setup the scroll to the correct page before we swap the views
                if (slideFromLeft) {
                    snapToPageImmediately(dragViewIndex - 1);
                }

                int firstIndex = (isLastWidgetPage ? 0 : mTempVisiblePagesRange[0]);
                int lastIndex = Math.min(mTempVisiblePagesRange[1], getPageCount() - 1);
                int lowerIndex = (slideFromLeft ? firstIndex : dragViewIndex + 1 );
                int upperIndex = (slideFromLeft ? dragViewIndex - 1 : lastIndex);
                ArrayList<Animator> animations = new ArrayList<Animator>();
                for (int i = lowerIndex; i <= upperIndex; ++i) {
                    View v = getChildAt(i);
                    // dragViewIndex < pageUnderPointIndex, so after we remove the
                    // drag view all subsequent views to pageUnderPointIndex will
                    // shift down.
                    int oldX = 0;
                    int newX = 0;
                    if (slideFromLeft) {
                        if (i == 0) {
                            // Simulate the page being offscreen with the page spacing
                            oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i)
                                    - mPageSpacing;
                        } else {
                            oldX = getViewportOffsetX() + getChildOffset(i - 1);
                        }
                        newX = getViewportOffsetX() + getChildOffset(i);
                    } else {
                        oldX = getChildOffset(i) - getChildOffset(i - 1);
                        newX = 0;
                    }

                    // Animate the view translation from its old position to its new
                    // position
                    AnimatorSet anim = (AnimatorSet) v.getTag();
                    if (anim != null) {
                        anim.cancel();
                    }

                    // Note: Hacky, but we want to skip any optimizations to not draw completely
                    // hidden views
                    v.setAlpha(Math.max(v.getAlpha(), 0.01f));
                    v.setTranslationX(oldX - newX);
                    anim = new AnimatorSet();
                    anim.playTogether(
                            ObjectAnimator.ofFloat(v, "translationX", 0f),
                            ObjectAnimator.ofFloat(v, "alpha", 1f));
                    animations.add(anim);
                    v.setTag(anim);
                }

                AnimatorSet slideAnimations = new AnimatorSet();
                slideAnimations.playTogether(animations);
                slideAnimations.setDuration(DELETE_SLIDE_IN_SIDE_PAGE_DURATION);
                slideAnimations.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        final Runnable onCompleteRunnable = new Runnable() {
                            @Override
                            public void run() {
                                mDeferringForDelete = false;
                                onEndReordering();
                                onRemoveViewAnimationCompleted();
                            }
                        };
                        zoomIn(onCompleteRunnable);
                    }
                });
                slideAnimations.start();

                removeView(dragView);
                onRemoveView(dragView, true);
            }
        };
    
protected voiddampedOverScroll(float amount)

        int screenSize = getViewportWidth();

        float f = (amount / screenSize);

        if (f == 0) return;
        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));

        // Clamp this factor, f, to -1 < f < 1
        if (Math.abs(f) >= 1) {
            f /= Math.abs(f);
        }

        int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
        if (amount < 0) {
            mOverScrollX = overScrollAmount;
            super.scrollTo(0, getScrollY());
        } else {
            mOverScrollX = mMaxScrollX + overScrollAmount;
            super.scrollTo(mMaxScrollX, getScrollY());
        }
        invalidate();
    
protected booleandetermineScrollingStart(MotionEvent ev)

        // Disallow scrolling if we don't have a valid pointer index
        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
        if (pointerIndex == -1) return false;

        // Disallow scrolling if we started the gesture from outside the viewport
        final float x = ev.getX(pointerIndex);
        final float y = ev.getY(pointerIndex);
        if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return false;

        // If we're only allowing edge swipes, we break out early if the down event wasn't
        // at the edge.
        if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return false;

        final int xDiff = (int) Math.abs(x - mLastMotionX);

        final int touchSlop = Math.round(TOUCH_SLOP_SCALE * mTouchSlop);
        boolean xPaged = xDiff > mPagingTouchSlop;
        boolean xMoved = xDiff > touchSlop;

        return mUsePagingTouchSlop ? xPaged : xMoved;
    
protected voiddispatchDraw(android.graphics.Canvas canvas)

        int halfScreenSize = getViewportWidth() / 2;
        // mOverScrollX is equal to getScrollX() when we're within the normal scroll range.
        // Otherwise it is equal to the scaled overscroll position.
        int screenCenter = mOverScrollX + halfScreenSize;

        if (screenCenter != mLastScreenCenter || mForceScreenScrolled) {
            // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can
            // set it for the next frame
            mForceScreenScrolled = false;
            screenScrolled(screenCenter);
            mLastScreenCenter = screenCenter;
        }

        // Find out which screens are visible; as an optimization we only call draw on them
        final int pageCount = getChildCount();
        if (pageCount > 0) {
            getVisiblePages(mTempVisiblePagesRange);
            final int leftScreen = mTempVisiblePagesRange[0];
            final int rightScreen = mTempVisiblePagesRange[1];
            if (leftScreen != -1 && rightScreen != -1) {
                final long drawingTime = getDrawingTime();
                // Clip to the bounds
                canvas.save();
                canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(),
                        getScrollY() + getBottom() - getTop());

                // Draw all the children, leaving the drag view for last
                for (int i = pageCount - 1; i >= 0; i--) {
                    final View v = getPageAt(i);
                    if (v == mDragView) continue;
                    if (mForceDrawAllChildrenNextFrame ||
                               (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
                        drawChild(canvas, v, drawingTime);
                    }
                }
                // Draw the drag view on top (if there is one)
                if (mDragView != null) {
                    drawChild(canvas, mDragView, drawingTime);
                }

                mForceDrawAllChildrenNextFrame = false;
                canvas.restore();
            }
        }
    
private voiddispatchOnPageBeginWarp()

        if (!mOnPageBeginWarpCalled) {
            onPageBeginWarp();
            mOnPageBeginWarpCalled = true;
        }
        mOnPageEndWarpCalled = false;
    
private voiddispatchOnPageEndWarp()

        if (!mOnPageEndWarpCalled) {
            onPageEndWarp();
            mOnPageEndWarpCalled = true;
        }
        mOnPageBeginWarpCalled = false;
    
public booleandispatchUnhandledMove(View focused, int direction)

        if (direction == View.FOCUS_LEFT) {
            if (getCurrentPage() > 0) {
                snapToPage(getCurrentPage() - 1);
                return true;
            }
        } else if (direction == View.FOCUS_RIGHT) {
            if (getCurrentPage() < getPageCount() - 1) {
                snapToPage(getCurrentPage() + 1);
                return true;
            }
        }
        return super.dispatchUnhandledMove(focused, direction);
    
floatdistanceInfluenceForSnapDuration(float f)

        f -= 0.5f; // center the values about 0.
        f *= 0.3f * Math.PI / 2.0f;
        return (float) Math.sin(f);
    
voidendCameraEvent()

        mIsCameraEvent = false;
    
voidendReordering()

        // For simplicity, we call endReordering sometimes even if reordering was never started.
        // In that case, we don't want to do anything.
        if (!mReorderingStarted) return;
        mReorderingStarted = false;

        // If we haven't flung-to-delete the current child, then we just animate the drag view
        // back into position
        final Runnable onCompleteRunnable = new Runnable() {
            @Override
            public void run() {
                onEndReordering();
            }
        };
        if (!mDeferringForDelete) {
            mPostReorderingPreZoomInRunnable = new Runnable() {
                public void run() {
                    zoomIn(onCompleteRunnable);
                };
            };

            mPostReorderingPreZoomInRemainingAnimationCount =
                    NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT;
            // Snap to the current page
            snapToPage(indexOfChild(mDragView), 0);
            // Animate the drag view back to the front position
            animateDragViewToOriginalPosition();
        } else {
            // Handled in post-delete-animation-callbacks
        }
    
protected voidflashScrollingIndicator(boolean animated)


        
        removeCallbacks(hideScrollingIndicatorRunnable);
        showScrollingIndicator(!animated);
        postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration);
    
public voidfocusableViewAvailable(View focused)
If one of our descendant views decides that it could be focused now, only pass that along if it's on the current page. This happens when live folders requery, and if they're off page, they end up calling requestFocus, which pulls it on page.

        View current = getPageAt(mCurrentPage);
        View v = focused;
        while (true) {
            if (v == current) {
                super.focusableViewAvailable(focused);
                return;
            }
            if (v == this) {
                return;
            }
            ViewParent parent = v.getParent();
            if (parent instanceof View) {
                v = (View)v.getParent();
            } else {
                return;
            }
        }
    
protected floatgetBoundedScrollProgress(int screenCenter, View v, int page)

        final int halfScreenSize = getViewportWidth() / 2;

        screenCenter = Math.min(mScrollX + halfScreenSize, screenCenter);
        screenCenter = Math.max(halfScreenSize,  screenCenter);

        return getScrollProgress(screenCenter, v, page);
    
protected intgetChildIndexForRelativeOffset(int relativeOffset)

        final int childCount = getChildCount();
        int left;
        int right;
        for (int i = 0; i < childCount; ++i) {
            left = getRelativeChildOffset(i);
            right = (left + getScaledMeasuredWidth(getPageAt(i)));
            if (left <= relativeOffset && relativeOffset <= right) {
                return i;
            }
        }
        return -1;
    
protected intgetChildOffset(int index)

        if (index < 0 || index > getChildCount() - 1) return 0;

        int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ?
                mChildOffsets : mChildOffsetsWithLayoutScale;

        if (childOffsets != null && childOffsets[index] != -1) {
            return childOffsets[index];
        } else {
            if (getChildCount() == 0)
                return 0;

            int offset = getRelativeChildOffset(0);
            for (int i = 0; i < index; ++i) {
                offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing;
            }
            if (childOffsets != null) {
                childOffsets[index] = offset;
            }
            return offset;
        }
    
protected intgetChildWidth(int index)

        // This functions are called enough times that it actually makes a difference in the
        // profiler -- so just inline the max() here
        final int measuredWidth = getPageAt(index).getMeasuredWidth();
        final int minWidth = mMinimumWidth;
        return (minWidth > measuredWidth) ? minWidth : measuredWidth;
    
intgetCurrentPage()
Returns the index of the currently displayed page.

return
The index of the currently displayed page.

        return mCurrentPage;
    
private intgetCurrentWarpOffset()

        if (mCurrentPage == getPageWarpIndex()) {
            return 0;
        }
        View viewRight = getPageAt(mCurrentPage + 1);
        View warpView = getPageAt(getPageWarpIndex());
        if (viewRight != warpView && viewRight != null && warpView != null) {
            return viewRight.getLeft() - warpView.getLeft();
        }
        return 0;
    
protected floatgetMaxScrollProgress()

        return 1.0f;
    
intgetNextPage()

        return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
    
ViewgetPageAt(int index)

        return getChildAt(index);
    
intgetPageCount()

        return getChildCount();
    
public intgetPageForView(View v)

        int result = -1;
        if (v != null) {
            ViewParent vp = v.getParent();
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                if (vp == getPageAt(i)) {
                    return i;
                }
            }
        }
        return result;
    
intgetPageNearestToCenterOfScreen()

        int minDistanceFromScreenCenter = Integer.MAX_VALUE;
        int minDistanceFromScreenCenterIndex = -1;
        int screenCenter = getViewportOffsetX() + getScrollX() + (getViewportWidth() / 2);
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; ++i) {
            View layout = (View) getPageAt(i);
            int childWidth = getScaledMeasuredWidth(layout);
            int halfChildWidth = (childWidth / 2);
            int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
                minDistanceFromScreenCenter = distanceFromScreenCenter;
                minDistanceFromScreenCenterIndex = i;
            }
        }
        return minDistanceFromScreenCenterIndex;
    
intgetPageNearestToPoint(float x)

        int index = 0;
        for (int i = 0; i < getChildCount(); ++i) {
            if (x < getChildAt(i).getRight() - getScrollX()) {
                return index;
            } else {
                index++;
            }
        }
        return Math.min(index, getChildCount() - 1);
    
private intgetPageSnapDuration()

        return isWarping() ? WARP_SNAP_DURATION : PAGE_SNAP_ANIMATION_DURATION;
    
protected intgetPageWarpIndex()

        return getPageCount() - 1;
    
protected intgetRelativeChildOffset(int index)

        if (index < 0 || index > getChildCount() - 1) return 0;

        if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) {
            return mChildRelativeOffsets[index];
        } else {
            final int padding = getPaddingLeft() + getPaddingRight();
            final int offset = getPaddingLeft() +
                    (getViewportWidth() - padding - getChildWidth(index)) / 2;
            if (mChildRelativeOffsets != null) {
                mChildRelativeOffsets[index] = offset;
            }
            return offset;
        }
    
protected intgetScaledMeasuredWidth(View child)

        // This functions are called enough times that it actually makes a difference in the
        // profiler -- so just inline the max() here
        final int measuredWidth = child.getMeasuredWidth();
        final int minWidth = mMinimumWidth;
        final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth;
        return (int) (maxWidth * mLayoutScale + 0.5f);
    
protected floatgetScrollProgress(int screenCenter, View v, int page)

        final int halfScreenSize = getViewportWidth() / 2;

        int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing;
        int delta = screenCenter - (getChildOffset(page) -
                getRelativeChildOffset(page) + halfScreenSize);

        float scrollProgress = delta / (totalDistance * 1.0f);
        scrollProgress = Math.min(scrollProgress, getMaxScrollProgress());
        scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress());
        return scrollProgress;
    
protected ViewgetScrollingIndicator()

    

       
        return null;
    
intgetViewportHeight()

        return mViewport.height();
    
intgetViewportOffsetX()

        return (getMeasuredWidth() - getViewportWidth()) / 2;
    
intgetViewportOffsetY()

        return (getMeasuredHeight() - getViewportHeight()) / 2;
    
intgetViewportWidth()

        return mViewport.width();
    
protected voidgetVisiblePages(int[] range)

        range[0] = 0;
        range[1] = getPageCount() - 1;

        /*
        final int pageCount = getChildCount();

        if (pageCount > 0) {
            final int screenWidth = getViewportWidth();
            int leftScreen = 0;
            int rightScreen = 0;
            int offsetX = getViewportOffsetX() + getScrollX();
            View currPage = getPageAt(leftScreen);
            while (leftScreen < pageCount - 1 &&
                    currPage.getX() + currPage.getWidth() -
                    currPage.getPaddingRight() < offsetX) {
                leftScreen++;
                currPage = getPageAt(leftScreen);
            }
            rightScreen = leftScreen;
            currPage = getPageAt(rightScreen + 1);
            while (rightScreen < pageCount - 1 &&
                    currPage.getX() - currPage.getPaddingLeft() < offsetX + screenWidth) {
                rightScreen++;
                currPage = getPageAt(rightScreen + 1);
            }

            // TEMP: this is a hacky way to ensure that animations to new pages are not clipped
            // because we don't draw them while scrolling?
            range[0] = Math.max(0, leftScreen - 1);
            range[1] = Math.min(rightScreen + 1, getChildCount() - 1);
        } else {
            range[0] = -1;
            range[1] = -1;
        }
        */
    
protected booleanhasElasticScrollIndicator()
To be overridden by subclasses to determine whether the scroll indicator should stretch to fill its space on the track or not.

        return true;
    
protected voidhideScrollingIndicator(boolean immediately)

        if (getChildCount() <= 1) return;
        if (!isScrollingIndicatorEnabled()) return;

        getScrollingIndicator();
        if (mScrollIndicator != null) {
            // Fade the indicator out
            updateScrollingIndicatorPosition();
            cancelScrollingIndicatorAnimations();
            if (immediately) {
                mScrollIndicator.setVisibility(View.INVISIBLE);
                mScrollIndicator.setAlpha(0f);
            } else {
                mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f);
                mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration);
                mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
                    private boolean cancelled = false;
                    @Override
                    public void onAnimationCancel(android.animation.Animator animation) {
                        cancelled = true;
                    }
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        if (!cancelled) {
                            mScrollIndicator.setVisibility(View.INVISIBLE);
                        }
                    }
                });
                mScrollIndicatorAnimator.start();
            }
        }
    
protected booleanhitsNextPage(float x, float y)
Return true if a tap at (x, y) should trigger a flip to the next page.

        return  (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
    
protected booleanhitsPreviousPage(float x, float y)
Return true if a tap at (x, y) should trigger a flip to the previous page.

        return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing);
    
protected intindexToPage(int index)

        return index;
    
protected voidinit()
Initializes various states for this workspace.

        mDirtyPageContent = new ArrayList<Boolean>();
        mDirtyPageContent.ensureCapacity(32);
        mScroller = new Scroller(getContext(), new ScrollInterpolator());
        mCurrentPage = 0;

        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = configuration.getScaledTouchSlop();
        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
        mDensity = getResources().getDisplayMetrics().density;
        mWarpPeekAmount = mDensity * WARP_ANIMATE_AMOUNT;

        // Scale the fling-to-delete threshold by the density
        mFlingToDeleteThresholdVelocity = (int) (mFlingToDeleteThresholdVelocity * mDensity);

        mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
        mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
        mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
        setOnHierarchyChangeListener(this);
    
protected voidinvalidateCachedOffsets()

        int count = getChildCount();
        if (count == 0) {
            mChildOffsets = null;
            mChildRelativeOffsets = null;
            mChildOffsetsWithLayoutScale = null;
            return;
        }

        mChildOffsets = new int[count];
        mChildRelativeOffsets = new int[count];
        mChildOffsetsWithLayoutScale = new int[count];
        for (int i = 0; i < count; i++) {
            mChildOffsets[i] = -1;
            mChildRelativeOffsets[i] = -1;
            mChildOffsetsWithLayoutScale[i] = -1;
        }
    
private booleanisAnimatingWarpPage()

        return mWarpAnimation != null;
    
protected booleanisDataReady()

        return mIsDataReady;
    
private android.graphics.PointFisFlingingToDelete()

        ViewConfiguration config = ViewConfiguration.get(getContext());
        mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());

        if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
            // Do a quick dot product test to ensure that we are flinging upwards
            PointF vel = new PointF(mVelocityTracker.getXVelocity(),
                    mVelocityTracker.getYVelocity());
            PointF upVec = new PointF(0f, -1f);
            float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
                    (vel.length() * upVec.length()));
            if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) {
                return vel;
            }
        }
        return null;
    
private booleanisHorizontalCameraScroll(MotionEvent ev)

        // Disallow scrolling if we don't have a valid pointer index
        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
        if (pointerIndex == -1) return false;

        // If we're only allowing edge swipes, we break out early if the down event wasn't
        // at the edge.
        if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return false;

        final float x = ev.getX(pointerIndex);
        final int xDiff = (int) Math.abs(x - mDownMotionX);

        final int touchSlop = Math.round(TOUCH_SLOP_SCALE * mTouchSlop);
        boolean xPaged = xDiff > mPagingTouchSlop;
        boolean xMoved = xDiff > touchSlop;

        return mIsCameraEvent && (mUsePagingTouchSlop ? xPaged : xMoved);
    
private booleanisHoveringOverDeleteDropTarget(int x, int y)

        if (mDeleteDropTarget != null) {
            mAltTmpRect.set(0, 0, 0, 0);
            View parent = (View) mDeleteDropTarget.getParent();
            if (parent != null) {
                parent.getGlobalVisibleRect(mAltTmpRect);
            }
            mDeleteDropTarget.getGlobalVisibleRect(mTmpRect);
            mTmpRect.offset(-mAltTmpRect.left, -mAltTmpRect.top);
            return mTmpRect.contains(x, y);
        }
        return false;
    
protected booleanisPageMoving()

        return mIsPageMoving;
    
booleanisReordering(boolean testTouchState)

        boolean state = mIsReordering;
        if (testTouchState) {
            state &= (mTouchState == TOUCH_STATE_REORDERING);
        }
        return state;
    
protected booleanisScrollingIndicatorEnabled()

        return false;
    
private booleanisTouchPointInCurrentPage(int x, int y)
Returns whether x and y originated within the current page view bounds

        View v = getPageAt(getCurrentPage());
        if (v != null) {
            mTmpRect.set((v.getLeft() - getScrollX()), 0, (v.getRight() - getScrollX()),
                    v.getBottom());
            return mTmpRect.contains(x, y);
        }
        return false;
    
private booleanisTouchPointInViewportWithBuffer(int x, int y)
Returns whether x and y originated within the buffered viewport

        mTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top,
                mViewport.right + mViewport.width() / 2, mViewport.bottom);
        return mTmpRect.contains(x, y);
    
protected booleanisWarping()

        return mWarpPageExposed;
    
float[]mapPointFromParentToView(View v, float x, float y)

        mTmpPoint[0] = x - v.getLeft();
        mTmpPoint[1] = y - v.getTop();
        v.getMatrix().invert(mTmpInvMatrix);
        mTmpInvMatrix.mapPoints(mTmpPoint);
        return mTmpPoint;
    
float[]mapPointFromViewToParent(View v, float x, float y)

        mTmpPoint[0] = x;
        mTmpPoint[1] = y;
        v.getMatrix().mapPoints(mTmpPoint);
        mTmpPoint[0] += v.getLeft();
        mTmpPoint[1] += v.getTop();
        return mTmpPoint;
    
protected floatmaxOverScroll()

        // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
        // exceed). Used to find out how much extra wallpaper we need for the over scroll effect
        float f = 1.0f;
        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
        return OVERSCROLL_DAMP_FACTOR * f;
    
protected voidnotifyPageSwitched()

        if (mPageSwitchListener != null) {
            mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage);
        }
    
protected voidnotifyPageSwitching(int whichPage)

        if (mPageSwitchListener != null) {
            mPageSwitchListener.onPageSwitching(getPageAt(whichPage), whichPage);
        }
    
public abstract voidonAddView(View v, int index)

public voidonChildViewAdded(View parent, View child)

        // This ensures that when children are added, they get the correct transforms / alphas
        // in accordance with any scroll effects.
        mForceScreenScrolled = true;
        invalidate();
        invalidateCachedOffsets();
    
public voidonChildViewRemoved(View parent, View child)

        mForceScreenScrolled = true;
        invalidate();
        invalidateCachedOffsets();
    
private voidonDropToDelete()

        final View dragView = mDragView;

        final float toScale = 0f;
        final float toAlpha = 0f;

        // Create and start the complex animation
        ArrayList<Animator> animations = new ArrayList<Animator>();
        AnimatorSet motionAnim = new AnimatorSet();
        motionAnim.setInterpolator(new DecelerateInterpolator(2));
        motionAnim.playTogether(
                ObjectAnimator.ofFloat(dragView, "scaleX", toScale),
                ObjectAnimator.ofFloat(dragView, "scaleY", toScale));
        animations.add(motionAnim);

        AnimatorSet alphaAnim = new AnimatorSet();
        alphaAnim.setInterpolator(new LinearInterpolator());
        alphaAnim.playTogether(
                ObjectAnimator.ofFloat(dragView, "alpha", toAlpha));
        animations.add(alphaAnim);

        mDeleteString = getContext().getResources()
                .getString(R.string.keyguard_accessibility_widget_deleted,
                        mDragView.getContentDescription());
        final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);

        AnimatorSet anim = new AnimatorSet();
        anim.playTogether(animations);
        anim.setDuration(DRAG_TO_DELETE_FADE_OUT_DURATION);
        anim.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator animation) {
                onAnimationEndRunnable.run();
            }
        });
        anim.start();

        mDeferringForDelete = true;
    
protected voidonEndReordering()

        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            if (mDeleteString != null) {
                announceForAccessibility(mDeleteString);
                mDeleteString = null;
            } else {
                announceForAccessibility(mContext.getString(
                        R.string.keyguard_accessibility_widget_reorder_end));
            }
        }
        mIsReordering = false;

        // Mark all the non-widget pages as visible again
        getVisiblePages(mTempVisiblePagesRange);
        boundByReorderablePages(true, mTempVisiblePagesRange);
        for (int i = 0; i < getPageCount(); ++i) {
            if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
                getPageAt(i).setAlpha(1f);
            }
        }
    
public voidonFlingToDelete(android.graphics.PointF vel)

        final long startTime = AnimationUtils.currentAnimationTimeMillis();

        // NOTE: Because it takes time for the first frame of animation to actually be
        // called and we expect the animation to be a continuation of the fling, we have
        // to account for the time that has elapsed since the fling finished.  And since
        // we don't have a startDelay, we will always get call to update when we call
        // start() (which we want to ignore).
        final TimeInterpolator tInterpolator = new TimeInterpolator() {
            private int mCount = -1;
            private long mStartTime;
            private float mOffset;
            /* Anonymous inner class ctor */ {
                mStartTime = startTime;
            }

            @Override
            public float getInterpolation(float t) {
                if (mCount < 0) {
                    mCount++;
                } else if (mCount == 0) {
                    mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
                            mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION);
                    mCount++;
                }
                return Math.min(1f, mOffset + t);
            }
        };

        final Rect from = new Rect();
        final View dragView = mDragView;
        from.left = (int) dragView.getTranslationX();
        from.top = (int) dragView.getTranslationY();
        AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel,
                from, startTime, FLING_TO_DELETE_FRICTION);

        mDeleteString = getContext().getResources()
                .getString(R.string.keyguard_accessibility_widget_deleted,
                        mDragView.getContentDescription());
        final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);

        // Create and start the animation
        ValueAnimator mDropAnim = new ValueAnimator();
        mDropAnim.setInterpolator(tInterpolator);
        mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION);
        mDropAnim.setFloatValues(0f, 1f);
        mDropAnim.addUpdateListener(updateCb);
        mDropAnim.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator animation) {
                onAnimationEndRunnable.run();
            }
        });
        mDropAnim.start();
        mDeferringForDelete = true;
    
public booleanonGenericMotionEvent(MotionEvent event)

        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_SCROLL: {
                    // Handle mouse (or ext. device) by shifting the page depending on the scroll
                    final float vscroll;
                    final float hscroll;
                    if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
                        vscroll = 0;
                        hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
                    } else {
                        vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
                        hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
                    }
                    if (hscroll != 0 || vscroll != 0) {
                        if (hscroll > 0 || vscroll > 0) {
                            scrollRight();
                        } else {
                            scrollLeft();
                        }
                        return true;
                    }
                }
            }
        }
        return super.onGenericMotionEvent(event);
    
public booleanonHoverEvent(android.view.MotionEvent event)

        return true;
    
public voidonInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event)

        super.onInitializeAccessibilityEvent(event);
        event.setScrollable(true);
        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
            event.setFromIndex(mCurrentPage);
            event.setToIndex(mCurrentPage);
            event.setItemCount(getChildCount());
        }
    
public voidonInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo info)

        super.onInitializeAccessibilityNodeInfo(info);
        info.setScrollable(getPageCount() > 1);
        if (getCurrentPage() < getPageCount() - 1) {
            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
        }
        if (getCurrentPage() > 0) {
            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
        }
    
public booleanonInterceptTouchEvent(MotionEvent ev)

        if (DISABLE_TOUCH_INTERACTION) {
            return false;
        }

        /*
         * This method JUST determines whether we want to intercept the motion.
         * If we return true, onTouchEvent will be called and we do the actual
         * scrolling there.
         */
        acquireVelocityTrackerAndAddMovement(ev);

        // Skip touch handling if there are no pages to swipe
        if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);

        /*
         * Shortcut the most recurring case: the user is in the dragging
         * state and he is moving his finger.  We want to intercept this
         * motion.
         */
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) &&
                (mTouchState == TOUCH_STATE_SCROLLING)) {
            return true;
        }

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                /*
                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                 * whether the user has moved far enough from his original down touch.
                 */
                if (mActivePointerId != INVALID_POINTER) {
                    if (mIsCameraEvent || determineScrollingStart(ev)) {
                        startScrolling(ev);
                    }
                    break;
                }
                // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
                // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
                // i.e. fall through to the next case (don't break)
                // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
                // while it's small- this was causing a crash before we checked for INVALID_POINTER)

                break;
            }

            case MotionEvent.ACTION_DOWN: {
                if (mIsCameraEvent) {
                    animateWarpPageOnScreen("interceptTouch(): DOWN");
                }
                // Remember where the motion event started
                saveDownState(ev);

                /*
                 * If being flinged and user touches the screen, initiate drag;
                 * otherwise don't.  mScroller.isFinished should be false when
                 * being flinged.
                 */
                final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
                final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
                if (finishedScrolling) {
                    setTouchState(TOUCH_STATE_REST);
                    mScroller.abortAnimation();
                } else {
                    if (mIsCameraEvent || isTouchPointInViewportWithBuffer(
                            (int) mDownMotionX, (int) mDownMotionY)) {
                        setTouchState(TOUCH_STATE_SCROLLING);
                    } else {
                        setTouchState(TOUCH_STATE_REST);
                    }
                }

                // check if this can be the beginning of a tap on the side of the pages
                // to scroll the current page
                if (!DISABLE_TOUCH_SIDE_PAGES) {
                    if (mTouchState != TOUCH_STATE_PREV_PAGE
                            && mTouchState != TOUCH_STATE_NEXT_PAGE) {
                        if (getChildCount() > 0) {
                            float x = ev.getX();
                            float y = ev.getY();
                            if (hitsPreviousPage(x, y)) {
                                setTouchState(TOUCH_STATE_PREV_PAGE);
                            } else if (hitsNextPage(x, y)) {
                                setTouchState(TOUCH_STATE_NEXT_PAGE);
                            }
                        }
                    }
                }
                break;
            }

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                resetTouchState();
                // Just intercept the touch event on up if we tap outside the strict viewport
                if (!isTouchPointInCurrentPage((int) mLastMotionX, (int) mLastMotionY)) {
                    return true;
                }
                break;

            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                releaseVelocityTracker();
                break;
        }

        /*
         * The only time we want to intercept motion events is if we are in the
         * drag mode.
         */
        return mTouchState != TOUCH_STATE_REST;
    
protected voidonLayout(boolean changed, int left, int top, int right, int bottom)

        if (!mIsDataReady || getChildCount() == 0) {
            return;
        }

        if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
        final int childCount = getChildCount();

        int offsetX = getViewportOffsetX();
        int offsetY = getViewportOffsetY();

        // Update the viewport offsets
        mViewport.offset(offsetX,  offsetY);

        int childLeft = offsetX + getRelativeChildOffset(0);
        for (int i = 0; i < childCount; i++) {
            final View child = getPageAt(i);
            int childTop = offsetY + getPaddingTop();
            if (child.getVisibility() != View.GONE) {
                final int childWidth = getScaledMeasuredWidth(child);
                final int childHeight = child.getMeasuredHeight();

                if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
                child.layout(childLeft, childTop,
                        childLeft + child.getMeasuredWidth(), childTop + childHeight);
                childLeft += childWidth + mPageSpacing;
            }
        }

        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
            setHorizontalScrollBarEnabled(false);
            updateCurrentPageScroll();
            setHorizontalScrollBarEnabled(true);
            mFirstLayout = false;
        }
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        if (!mIsDataReady || getChildCount() == 0) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        // We measure the dimensions of the PagedView to be larger than the pages so that when we
        // zoom out (and scale down), the view is still contained in the parent
        View parent = (View) getParent();
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
        // viewport, we can be at most one and a half screens offset once we scale down
        DisplayMetrics dm = getResources().getDisplayMetrics();
        int maxSize = Math.max(dm.widthPixels, dm.heightPixels);
        int parentWidthSize = (int) (1.5f * maxSize);
        int parentHeightSize = maxSize;
        int scaledWidthSize = (int) (parentWidthSize / mMinScale);
        int scaledHeightSize = (int) (parentHeightSize / mMinScale);
        mViewport.set(0, 0, widthSize, heightSize);

        if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        // Return early if we aren't given a proper dimension
        if (widthSize <= 0 || heightSize <= 0) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        /* Allow the height to be set as WRAP_CONTENT. This allows the particular case
         * of the All apps view on XLarge displays to not take up more space then it needs. Width
         * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
         * each page to have the same width.
         */
        final int verticalPadding = getPaddingTop() + getPaddingBottom();
        final int horizontalPadding = getPaddingLeft() + getPaddingRight();

        // The children are given the same width and height as the workspace
        // unless they were set to WRAP_CONTENT
        if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
        if (DEBUG) Log.d(TAG, "PagedView.scaledSize: " + scaledWidthSize + ", " + scaledHeightSize);
        if (DEBUG) Log.d(TAG, "PagedView.parentSize: " + parentWidthSize + ", " + parentHeightSize);
        if (DEBUG) Log.d(TAG, "PagedView.horizontalPadding: " + horizontalPadding);
        if (DEBUG) Log.d(TAG, "PagedView.verticalPadding: " + verticalPadding);
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            // disallowing padding in paged view (just pass 0)
            final View child = getPageAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            int childWidthMode;
            if (lp.width == LayoutParams.WRAP_CONTENT) {
                childWidthMode = MeasureSpec.AT_MOST;
            } else {
                childWidthMode = MeasureSpec.EXACTLY;
            }

            int childHeightMode;
            if (lp.height == LayoutParams.WRAP_CONTENT) {
                childHeightMode = MeasureSpec.AT_MOST;
            } else {
                childHeightMode = MeasureSpec.EXACTLY;
            }

            final int childWidthMeasureSpec =
                MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode);
            final int childHeightMeasureSpec =
                MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);

            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
        setMeasuredDimension(scaledWidthSize, scaledHeightSize);

        // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions.
        // We also wait until we set the measured dimensions before flushing the cache as well, to
        // ensure that the cache is filled with good values.
        invalidateCachedOffsets();

        if (mChildCountOnLastMeasure != getChildCount() && !mDeferringForDelete) {
            setCurrentPage(mCurrentPage);
        }
        mChildCountOnLastMeasure = getChildCount();

        if (childCount > 0) {
            if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getViewportWidth() + ", "
                    + getChildWidth(0));

            // Calculate the variable page spacing if necessary
            if (mPageSpacing == AUTOMATIC_PAGE_SPACING) {
                // The gap between pages in the PagedView should be equal to the gap from the page
                // to the edge of the screen (so it is not visible in the current screen).  To
                // account for unequal padding on each side of the paged view, we take the maximum
                // of the left/right gap and use that as the gap between each page.
                int offset = getRelativeChildOffset(0);
                int spacing = Math.max(offset, widthSize - offset -
                        getChildAt(0).getMeasuredWidth());
                setPageSpacing(spacing);
            }
        }

        updateScrollingIndicatorPosition();

        if (childCount > 0) {
            mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
        } else {
            mMaxScrollX = 0;
        }
    
protected voidonPageBeginMoving()

    
public voidonPageBeginWarp()


    
protected voidonPageEndMoving()

    
public voidonPageEndWarp()


    
private voidonPostReorderingAnimationCompleted()

        // Trigger the callback when reordering has settled
        --mPostReorderingPreZoomInRemainingAnimationCount;
        if (mPostReorderingPreZoomInRunnable != null &&
                mPostReorderingPreZoomInRemainingAnimationCount == 0) {
            mPostReorderingPreZoomInRunnable.run();
            mPostReorderingPreZoomInRunnable = null;
        }
    
public abstract voidonRemoveView(View v, boolean deletePermanently)

public abstract voidonRemoveViewAnimationCompleted()

protected booleanonRequestFocusInDescendants(int direction, android.graphics.Rect previouslyFocusedRect)

        int focusablePage;
        if (mNextPage != INVALID_PAGE) {
            focusablePage = mNextPage;
        } else {
            focusablePage = mCurrentPage;
        }
        View v = getPageAt(focusablePage);
        if (v != null) {
            return v.requestFocus(direction, previouslyFocusedRect);
        }
        return false;
    
private voidonSecondaryPointerUp(MotionEvent ev)

        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
            // This was our active pointer going up. Choose a new
            // active pointer and adjust accordingly.
            // TODO: Make this decision more intelligent.
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
            mLastMotionY = ev.getY(newPointerIndex);
            mLastMotionXRemainder = 0;
            mActivePointerId = ev.getPointerId(newPointerIndex);
            if (mVelocityTracker != null) {
                mVelocityTracker.clear();
            }
        }
    
protected voidonStartReordering()

        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            announceForAccessibility(mContext.getString(
                    R.string.keyguard_accessibility_widget_reorder_start));
        }

        // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
        setTouchState(TOUCH_STATE_REORDERING);
        mIsReordering = true;

        // Mark all the non-widget pages as invisible
        getVisiblePages(mTempVisiblePagesRange);
        boundByReorderablePages(true, mTempVisiblePagesRange);
        for (int i = 0; i < getPageCount(); ++i) {
            if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
                getPageAt(i).setAlpha(0f);
            }
        }

        // We must invalidate to trigger a redraw to update the layers such that the drag view
        // is always drawn on top
        invalidate();
    
public booleanonTouchEvent(MotionEvent ev)

        if (DISABLE_TOUCH_INTERACTION) {
            return false;
        }

        // Skip touch handling if there are no pages to swipe
        if (getChildCount() <= 0) return super.onTouchEvent(ev);

        acquireVelocityTrackerAndAddMovement(ev);

        final int action = ev.getAction();

        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            /*
             * If being flinged and user touches, stop the fling. isFinished
             * will be false if being flinged.
             */
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            }

            // Remember where the motion event started
            saveDownState(ev);

            if (mTouchState == TOUCH_STATE_SCROLLING) {
                pageBeginMoving();
            } else {
                setTouchState(TOUCH_STATE_READY);
            }

            if (mIsCameraEvent) {
                animateWarpPageOnScreen("onTouch(): DOWN");
            }
            break;

        case MotionEvent.ACTION_MOVE:
            if (mTouchState == TOUCH_STATE_SCROLLING) {
                // Scroll to follow the motion event
                final int pointerIndex = ev.findPointerIndex(mActivePointerId);

                if (pointerIndex == -1) return true;

                final float x = ev.getX(pointerIndex);
                final float deltaX = mLastMotionX + mLastMotionXRemainder - x;

                mTotalMotionX += Math.abs(deltaX);

                // Only scroll and update mLastMotionX if we have moved some discrete amount.  We
                // keep the remainder because we are actually testing if we've moved from the last
                // scrolled position (which is discrete).
                if (Math.abs(deltaX) >= 1.0f) {
                    mTouchX += deltaX;
                    mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
                    if (isWarping()) {
                        KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex());
                        v.setTranslationX(v.getTranslationX() - deltaX);
                    } else if (!mDeferScrollUpdate) {
                        scrollBy((int) deltaX, 0);
                        if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX);
                    } else {
                        invalidate();
                    }
                    mLastMotionX = x;
                    mLastMotionXRemainder = deltaX - (int) deltaX;
                } else {
                    awakenScrollBars();
                }
            } else if (mTouchState == TOUCH_STATE_REORDERING) {
                // Update the last motion position
                mLastMotionX = ev.getX();
                mLastMotionY = ev.getY();

                // Update the parent down so that our zoom animations take this new movement into
                // account
                float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
                mParentDownMotionX = pt[0];
                mParentDownMotionY = pt[1];
                updateDragViewTranslationDuringDrag();

                // Find the closest page to the touch point
                final int dragViewIndex = indexOfChild(mDragView);
                int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE *
                    getViewportWidth());
                int leftBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.left, 0)[0]
                        + bufferSize);
                int rightBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.right, 0)[0]
                        - bufferSize);

                // Change the drag view if we are hovering over the drop target
                boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget(
                        (int) mParentDownMotionX, (int) mParentDownMotionY);
                setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete);

                if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge);
                if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge);
                if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
                if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
                if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
                if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);

                float parentX = mParentDownMotionX;
                int pageIndexToSnapTo = -1;
                if (parentX < leftBufferEdge && dragViewIndex > 0) {
                    pageIndexToSnapTo = dragViewIndex - 1;
                } else if (parentX > rightBufferEdge && dragViewIndex < getChildCount() - 1) {
                    pageIndexToSnapTo = dragViewIndex + 1;
                }

                final int pageUnderPointIndex = pageIndexToSnapTo;
                if (pageUnderPointIndex > -1 && !isHoveringOverDelete) {
                    mTempVisiblePagesRange[0] = 0;
                    mTempVisiblePagesRange[1] = getPageCount() - 1;
                    boundByReorderablePages(true, mTempVisiblePagesRange);
                    if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
                            pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
                            pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
                        mSidePageHoverIndex = pageUnderPointIndex;
                        mSidePageHoverRunnable = new Runnable() {
                            @Override
                            public void run() {
                                // Update the down scroll position to account for the fact that the
                                // current page is moved
                                mDownScrollX = getChildOffset(pageUnderPointIndex)
                                        - getRelativeChildOffset(pageUnderPointIndex);

                                // Setup the scroll to the correct page before we swap the views
                                snapToPage(pageUnderPointIndex);

                                // For each of the pages between the paged view and the drag view,
                                // animate them from the previous position to the new position in
                                // the layout (as a result of the drag view moving in the layout)
                                int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1;
                                int lowerIndex = (dragViewIndex < pageUnderPointIndex) ?
                                        dragViewIndex + 1 : pageUnderPointIndex;
                                int upperIndex = (dragViewIndex > pageUnderPointIndex) ?
                                        dragViewIndex - 1 : pageUnderPointIndex;
                                for (int i = lowerIndex; i <= upperIndex; ++i) {
                                    View v = getChildAt(i);
                                    // dragViewIndex < pageUnderPointIndex, so after we remove the
                                    // drag view all subsequent views to pageUnderPointIndex will
                                    // shift down.
                                    int oldX = getViewportOffsetX() + getChildOffset(i);
                                    int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta);

                                    // Animate the view translation from its old position to its new
                                    // position
                                    AnimatorSet anim = (AnimatorSet) v.getTag();
                                    if (anim != null) {
                                        anim.cancel();
                                    }

                                    v.setTranslationX(oldX - newX);
                                    anim = new AnimatorSet();
                                    anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
                                    anim.playTogether(
                                            ObjectAnimator.ofFloat(v, "translationX", 0f));
                                    anim.start();
                                    v.setTag(anim);
                                }

                                removeView(mDragView);
                                onRemoveView(mDragView, false);
                                addView(mDragView, pageUnderPointIndex);
                                onAddView(mDragView, pageUnderPointIndex);
                                mSidePageHoverIndex = -1;
                            }
                        };
                        postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
                    }
                } else {
                    removeCallbacks(mSidePageHoverRunnable);
                    mSidePageHoverIndex = -1;
                }
            } else if (determineScrollingStart(ev)) {
                startScrolling(ev);
            } else if (isHorizontalCameraScroll(ev)) {
                startScrolling(ev);
                // we need to cancel the camera animation
                KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex());
                v.animate().cancel();
            }
            break;

        case MotionEvent.ACTION_UP:
            if (mTouchState == TOUCH_STATE_SCROLLING) {
                final int activePointerId = mActivePointerId;
                final int pointerIndex = ev.findPointerIndex(activePointerId);

                if (pointerIndex == -1) return true;

                final float x = ev.getX(pointerIndex);
                final VelocityTracker velocityTracker = mVelocityTracker;
                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
                final int deltaX = (int) (x - mDownMotionX);
                final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage));
                boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
                        SIGNIFICANT_MOVE_THRESHOLD;

                mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);

                boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
                        Math.abs(velocityX) > mFlingThresholdVelocity;

                // In the case that the page is moved far to one direction and then is flung
                // in the opposite direction, we use a threshold to determine whether we should
                // just return to the starting page, or if we should skip one further.
                boolean returnToOriginalPage = false;
                if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
                        Math.signum(velocityX) != Math.signum(deltaX) && isFling) {
                    returnToOriginalPage = true;
                }

                int finalPage;
                // We give flings precedence over large moves, which is why we short-circuit our
                // test for a large move if a fling has been registered. That is, a large
                // move to the left and fling to the right will register as a fling to the right.
                if (((isSignificantMove && deltaX > 0 && !isFling) ||
                        (isFling && velocityX > 0)) && mCurrentPage > 0) {
                    finalPage = returnToOriginalPage || isWarping()
                            ? mCurrentPage : mCurrentPage - 1;
                    snapToPageWithVelocity(finalPage, velocityX);
                } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
                        (isFling && velocityX < 0)) &&
                        mCurrentPage < getChildCount() - 1) {
                    finalPage = returnToOriginalPage ? mCurrentPage :
                            isWarping() ? getPageWarpIndex() : mCurrentPage + 1;
                    snapToPageWithVelocity(finalPage, velocityX);
                } else {
                    snapToDestination();
                }
            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
                // at this point we have not moved beyond the touch slop
                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
                // we can just page
                int nextPage = Math.max(0, mCurrentPage - 1);
                if (nextPage != mCurrentPage) {
                    snapToPage(nextPage);
                } else {
                    snapToDestination();
                }
            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
                // at this point we have not moved beyond the touch slop
                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
                // we can just page
                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
                if (nextPage != mCurrentPage) {
                    snapToPage(nextPage);
                } else {
                    snapToDestination();
                }
            } else if (mTouchState == TOUCH_STATE_REORDERING) {
                // Update the last motion position
                mLastMotionX = ev.getX();
                mLastMotionY = ev.getY();

                // Update the parent down so that our zoom animations take this new movement into
                // account
                float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
                mParentDownMotionX = pt[0];
                mParentDownMotionY = pt[1];
                updateDragViewTranslationDuringDrag();
                boolean handledFling = false;
                if (!DISABLE_FLING_TO_DELETE) {
                    // Check the velocity and see if we are flinging-to-delete
                    PointF flingToDeleteVector = isFlingingToDelete();
                    if (flingToDeleteVector != null) {
                        onFlingToDelete(flingToDeleteVector);
                        handledFling = true;
                    }
                }
                if (!handledFling && isHoveringOverDeleteDropTarget((int) mParentDownMotionX,
                        (int) mParentDownMotionY)) {
                    onDropToDelete();
                }
            } else {
                if (DEBUG_WARP) Log.v(TAG, "calling onUnhandledTap()");
                if (mWarpPageExposed && !isAnimatingWarpPage()) {
                    animateWarpPageOffScreen("unhandled tap", true);
                }
                onUnhandledTap(ev);
            }

            // Remove the callback to wait for the side page hover timeout
            removeCallbacks(mSidePageHoverRunnable);
            // End any intermediate reordering states
            resetTouchState();
            break;

        case MotionEvent.ACTION_CANCEL:
            if (mTouchState == TOUCH_STATE_SCROLLING) {
                snapToDestination();
            }
            resetTouchState();
            break;

        case MotionEvent.ACTION_POINTER_UP:
            onSecondaryPointerUp(ev);
            break;
        }

        return true;
    
voidonTouchStateChanged(int newTouchState)

        if (DEBUG) {
            Log.v(TAG, "onTouchStateChanged(old="+ mTouchState + ", new=" + newTouchState + ")");
        }
    
protected voidonUnhandledTap(MotionEvent ev)

protected voidoverScroll(float amount)

        dampedOverScroll(amount);
    
private floatoverScrollInfluenceCurve(float f)

        f -= 1.0f;
        return f * f * f + 1.0f;
    
protected voidpageBeginMoving()

        if (DEBUG_WARP) Log.v(TAG, "pageBeginMoving(" + mIsPageMoving + ")");
        if (!mIsPageMoving) {
            mIsPageMoving = true;
            if (isWarping()) {
                dispatchOnPageBeginWarp();
            }
            onPageBeginMoving();
        }
    
protected voidpageEndMoving()

        if (DEBUG_WARP) Log.v(TAG, "pageEndMoving(" + mIsPageMoving + ")");
        if (mIsPageMoving) {
            mIsPageMoving = false;
            if (isWarping()) {
                dispatchOnPageEndWarp();
                mWarpPageExposed = false;
            }
            onPageEndMoving();
        }
    
public booleanperformAccessibilityAction(int action, android.os.Bundle arguments)

        if (super.performAccessibilityAction(action, arguments)) {
            return true;
        }
        switch (action) {
            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
                if (getCurrentPage() < getPageCount() - 1) {
                    scrollRight();
                    return true;
                }
            } break;
            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
                if (getCurrentPage() > 0) {
                    scrollLeft();
                    return true;
                }
            } break;
        }
        return false;
    
private voidreleaseVelocityTracker()

        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    
public voidrequestChildFocus(View child, View focused)

        super.requestChildFocus(child, focused);
        int page = indexToPage(indexOfChild(child));
        if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
            snapToPage(page);
        }
    
public booleanrequestChildRectangleOnScreen(View child, android.graphics.Rect rectangle, boolean immediate)

        int page = indexToPage(indexOfChild(child));
        if (page != mCurrentPage || !mScroller.isFinished()) {
            snapToPage(page);
            return true;
        }
        return false;
    
private voidresetTouchState()

        releaseVelocityTracker();
        endReordering();
        setTouchState(TOUCH_STATE_REST);
        mActivePointerId = INVALID_POINTER;
        mDownEventOnEdge = false;
    
private voidsaveDownState(MotionEvent ev)
Save the state when we get {@link MotionEvent#ACTION_DOWN}

param
ev

        // Remember where the motion event started
        mDownMotionX = mLastMotionX = ev.getX();
        mDownMotionY = mLastMotionY = ev.getY();
        mDownScrollX = getScrollX();
        float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
        mParentDownMotionX = p[0];
        mParentDownMotionY = p[1];
        mLastMotionXRemainder = 0;
        mTotalMotionX = 0;
        mActivePointerId = ev.getPointerId(0);

        // Determine if the down event is within the threshold to be an edge swipe
        int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
        int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
        if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
            mDownEventOnEdge = true;
        }
    
protected voidscreenScrolled(int screenCenter)

    
public voidscrollBy(int x, int y)

        scrollTo(mUnboundedScrollX + x, getScrollY() + y);
    
public voidscrollLeft()

        if (mScroller.isFinished()) {
            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
        } else {
            if (mNextPage > 0) snapToPage(mNextPage - 1);
        }
    
public voidscrollRight()

        if (mScroller.isFinished()) {
            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
        } else {
            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
        }
    
public voidscrollTo(int x, int y)

        mUnboundedScrollX = x;

        if (x < 0) {
            super.scrollTo(0, y);
            if (mAllowOverScroll) {
                overScroll(x);
            }
        } else if (x > mMaxScrollX) {
            super.scrollTo(mMaxScrollX, y);
            if (mAllowOverScroll) {
                overScroll(x - mMaxScrollX);
            }
        } else {
            mOverScrollX = x;
            super.scrollTo(x, y);
        }

        mTouchX = x;
        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;

        // Update the last motion events when scrolling
        if (isReordering(true)) {
            float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
            mLastMotionX = p[0];
            mLastMotionY = p[1];
            updateDragViewTranslationDuringDrag();
        }
    
voidsetCurrentPage(int currentPage)
Sets the current page.

        notifyPageSwitching(currentPage);
        if (!mScroller.isFinished()) {
            mScroller.abortAnimation();
        }
        // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
        // the default
        if (getChildCount() == 0) {
            return;
        }

        mForceScreenScrolled = true;
        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
        updateCurrentPageScroll();
        updateScrollingIndicator();
        notifyPageSwitched();
        invalidate();
    
protected voidsetDataIsReady()
Called by subclasses to mark that data is ready, and that we can begin loading and laying out pages.

        mIsDataReady = true;
    
voidsetDeleteDropTarget(View v)

        mDeleteDropTarget = v;
    
public voidsetMinScale(float f)

        mMinScale = f;
        requestLayout();
    
public voidsetOnLongClickListener(OnLongClickListener l)
Registers the specified listener on each page contained in this workspace.

param
l The listener used to respond to long clicks.

        mLongClickListener = l;
        final int count = getPageCount();
        for (int i = 0; i < count; i++) {
            getPageAt(i).setOnLongClickListener(l);
        }
    
public voidsetOnlyAllowEdgeSwipes(boolean enable)

        mOnlyAllowEdgeSwipes = enable;
    
protected voidsetPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering)

public voidsetPageSpacing(int pageSpacing)

        mPageSpacing = pageSpacing;
        invalidateCachedOffsets();
    
public voidsetPageSwitchListener(com.android.keyguard.PagedView$PageSwitchListener pageSwitchListener)

        mPageSwitchListener = pageSwitchListener;
        if (mPageSwitchListener != null) {
            mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage);
        }
    
public voidsetScaleX(float scaleX)

        super.setScaleX(scaleX);
        if (isReordering(true)) {
            float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
            mLastMotionX = p[0];
            mLastMotionY = p[1];
            updateDragViewTranslationDuringDrag();
        }
    
private voidsetTouchState(int touchState)

        if (mTouchState != touchState) {
            if (DEBUG_WARP) Log.v(TAG, "mTouchState changing to " + touchState);
            onTouchStateChanged(touchState);
            mTouchState = touchState;
        }
    
protected booleanshouldDrawChild(View child)

        return child.getAlpha() > 0;
    
protected booleanshouldSetTopAlignedPivotForWidget(int childIndex)

        return mTopAlignPageWhenShrinkingForBouncer;
    
protected voidshowScrollingIndicator(boolean immediately)

        mShouldShowScrollIndicator = true;
        mShouldShowScrollIndicatorImmediately = true;
        if (getChildCount() <= 1) return;
        if (!isScrollingIndicatorEnabled()) return;

        mShouldShowScrollIndicator = false;
        getScrollingIndicator();
        if (mScrollIndicator != null) {
            // Fade the indicator in
            updateScrollingIndicatorPosition();
            mScrollIndicator.setVisibility(View.VISIBLE);
            cancelScrollingIndicatorAnimations();
            if (immediately) {
                mScrollIndicator.setAlpha(1f);
            } else {
                mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f);
                mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration);
                mScrollIndicatorAnimator.start();
            }
        }
    
protected voidsnapToDestination()

        final int newPage = getPageNearestToCenterOfScreen();
        if (isWarping()) {
            cancelWarpAnimation("snapToDestination", mCurrentPage != newPage);
        }
        snapToPage(newPage, getPageSnapDuration());
    
protected voidsnapToPage(int whichPage, int duration)

        snapToPage(whichPage, duration, false);
    
protected voidsnapToPage(int whichPage, int duration, boolean immediate)

        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));

        if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
        if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getViewportWidth() + ", "
                + getChildWidth(whichPage));
        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
        final int sX = mUnboundedScrollX;
        final int delta = newX - sX;
        snapToPage(whichPage, delta, duration, immediate);
    
protected voidsnapToPage(int whichPage, int delta, int duration)

        snapToPage(whichPage, delta, duration, false);
    
protected voidsnapToPage(int whichPage, int delta, int duration, boolean immediate)

        if (isWarping() && whichPage == mCurrentPage+1) {
            mNextPage = getPageWarpIndex(); // jump to the warp page
            if (DEBUG_WARP) Log.v(TAG, "snapToPage(" + whichPage + ") : reset mPageSwapIndex");
        } else {
            mNextPage = whichPage;
        }

        if(mWarpPageExposed) {
            dispatchOnPageEndWarp();
            mWarpPageExposed = false;
        }
        notifyPageSwitching(whichPage);


        View focusedChild = getFocusedChild();
        if (focusedChild != null && whichPage != mCurrentPage &&
                focusedChild == getPageAt(mCurrentPage)) {
            focusedChild.clearFocus();
        }

        pageBeginMoving();
        awakenScrollBars(duration);
        if (immediate) {
            duration = 0;
        } else if (duration == 0) {
            duration = Math.abs(delta);
        }

        if (!mScroller.isFinished()) mScroller.abortAnimation();
        mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);

        notifyPageSwitched();

        // Trigger a compute() to finish switching pages if necessary
        if (immediate) {
            computeScroll();
        }

        mForceScreenScrolled = true;
        invalidate();
    
protected voidsnapToPage(int whichPage)

        snapToPage(whichPage, getPageSnapDuration());
    
protected voidsnapToPageImmediately(int whichPage)

        snapToPage(whichPage, getPageSnapDuration(), true);
    
protected voidsnapToPageWithVelocity(int whichPage, int velocity)

        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
        int halfScreenSize = getViewportWidth() / 2;

        if (isWarping()) {
            cancelWarpAnimation("snapToPageWithVelocity", mCurrentPage != whichPage);
        }

        if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
        if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
                + getViewportWidth() + ", " + getChildWidth(whichPage));
        final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
        int delta = newX - mUnboundedScrollX;
        int duration = 0;

        if (Math.abs(velocity) < mMinFlingVelocity) {
            // If the velocity is low enough, then treat this more as an automatic page advance
            // as opposed to an apparent physical response to flinging
            snapToPage(whichPage, getPageSnapDuration());
            return;
        }

        // Here we compute a "distance" that will be used in the computation of the overall
        // snap duration. This is a function of the actual distance that needs to be traveled;
        // we keep this value close to half screen size in order to reduce the variance in snap
        // duration as a function of the distance the page needs to travel.
        float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize));
        float distance = halfScreenSize + halfScreenSize *
                distanceInfluenceForSnapDuration(distanceRatio);

        velocity = Math.abs(velocity);
        velocity = Math.max(mMinSnapVelocity, velocity);

        // we want the page's snap velocity to approximately match the velocity at which the
        // user flings, so we scale the duration by a value near to the derivative of the scroll
        // interpolator at zero, ie. 5. We use 4 to make it a little slower.
        duration = 4 * Math.round(1000 * Math.abs(distance / velocity));

        snapToPage(whichPage, delta, duration);
    
public voidstartPageWarp(int pageIndex)

        if (DEBUG_WARP) Log.v(TAG, "START WARP");
        if (pageIndex != mCurrentPage + 1) {
            mPageSwapIndex = mCurrentPage + 1;
        }
        mPageWarpIndex = pageIndex;
    
public booleanstartReordering()

        int dragViewIndex = getPageNearestToCenterOfScreen();
        mTempVisiblePagesRange[0] = 0;
        mTempVisiblePagesRange[1] = getPageCount() - 1;
        boundByReorderablePages(true, mTempVisiblePagesRange);

        // Check if we are within the reordering range
        if (mTempVisiblePagesRange[0] <= dragViewIndex &&
                dragViewIndex <= mTempVisiblePagesRange[1]) {
            mReorderingStarted = true;
            if (zoomOut()) {
                // Find the drag view under the pointer
                mDragView = getChildAt(dragViewIndex);

                onStartReordering();
            }
            return true;
        }
        return false;
    
private voidstartScrolling(MotionEvent ev)

        // Ignore if we don't have a valid pointer index
        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
        if (pointerIndex == -1) return;

        final float x = ev.getX(pointerIndex);
        setTouchState(TOUCH_STATE_SCROLLING);
        mTotalMotionX += Math.abs(mLastMotionX - x);
        mLastMotionX = x;
        mLastMotionXRemainder = 0;
        mTouchX = getViewportOffsetX() + getScrollX();
        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
        pageBeginMoving();
    
public voidstopPageWarp()

        if (DEBUG_WARP) Log.v(TAG, "END WARP");
        // mPageSwapIndex is reset in snapToPage() after the scroll animation completes
    
voidswapPages(int indexA, int indexB)
Swaps the position of the views by setting the left and right edges appropriately.

        View viewA = getPageAt(indexA);
        View viewB = getPageAt(indexB);
        if (viewA != viewB && viewA != null && viewB != null) {
            int deltaX = viewA.getLeft() - viewB.getLeft();
            viewA.offsetLeftAndRight(-deltaX);
            viewB.offsetLeftAndRight(deltaX);
        }
    
protected voidupdateCurrentPageScroll()
Updates the scroll of the current page immediately to its final scroll position. We use this in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of the previous tab page.

        int offset = getChildOffset(mCurrentPage);
        int relOffset = getRelativeChildOffset(mCurrentPage);
        int newX = offset - relOffset;
        scrollTo(newX, 0);
        mScroller.setFinalX(newX);
        mScroller.forceFinished(true);
    
voidupdateDragViewTranslationDuringDrag()

        float x = mLastMotionX - mDownMotionX + getScrollX() - mDownScrollX;
        float y = mLastMotionY - mDownMotionY;
        mDragView.setTranslationX(x);
        mDragView.setTranslationY(y);

        if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): " + x + ", " + y);
    
private voidupdateScrollingIndicator()

        if (getChildCount() <= 1) return;
        if (!isScrollingIndicatorEnabled()) return;

        getScrollingIndicator();
        if (mScrollIndicator != null) {
            updateScrollingIndicatorPosition();
        }
        if (mShouldShowScrollIndicator) {
            showScrollingIndicator(mShouldShowScrollIndicatorImmediately);
        }
    
private voidupdateScrollingIndicatorPosition()

        if (!isScrollingIndicatorEnabled()) return;
        if (mScrollIndicator == null) return;
        int numPages = getChildCount();
        int pageWidth = getViewportWidth();
        int lastChildIndex = Math.max(0, getChildCount() - 1);
        int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
        int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
        int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
                mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();

        float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX));
        int indicatorSpace = trackWidth / numPages;
        int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft;
        if (hasElasticScrollIndicator()) {
            if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) {
                mScrollIndicator.getLayoutParams().width = indicatorSpace;
                mScrollIndicator.requestLayout();
            }
        } else {
            int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2;
            indicatorPos += indicatorCenterOffset;
        }
        mScrollIndicator.setTranslationX(indicatorPos);
    
protected booleanzoomIn(java.lang.Runnable onCompleteRunnable)

        if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
            mZoomInOutAnim.cancel();
        }
        if (getScaleX() < 1f || getScaleY() < 1f) {
            mZoomInOutAnim = new AnimatorSet();
            mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
            mZoomInOutAnim.playTogether(
                    ObjectAnimator.ofFloat(this, "scaleX", 1f),
                    ObjectAnimator.ofFloat(this, "scaleY", 1f));
            mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    // Hide the delete drop target
                    if (mDeleteDropTarget != null) {
                        mDeleteDropTarget.animate().alpha(0f)
                            .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
                            .setListener(new AnimatorListenerAdapter() {
                                @Override
                                public void onAnimationEnd(Animator animation) {
                                    mDeleteDropTarget.setVisibility(View.GONE);
                                }
                            });
                    }
                }
                @Override
                public void onAnimationCancel(Animator animation) {
                    mDragView = null;
                }
                @Override
                public void onAnimationEnd(Animator animation) {
                    mDragView = null;
                    if (onCompleteRunnable != null) {
                        onCompleteRunnable.run();
                    }
                }
            });
            mZoomInOutAnim.start();
            return true;
        } else {
            if (onCompleteRunnable != null) {
                onCompleteRunnable.run();
            }
        }
        return false;
    
protected booleanzoomOut()

        if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
            mZoomInOutAnim.cancel();
        }

        if (!(getScaleX() < 1f || getScaleY() < 1f)) {
            mZoomInOutAnim = new AnimatorSet();
            mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
            mZoomInOutAnim.playTogether(
                    ObjectAnimator.ofFloat(this, "scaleX", mMinScale),
                    ObjectAnimator.ofFloat(this, "scaleY", mMinScale));
            mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    // Show the delete drop target
                    if (mDeleteDropTarget != null) {
                        mDeleteDropTarget.setVisibility(View.VISIBLE);
                        mDeleteDropTarget.animate().alpha(1f)
                            .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
                            .setListener(new AnimatorListenerAdapter() {
                                @Override
                                public void onAnimationStart(Animator animation) {
                                    mDeleteDropTarget.setAlpha(0f);
                                }
                            });
                    }
                }
            });
            mZoomInOutAnim.start();
            return true;
        }
        return false;