Added Android code
[wl-app.git] / Android / folioreader / src / main / java / com / folioreader / view / DirectionalViewpager.java
diff --git a/Android/folioreader/src/main/java/com/folioreader/view/DirectionalViewpager.java b/Android/folioreader/src/main/java/com/folioreader/view/DirectionalViewpager.java
new file mode 100755 (executable)
index 0000000..574e453
--- /dev/null
@@ -0,0 +1,4119 @@
+package com.folioreader.view;\r
+\r
+/**\r
+ * Created by mobisys on 10/10/2016.\r
+ */\r
+\r
+\r
+import android.content.Context;\r
+import android.content.res.Resources;\r
+import android.content.res.TypedArray;\r
+import android.database.DataSetObserver;\r
+import android.graphics.Canvas;\r
+import android.graphics.Rect;\r
+import android.graphics.drawable.Drawable;\r
+import android.os.Build;\r
+import android.os.Bundle;\r
+import android.os.Parcel;\r
+import android.os.Parcelable;\r
+import android.os.SystemClock;\r
+import android.support.annotation.CallSuper;\r
+import android.support.annotation.DrawableRes;\r
+import android.support.v4.os.ParcelableCompat;\r
+import android.support.v4.os.ParcelableCompatCreatorCallbacks;\r
+import android.support.v4.view.AccessibilityDelegateCompat;\r
+import android.support.v4.view.MotionEventCompat;\r
+import android.support.v4.view.PagerAdapter;\r
+import android.support.v4.view.VelocityTrackerCompat;\r
+import android.support.v4.view.ViewCompat;\r
+import android.support.v4.view.ViewConfigurationCompat;\r
+import android.support.v4.view.WindowInsetsCompat;\r
+import android.support.v4.view.accessibility.AccessibilityEventCompat;\r
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;\r
+import android.support.v4.view.accessibility.AccessibilityRecordCompat;\r
+import android.support.v4.widget.EdgeEffectCompat;\r
+import android.util.AttributeSet;\r
+import android.util.Log;\r
+import android.view.FocusFinder;\r
+import android.view.Gravity;\r
+import android.view.KeyEvent;\r
+import android.view.MotionEvent;\r
+import android.view.SoundEffectConstants;\r
+import android.view.VelocityTracker;\r
+import android.view.View;\r
+import android.view.ViewConfiguration;\r
+import android.view.ViewGroup;\r
+import android.view.ViewParent;\r
+import android.view.accessibility.AccessibilityEvent;\r
+import android.view.animation.Interpolator;\r
+import android.widget.Scroller;\r
+\r
+import com.folioreader.R;\r
+\r
+import java.lang.reflect.Method;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.List;\r
+\r
+public class DirectionalViewpager extends ViewGroup {\r
+    private static final String TAG = "ViewPager";\r
+    private static final boolean DEBUG = false;\r
+\r
+    private static final boolean USE_CACHE = false;\r
+\r
+    private static final int DEFAULT_OFFSCREEN_PAGES = 1;\r
+    private static final int MAX_SETTLE_DURATION = 600; // ms\r
+    private static final int MIN_DISTANCE_FOR_FLING = 25; // dips\r
+\r
+    private static final int DEFAULT_GUTTER_SIZE = 16; // dips\r
+\r
+    private static final int MIN_FLING_VELOCITY = 400; // dips\r
+\r
+    private static final int[] LAYOUT_ATTRS = new int[]{\r
+            android.R.attr.layout_gravity\r
+    };\r
+\r
+    public static enum Direction {\r
+        HORIZONTAL,\r
+        VERTICAL,\r
+    }\r
+\r
+    /**\r
+     * Used to track what the expected number of items in the adapter should be.\r
+     * If the app changes this when we don't expect it, we'll throw a big obnoxious exception.\r
+     */\r
+    private int mExpectedAdapterCount;\r
+    public String mDirection = Direction.VERTICAL.name();\r
+\r
+    static class ItemInfo {\r
+        Object object;\r
+        int position;\r
+        boolean scrolling;\r
+        float widthFactor;\r
+        float heightFactor;\r
+        float offset;\r
+    }\r
+\r
+    private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>() {\r
+        @Override\r
+        public int compare(ItemInfo lhs, ItemInfo rhs) {\r
+            return lhs.position - rhs.position;\r
+        }\r
+    };\r
+\r
+    private static final Interpolator sInterpolator = new Interpolator() {\r
+        public float getInterpolation(float t) {\r
+            t -= 1.0f;\r
+            return t * t * t * t * t + 1.0f;\r
+        }\r
+    };\r
+\r
+    private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();\r
+    private final ItemInfo mTempItem = new ItemInfo();\r
+\r
+    private final Rect mTempRect = new Rect();\r
+\r
+    private PagerAdapter mAdapter;\r
+    private int mCurItem;   // Index of currently displayed page.\r
+    private int mRestoredCurItem = -1;\r
+    private Parcelable mRestoredAdapterState = null;\r
+    private ClassLoader mRestoredClassLoader = null;\r
+\r
+    private Scroller mScroller;\r
+    private boolean mIsScrollStarted;\r
+\r
+    private PagerObserver mObserver;\r
+\r
+    private int mPageMargin;\r
+    private Drawable mMarginDrawable;\r
+    private int mTopPageBounds;\r
+    private int mBottomPageBounds;\r
+    private int mLeftPageBounds;\r
+    private int mRightPageBounds;\r
+\r
+    // Offsets of the first and last items, if known.\r
+    // Set during population, used to determine if we are at the beginning\r
+    // or end of the pager data set during touch scrolling.\r
+    private float mFirstOffset = -Float.MAX_VALUE;\r
+    private float mLastOffset = Float.MAX_VALUE;\r
+\r
+    private int mChildWidthMeasureSpec;\r
+    private int mChildHeightMeasureSpec;\r
+    private boolean mInLayout;\r
+\r
+    private boolean mScrollingCacheEnabled;\r
+\r
+    private boolean mPopulatePending;\r
+    private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;\r
+\r
+    private boolean mIsBeingDragged;\r
+    private boolean mIsUnableToDrag;\r
+    private boolean mIgnoreGutter;\r
+    private int mDefaultGutterSize;\r
+    private int mGutterSize;\r
+    private int mTouchSlop;\r
+    /**\r
+     * Position of the last motion event.\r
+     */\r
+    private float mLastMotionX;\r
+    private float mLastMotionY;\r
+    private float mInitialMotionX;\r
+    private float mInitialMotionY;\r
+    /**\r
+     * ID of the active pointer. This is used to retain consistency during\r
+     * drags/flings if multiple pointers are used.\r
+     */\r
+    private int mActivePointerId = INVALID_POINTER;\r
+    /**\r
+     * Sentinel value for no current active pointer.\r
+     * Used by {@link #mActivePointerId}.\r
+     */\r
+    private static final int INVALID_POINTER = -1;\r
+\r
+    /**\r
+     * Determines speed during touch scrolling\r
+     */\r
+    private VelocityTracker mVelocityTracker;\r
+    private int mMinimumVelocity;\r
+    private int mMaximumVelocity;\r
+    private int mFlingDistance;\r
+    private int mCloseEnough;\r
+\r
+    // If the pager is at least this close to its final position, complete the scroll\r
+    // on touch down and let the user interact with the content inside instead of\r
+    // "catching" the flinging pager.\r
+    private static final int CLOSE_ENOUGH = 2; // dp\r
+\r
+    private boolean mFakeDragging;\r
+    private long mFakeDragBeginTime;\r
+\r
+    private EdgeEffectCompat mLeftEdge;\r
+    private EdgeEffectCompat mRightEdge;\r
+    private EdgeEffectCompat mTopEdge;\r
+    private EdgeEffectCompat mBottomEdge;\r
+\r
+    private boolean mFirstLayout = true;\r
+    private boolean mNeedCalculatePageOffsets = false;\r
+    private boolean mCalledSuper;\r
+    private int mDecorChildCount;\r
+\r
+    private List<OnPageChangeListener> mOnPageChangeListeners;\r
+    private OnPageChangeListener mOnPageChangeListener;\r
+    private OnPageChangeListener mInternalPageChangeListener;\r
+    private OnAdapterChangeListener mAdapterChangeListener;\r
+    private PageTransformer mPageTransformer;\r
+    private Method mSetChildrenDrawingOrderEnabled;\r
+\r
+    private static final int DRAW_ORDER_DEFAULT = 0;\r
+    private static final int DRAW_ORDER_FORWARD = 1;\r
+    private static final int DRAW_ORDER_REVERSE = 2;\r
+    private int mDrawingOrder;\r
+    private ArrayList<View> mDrawingOrderedChildren;\r
+    private static final ViewPositionComparator sPositionComparator = new ViewPositionComparator();\r
+\r
+    /**\r
+     * Indicates that the pager is in an idle, settled state. The current page\r
+     * is fully in view and no animation is in progress.\r
+     */\r
+    public static final int SCROLL_STATE_IDLE = 0;\r
+\r
+    /**\r
+     * Indicates that the pager is currently being dragged by the user.\r
+     */\r
+    public static final int SCROLL_STATE_DRAGGING = 1;\r
+\r
+    /**\r
+     * Indicates that the pager is in the process of settling to a final position.\r
+     */\r
+    public static final int SCROLL_STATE_SETTLING = 2;\r
+\r
+    private final Runnable mEndScrollRunnable = new Runnable() {\r
+        public void run() {\r
+            setScrollState(SCROLL_STATE_IDLE);\r
+            populate();\r
+        }\r
+    };\r
+\r
+    private int mScrollState = SCROLL_STATE_IDLE;\r
+\r
+    /**\r
+     * Callback interface for responding to changing state of the selected page.\r
+     */\r
+    public interface OnPageChangeListener {\r
+\r
+        /**\r
+         * This method will be invoked when the current\r
+         * page is scrolled, either as part\r
+         * of a programmatically initiated\r
+         * smooth scroll or a user initiated touch scroll.\r
+         *\r
+         * @param position             Position index of the first page currently being displayed.\r
+         *                             <p>\r
+         *                             Page position+1 will be visible if positionOffset is nonzero.\r
+         * @param positionOffset\r
+         * Value from [0, 1) indicating the offset from the page at position.\r
+         * @param positionOffsetPixels\r
+         * Value in pixels indicating the offset from position.\r
+         */\r
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);\r
+\r
+        /**\r
+         * This method will be invoked when a new page becomes selected. Animation is not\r
+         * necessarily complete.\r
+         *\r
+         * @param position Position index of the new selected page.\r
+         */\r
+        public void onPageSelected(int position);\r
+\r
+        /**\r
+         * Called when the scroll state changes. Useful for discovering when the user\r
+         * begins dragging, when the pager is automatically settling to the current page,\r
+         * or when it is fully stopped/idle.\r
+         *\r
+         * @param state The new scroll state.\r
+         * @see ViewPager#SCROLL_STATE_IDLE\r
+         * @see ViewPager#SCROLL_STATE_DRAGGING\r
+         * @see ViewPager#SCROLL_STATE_SETTLING\r
+         */\r
+        public void onPageScrollStateChanged(int state);\r
+    }\r
+\r
+    /**\r
+     * Simple implementation of the {@link OnPageChangeListener}\r
+     * interface with stub\r
+     * implementations of each method.\r
+     * Extend this if you do not intend to override\r
+     * every method of {@link OnPageChangeListener}.\r
+     */\r
+    public static class SimpleOnPageChangeListener implements OnPageChangeListener {\r
+        @Override\r
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\r
+            // This space for rent\r
+        }\r
+\r
+        @Override\r
+        public void onPageSelected(int position) {\r
+            // This space for rent\r
+        }\r
+\r
+        @Override\r
+        public void onPageScrollStateChanged(int state) {\r
+            // This space for rent\r
+        }\r
+    }\r
+\r
+    /**\r
+     * A PageTransformer is invoked whenever a visible/attached page is scrolled.\r
+     * This offers an opportunity for the application to apply a custom transformation\r
+     * to the page views using animation properties.\r
+     * <p>\r
+     * <p>As property animation is only supported as of Android 3.0 and forward,\r
+     * setting a PageTransformer on a ViewPager on earlier platform versions will\r
+     * be ignored.</p>\r
+     */\r
+    public interface PageTransformer {\r
+        /**\r
+         * Apply a property transformation to the given page.\r
+         *\r
+         * @param page     Apply the transformation to this page\r
+         * @param position Position of page relative to the current front-and-center\r
+         *                 position of the pager. 0 is front and center. 1 is one full\r
+         *                 page position to the right, and -1 is one page position to the left.\r
+         */\r
+        public void transformPage(View page, float position);\r
+    }\r
+\r
+    /**\r
+     * Used internally to monitor when adapters are switched.\r
+     */\r
+    interface OnAdapterChangeListener {\r
+        public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter);\r
+    }\r
+\r
+    /**\r
+     * Used internally to tag special types of child views that should be added as\r
+     * pager decorations by default.\r
+     */\r
+    interface Decor {\r
+    }\r
+\r
+    public DirectionalViewpager(Context context) {\r
+        super(context);\r
+        initViewPager();\r
+    }\r
+\r
+    public DirectionalViewpager(Context context, AttributeSet attrs) {\r
+        super(context, attrs);\r
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DirectionalViewpager);\r
+        if (a.getString(R.styleable.DirectionalViewpager_direction) != null) {\r
+            mDirection = a.getString(R.styleable.DirectionalViewpager_direction);\r
+        }\r
+        initViewPager();\r
+    }\r
+\r
+    void initViewPager() {\r
+        setWillNotDraw(false);\r
+        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);\r
+        setFocusable(true);\r
+        final Context context = getContext();\r
+        mScroller = new Scroller(context, sInterpolator);\r
+        final ViewConfiguration configuration = ViewConfiguration.get(context);\r
+        final float density = context.getResources().getDisplayMetrics().density;\r
+\r
+        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);\r
+        mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);\r
+        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();\r
+        mLeftEdge = new EdgeEffectCompat(context);\r
+        mRightEdge = new EdgeEffectCompat(context);\r
+        mTopEdge = new EdgeEffectCompat(context);\r
+        mBottomEdge = new EdgeEffectCompat(context);\r
+\r
+        mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);\r
+        mCloseEnough = (int) (CLOSE_ENOUGH * density);\r
+        mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);\r
+\r
+        ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate());\r
+\r
+        if (ViewCompat.getImportantForAccessibility(this)\r
+                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {\r
+            ViewCompat.setImportantForAccessibility(this,\r
+                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);\r
+        }\r
+\r
+        ViewCompat.setOnApplyWindowInsetsListener(this,\r
+                new android.support\r
+                        .v4.view.OnApplyWindowInsetsListener() {\r
+                    private final Rect mTempRect = new Rect();\r
+\r
+                    @Override\r
+                    public WindowInsetsCompat\r
+                            onApplyWindowInsets(final View v,\r
+                                        final WindowInsetsCompat originalInsets) {\r
+                        // First let the ViewPager itself try and consume them...\r
+                        final WindowInsetsCompat applied =\r
+                                    ViewCompat.onApplyWindowInsets(v, originalInsets);\r
+                        if (applied.isConsumed()) {\r
+                            // If the ViewPager consumed all insets, return now\r
+                            return applied;\r
+                        }\r
+\r
+                        // Now we'll manually dispatch the insets to our children. Since ViewPager\r
+                        // children are always full-height, we do not want to use the standard\r
+                        // ViewGroup dispatchApplyWindowInsets since if child 0 consumes them,\r
+                        // the rest of the children will not receive any insets. To workaround this\r
+                        // we manually dispatch the applied insets, not allowing children to\r
+                        // consume them from each other. We do however keep track of any insets\r
+                        // which are consumed, returning the union of our children's consumption\r
+                        final Rect res = mTempRect;\r
+                        res.left = applied.getSystemWindowInsetLeft();\r
+                        res.top = applied.getSystemWindowInsetTop();\r
+                        res.right = applied.getSystemWindowInsetRight();\r
+                        res.bottom = applied.getSystemWindowInsetBottom();\r
+\r
+                        for (int i = 0, count = getChildCount(); i < count; i++) {\r
+                            final WindowInsetsCompat childInsets = ViewCompat\r
+                                    .dispatchApplyWindowInsets(getChildAt(i), applied);\r
+                            // Now keep track of any consumed by tracking each dimension's min\r
+                            // value\r
+                            res.left\r
+                                    = Math.min(childInsets.getSystemWindowInsetLeft(),\r
+                                    res.left);\r
+                            res.top = Math.min(childInsets.getSystemWindowInsetTop(),\r
+                                    res.top);\r
+                            res.right = Math.min(childInsets.getSystemWindowInsetRight(),\r
+                                    res.right);\r
+                            res.bottom = Math.min(childInsets.getSystemWindowInsetBottom(),\r
+                                    res.bottom);\r
+                        }\r
+\r
+                        // Now return a new WindowInsets, using the consumed window insets\r
+                        return applied.replaceSystemWindowInsets(\r
+                                res.left, res.top, res.right, res.bottom);\r
+                    }\r
+                });\r
+    }\r
+\r
+    @Override\r
+    protected void onDetachedFromWindow() {\r
+        removeCallbacks(mEndScrollRunnable);\r
+        // To be on the safe side, abort the scroller\r
+        if ((mScroller != null) && !mScroller.isFinished()) {\r
+            mScroller.abortAnimation();\r
+        }\r
+        super.onDetachedFromWindow();\r
+    }\r
+\r
+    private void setScrollState(int newState) {\r
+        if (mScrollState == newState) {\r
+            return;\r
+        }\r
+\r
+        mScrollState = newState;\r
+        if (mPageTransformer != null) {\r
+            // PageTransformers can do complex things that benefit from hardware layers.\r
+            enableLayers(newState != SCROLL_STATE_IDLE);\r
+        }\r
+        dispatchOnScrollStateChanged(newState);\r
+    }\r
+\r
+    /**\r
+     * Set a PagerAdapter that will supply views for this pager as needed.\r
+     *\r
+     * @param adapter Adapter to use\r
+     */\r
+    public void setAdapter(PagerAdapter adapter) {\r
+        if (mAdapter != null) {\r
+            mAdapter.unregisterDataSetObserver(mObserver);\r
+            mAdapter.startUpdate(this);\r
+            for (int i = 0; i < mItems.size(); i++) {\r
+                final ItemInfo ii = mItems.get(i);\r
+                mAdapter.destroyItem(this, ii.position, ii.object);\r
+            }\r
+            mAdapter.finishUpdate(this);\r
+            mItems.clear();\r
+            removeNonDecorViews();\r
+            mCurItem = 0;\r
+            scrollTo(0, 0);\r
+        }\r
+\r
+        final PagerAdapter oldAdapter = mAdapter;\r
+        mAdapter = adapter;\r
+        mExpectedAdapterCount = 0;\r
+\r
+        if (mAdapter != null) {\r
+            if (mObserver == null) {\r
+                mObserver = new PagerObserver();\r
+            }\r
+            mAdapter.registerDataSetObserver(mObserver);\r
+            mPopulatePending = false;\r
+            final boolean wasFirstLayout = mFirstLayout;\r
+            mFirstLayout = true;\r
+            mExpectedAdapterCount = mAdapter.getCount();\r
+            if (mRestoredCurItem >= 0) {\r
+                mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);\r
+                setCurrentItemInternal(mRestoredCurItem, false, true);\r
+                mRestoredCurItem = -1;\r
+                mRestoredAdapterState = null;\r
+                mRestoredClassLoader = null;\r
+            } else if (!wasFirstLayout) {\r
+                populate();\r
+            } else {\r
+                requestLayout();\r
+            }\r
+        }\r
+\r
+        if (mAdapterChangeListener != null && oldAdapter != adapter) {\r
+            mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);\r
+        }\r
+    }\r
+\r
+    private void removeNonDecorViews() {\r
+        for (int i = 0; i < getChildCount(); i++) {\r
+            final View child = getChildAt(i);\r
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+            if (!lp.isDecor) {\r
+                removeViewAt(i);\r
+                i--;\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Retrieve the current adapter supplying pages.\r
+     *\r
+     * @return The currently registered PagerAdapter\r
+     */\r
+    public PagerAdapter getAdapter() {\r
+        return mAdapter;\r
+    }\r
+\r
+    void setOnAdapterChangeListener(OnAdapterChangeListener listener) {\r
+        mAdapterChangeListener = listener;\r
+    }\r
+\r
+    private int getClientWidth() {\r
+        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();\r
+    }\r
+\r
+    private int getClientHeight() {\r
+        return getMeasuredHeight() - getPaddingTop() - getPaddingBottom();\r
+    }\r
+\r
+    /**\r
+     * Set the currently selected page. If the ViewPager has already been through its first\r
+     * layout with its current adapter there will be a smooth animated transition between\r
+     * the current item and the specified item.\r
+     *\r
+     * @param item Item index to select\r
+     */\r
+    public void setCurrentItem(int item) {\r
+        mPopulatePending = false;\r
+        setCurrentItemInternal(item, !mFirstLayout, false);\r
+    }\r
+\r
+    /**\r
+     * Set the currently selected page.\r
+     *\r
+     * @param item         Item index to select\r
+     * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately\r
+     */\r
+    public void setCurrentItem(int item, boolean smoothScroll) {\r
+        mPopulatePending = false;\r
+        setCurrentItemInternal(item, smoothScroll, false);\r
+    }\r
+\r
+    public int getCurrentItem() {\r
+        return mCurItem;\r
+    }\r
+\r
+    public int getExpectedAdapterCount() {\r
+        return mExpectedAdapterCount;\r
+    }\r
+\r
+    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {\r
+        setCurrentItemInternal(item, smoothScroll, always, 0);\r
+    }\r
+\r
+    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {\r
+        if (mAdapter == null || mAdapter.getCount() <= 0) {\r
+            setScrollingCacheEnabled(false);\r
+            return;\r
+        }\r
+        if (!always && mCurItem == item && mItems.size() != 0) {\r
+            setScrollingCacheEnabled(false);\r
+            return;\r
+        }\r
+\r
+        if (item < 0) {\r
+            item = 0;\r
+        } else if (item >= mAdapter.getCount()) {\r
+            item = mAdapter.getCount() - 1;\r
+        }\r
+        final int pageLimit = mOffscreenPageLimit;\r
+        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {\r
+            // We are doing a jump by more than one page.  To avoid\r
+            // glitches, we want to keep all current pages in the view\r
+            // until the scroll ends.\r
+            for (int i = 0; i < mItems.size(); i++) {\r
+                mItems.get(i).scrolling = true;\r
+            }\r
+        }\r
+        final boolean dispatchSelected = mCurItem != item;\r
+\r
+        if (mFirstLayout) {\r
+            // We don't have any idea how big we are yet and shouldn't have any pages either.\r
+            // Just set things up and let the pending layout handle things.\r
+            mCurItem = item;\r
+            if (dispatchSelected) {\r
+                dispatchOnPageSelected(item);\r
+            }\r
+            requestLayout();\r
+        } else {\r
+            populate(item);\r
+            scrollToItem(item, smoothScroll, velocity, dispatchSelected);\r
+        }\r
+    }\r
+\r
+    private void scrollToItem(int item, boolean smoothScroll, int velocity,\r
+                              boolean dispatchSelected) {\r
+        final ItemInfo curInfo = infoForPosition(item);\r
+        int destX = 0;\r
+        int destY = 0;\r
+        if (isHorizontal()) {\r
+            if (curInfo != null) {\r
+                final int width = getClientWidth();\r
+                destX = (int) (width * Math.max(mFirstOffset,\r
+                        Math.min(curInfo.offset, mLastOffset)));\r
+            }\r
+            if (smoothScroll) {\r
+                smoothScrollTo(destX, 0, velocity);\r
+                if (dispatchSelected) {\r
+                    dispatchOnPageSelected(item);\r
+                }\r
+            } else {\r
+                if (dispatchSelected) {\r
+                    dispatchOnPageSelected(item);\r
+                }\r
+                completeScroll(false);\r
+                scrollTo(destX, 0);\r
+                pageScrolled(destX, 0);\r
+            }\r
+        } else {\r
+            if (curInfo != null) {\r
+                final int height = getClientHeight();\r
+                destY = (int) (height * Math.max(mFirstOffset,\r
+                        Math.min(curInfo.offset, mLastOffset)));\r
+            }\r
+            if (smoothScroll) {\r
+                smoothScrollTo(0, destY, velocity);\r
+                if (dispatchSelected && mOnPageChangeListener != null) {\r
+                    mOnPageChangeListener.onPageSelected(item);\r
+                }\r
+                if (dispatchSelected && mInternalPageChangeListener != null) {\r
+                    mInternalPageChangeListener.onPageSelected(item);\r
+                }\r
+            } else {\r
+                if (dispatchSelected && mOnPageChangeListener != null) {\r
+                    mOnPageChangeListener.onPageSelected(item);\r
+                }\r
+                if (dispatchSelected && mInternalPageChangeListener != null) {\r
+                    mInternalPageChangeListener.onPageSelected(item);\r
+                }\r
+                completeScroll(false);\r
+                scrollTo(0, destY);\r
+                pageScrolled(0, destY);\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Set a listener that will be invoked whenever the page changes or is incrementally\r
+     * scrolled. See {@link OnPageChangeListener}.\r
+     *\r
+     * @param listener Listener to set\r
+     * @deprecated Use {@link #addOnPageChangeListener(OnPageChangeListener)}\r
+     * and {@link #removeOnPageChangeListener(OnPageChangeListener)} instead.\r
+     */\r
+    @Deprecated\r
+    public void setOnPageChangeListener(OnPageChangeListener listener) {\r
+        mOnPageChangeListener = listener;\r
+    }\r
+\r
+    /**\r
+     * Add a listener that will be invoked whenever the page changes or is incrementally\r
+     * scrolled. See {@link OnPageChangeListener}.\r
+     * <p>\r
+     * <p>Components that add a listener should take care to remove it when finished.\r
+     * Other components that take ownership of a view may call {@link #clearOnPageChangeListeners()}\r
+     * to remove all attached listeners.</p>\r
+     *\r
+     * @param listener listener to add\r
+     */\r
+    public void addOnPageChangeListener(OnPageChangeListener listener) {\r
+        if (mOnPageChangeListeners == null) {\r
+            mOnPageChangeListeners = new ArrayList<>();\r
+        }\r
+        mOnPageChangeListeners.add(listener);\r
+    }\r
+\r
+    /**\r
+     * Remove a listener that was previously added via\r
+     * {@link #addOnPageChangeListener(OnPageChangeListener)}.\r
+     *\r
+     * @param listener listener to remove\r
+     */\r
+    public void removeOnPageChangeListener(OnPageChangeListener listener) {\r
+        if (mOnPageChangeListeners != null) {\r
+            mOnPageChangeListeners.remove(listener);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Remove all listeners that are notified of any changes in scroll state or position.\r
+     */\r
+    public void clearOnPageChangeListeners() {\r
+        if (mOnPageChangeListeners != null) {\r
+            mOnPageChangeListeners.clear();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Set a {@link PageTransformer} that will be called for each attached page whenever\r
+     * the scroll position is changed. This allows the application to apply custom property\r
+     * transformations to each page, overriding the default sliding look and feel.\r
+     * <p>\r
+     * <p><em>Note:</em> Prior to Android 3.0 the property animation APIs did not exist.\r
+     * As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.</p>\r
+     *\r
+     * @param reverseDrawingOrder true if the supplied PageTransformer requires page views\r
+     *                            to be drawn from last to first instead of first to last.\r
+     * @param transformer         PageTransformer that will modify each page's animation properties\r
+     */\r
+    public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {\r
+        if (Build.VERSION.SDK_INT >= 11) {\r
+            final boolean hasTransformer = transformer != null;\r
+            final boolean needsPopulate = hasTransformer != (mPageTransformer != null);\r
+            mPageTransformer = transformer;\r
+            setChildrenDrawingOrderEnabledCompat(hasTransformer);\r
+            if (hasTransformer) {\r
+                mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;\r
+            } else {\r
+                mDrawingOrder = DRAW_ORDER_DEFAULT;\r
+            }\r
+            if (needsPopulate) populate();\r
+        }\r
+    }\r
+\r
+    void setChildrenDrawingOrderEnabledCompat(boolean enable) {\r
+        if (Build.VERSION.SDK_INT >= 7) {\r
+            if (mSetChildrenDrawingOrderEnabled == null) {\r
+                try {\r
+                    mSetChildrenDrawingOrderEnabled = ViewGroup.class.getDeclaredMethod(\r
+                            "setChildrenDrawingOrderEnabled", new Class[]{Boolean.TYPE});\r
+                } catch (NoSuchMethodException e) {\r
+                    Log.e(TAG, "Can't find setChildrenDrawingOrderEnabled", e);\r
+                }\r
+            }\r
+            try {\r
+                mSetChildrenDrawingOrderEnabled\r
+                        .invoke(this, enable);\r
+            } catch (Exception e) {\r
+                Log.e(TAG, "Error changing children drawing order", e);\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    protected int getChildDrawingOrder(int childCount, int i) {\r
+        final int index = mDrawingOrder\r
+                == DRAW_ORDER_REVERSE ? childCount - 1 - i : i;\r
+        final int result\r
+                = ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex;\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Set a separate OnPageChangeListener for internal use by the support library.\r
+     *\r
+     * @param listener Listener to set\r
+     * @return The old listener that was set, if any.\r
+     */\r
+    OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {\r
+        OnPageChangeListener oldListener = mInternalPageChangeListener;\r
+        mInternalPageChangeListener = listener;\r
+        return oldListener;\r
+    }\r
+\r
+    /**\r
+     * Returns the number of pages that will be retained to either side of the\r
+     * current page in the view hierarchy in an idle state. Defaults to 1.\r
+     *\r
+     * @return How many pages will be kept offscreen on either side\r
+     * @see #setOffscreenPageLimit(int)\r
+     */\r
+    public int getOffscreenPageLimit() {\r
+        return mOffscreenPageLimit;\r
+    }\r
+\r
+    /**\r
+     * Set the number of pages that should be\r
+     * retained to either side of the\r
+     * current page in the view hierarchy\r
+     * in an idle state. Pages beyond this\r
+     * limit will be recreated from the adapter when needed.\r
+     * <p>\r
+     * <p>This is offered as an optimization.\r
+     * If you know in advance the number\r
+     * of pages you will need to support or\r
+     * have lazy-loading mechanisms in place\r
+     * on your pages, tweaking this setting\r
+     * can have benefits in perceived smoothness\r
+     * of paging animations and interaction.\r
+     * If you have a small number of pages (3-4)\r
+     * that you can keep active all at once,\r
+     * less time will be spent in layout for\r
+     * newly created view subtrees as the\r
+     * user pages back and forth.</p>\r
+     * <p>\r
+     * <p>You should keep this limit low,\r
+     * especially if your pages have complex layouts.\r
+     * This setting defaults to 1.</p>\r
+     *\r
+     * @param limit How many pages will be kept offscreen in an idle state.\r
+     */\r
+    public void setOffscreenPageLimit(int limit) {\r
+        if (limit < DEFAULT_OFFSCREEN_PAGES) {\r
+            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +\r
+                    DEFAULT_OFFSCREEN_PAGES);\r
+            limit = DEFAULT_OFFSCREEN_PAGES;\r
+        }\r
+        if (limit != mOffscreenPageLimit) {\r
+            mOffscreenPageLimit = limit;\r
+            populate();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Set the margin between pages.\r
+     *\r
+     * @param marginPixels Distance between adjacent pages in pixels\r
+     * @see #getPageMargin()\r
+     * @see #setPageMarginDrawable(Drawable)\r
+     * @see #setPageMarginDrawable(int)\r
+     */\r
+    public void setPageMargin(int marginPixels) {\r
+        final int oldMargin = mPageMargin;\r
+        mPageMargin = marginPixels;\r
+\r
+        if (isHorizontal()) {\r
+            int width = getWidth();\r
+            recomputeScrollPosition(width, width, marginPixels, oldMargin, 0, 0);\r
+        } else {\r
+            int height = getHeight();\r
+            recomputeScrollPosition(0, 0, marginPixels, oldMargin, height, height);\r
+        }\r
+\r
+        requestLayout();\r
+    }\r
+\r
+    /**\r
+     * Return the margin between pages.\r
+     *\r
+     * @return The size of the margin in pixels\r
+     */\r
+    public int getPageMargin() {\r
+        return mPageMargin;\r
+    }\r
+\r
+    /**\r
+     * Set a drawable that will be used to fill the margin between pages.\r
+     *\r
+     * @param d Drawable to display between pages\r
+     */\r
+    public void setPageMarginDrawable(Drawable d) {\r
+        mMarginDrawable = d;\r
+        if (d != null) refreshDrawableState();\r
+        setWillNotDraw(d == null);\r
+        invalidate();\r
+    }\r
+\r
+    /**\r
+     * Set a drawable that will be used to fill the margin between pages.\r
+     *\r
+     * @param resId Resource ID of a drawable to display between pages\r
+     */\r
+    public void setPageMarginDrawable(@DrawableRes int resId) {\r
+        setPageMarginDrawable(getContext().getResources().getDrawable(resId));\r
+    }\r
+\r
+    @Override\r
+    protected boolean verifyDrawable(Drawable who) {\r
+        return super.verifyDrawable(who) || who == mMarginDrawable;\r
+    }\r
+\r
+    @Override\r
+    protected void drawableStateChanged() {\r
+        super.drawableStateChanged();\r
+        final Drawable d = mMarginDrawable;\r
+        if (d != null && d.isStateful()) {\r
+            d.setState(getDrawableState());\r
+        }\r
+    }\r
+\r
+    // We want the duration of the page snap animation to be influenced by the distance that\r
+    // the screen has to travel, however, we don't want this duration to be effected in a\r
+    // purely linear fashion. Instead, we use this method to moderate the effect that the distance\r
+    // of travel has on the overall snap duration.\r
+    float distanceInfluenceForSnapDuration(float f) {\r
+        f -= 0.5f; // center the values about 0.\r
+        f *= 0.3f * Math.PI / 2.0f;\r
+        return (float) Math.sin(f);\r
+    }\r
+\r
+    /**\r
+     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.\r
+     *\r
+     * @param x the number of pixels to scroll by on the X axis\r
+     * @param y the number of pixels to scroll by on the Y axis\r
+     */\r
+    void smoothScrollTo(int x, int y) {\r
+        smoothScrollTo(x, y, 0);\r
+    }\r
+\r
+    /**\r
+     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.\r
+     *\r
+     * @param x        the number of pixels to scroll by on the X axis\r
+     * @param y        the number of pixels to scroll by on the Y axis\r
+     * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)\r
+     */\r
+    void smoothScrollTo(int x, int y, int velocity) {\r
+        if (getChildCount() == 0) {\r
+            // Nothing to do.\r
+            setScrollingCacheEnabled(false);\r
+            return;\r
+        }\r
+\r
+        int sx;\r
+        if (isHorizontal()) {\r
+            boolean wasScrolling = (mScroller != null) && !mScroller.isFinished();\r
+            if (wasScrolling) {\r
+                // We're in the middle of a previously initiated scrolling. Check to see\r
+                // whether that scrolling has actually started (if we always call getStartX\r
+                // we can get a stale value from the scroller if it hadn't yet had its first\r
+                // computeScrollOffset call) to decide what is the current scrolling position.\r
+                sx = mIsScrollStarted ? mScroller.getCurrX() : mScroller.getStartX();\r
+                // And abort the current scrolling.\r
+                mScroller.abortAnimation();\r
+                setScrollingCacheEnabled(false);\r
+            } else {\r
+                sx = getScrollX();\r
+            }\r
+        } else {\r
+            sx = getScrollX();\r
+        }\r
+        int sy = getScrollY();\r
+        int dx = x - sx;\r
+        int dy = y - sy;\r
+        if (dx == 0 && dy == 0) {\r
+            completeScroll(false);\r
+            populate();\r
+            setScrollState(SCROLL_STATE_IDLE);\r
+            return;\r
+        }\r
+\r
+        setScrollingCacheEnabled(true);\r
+        setScrollState(SCROLL_STATE_SETTLING);\r
+        int duration = 0;\r
+        if (isHorizontal()) {\r
+            final int width = getClientWidth();\r
+            final int halfWidth = width / 2;\r
+            final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);\r
+            final float distance = halfWidth + halfWidth *\r
+                    distanceInfluenceForSnapDuration(distanceRatio);\r
+            velocity = Math.abs(velocity);\r
+            if (velocity > 0) {\r
+                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));\r
+            } else {\r
+                final float pageWidth = width * mAdapter.getPageWidth(mCurItem);\r
+                final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin);\r
+                duration = (int) ((pageDelta + 1) * 100);\r
+            }\r
+        } else {\r
+            final int height = getClientHeight();\r
+            final int halfHeight = height / 2;\r
+            final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / height);\r
+            final float distance = halfHeight + halfHeight *\r
+                    distanceInfluenceForSnapDuration(distanceRatio);\r
+\r
+            duration = 0;\r
+            velocity = Math.abs(velocity);\r
+            if (velocity > 0) {\r
+                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));\r
+            } else {\r
+                final float pageHeight = height * mAdapter.getPageWidth(mCurItem);\r
+                final float pageDelta = (float) Math.abs(dx) / (pageHeight + mPageMargin);\r
+                duration = (int) ((pageDelta + 1) * 100);\r
+            }\r
+        }\r
+        duration = Math.min(duration, MAX_SETTLE_DURATION);\r
+\r
+        // Reset the "scroll started" flag. It will be flipped to true in all places\r
+        // where we call computeScrollOffset().\r
+        if (isHorizontal()) {\r
+            mIsScrollStarted = false;\r
+        }\r
+        mScroller.startScroll(sx, sy, dx, dy, duration);\r
+        ViewCompat.postInvalidateOnAnimation(this);\r
+    }\r
+\r
+    private boolean isHorizontal() {\r
+        return mDirection.equalsIgnoreCase(Direction.HORIZONTAL.name());\r
+    }\r
+\r
+    ItemInfo addNewItem(int position, int index) {\r
+        ItemInfo ii = new ItemInfo();\r
+        ii.position = position;\r
+        ii.object = mAdapter.instantiateItem(this, position);\r
+        if (isHorizontal()) {\r
+            ii.widthFactor = mAdapter.getPageWidth(position);\r
+        } else {\r
+            ii.heightFactor = mAdapter.getPageWidth(position);\r
+        }\r
+        if (index < 0 || index >= mItems.size()) {\r
+            mItems.add(ii);\r
+        } else {\r
+            mItems.add(index, ii);\r
+        }\r
+        return ii;\r
+    }\r
+\r
+    void dataSetChanged() {\r
+        // This method only gets called if our observer is attached, so mAdapter is non-null.\r
+\r
+        final int adapterCount = mAdapter.getCount();\r
+        mExpectedAdapterCount = adapterCount;\r
+        boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 &&\r
+                mItems.size() < adapterCount;\r
+        int newCurrItem = mCurItem;\r
+\r
+        boolean isUpdating = false;\r
+        for (int i = 0; i < mItems.size(); i++) {\r
+            final ItemInfo ii = mItems.get(i);\r
+            final int newPos = mAdapter.getItemPosition(ii.object);\r
+\r
+            if (newPos == PagerAdapter.POSITION_UNCHANGED) {\r
+                continue;\r
+            }\r
+\r
+            if (newPos == PagerAdapter.POSITION_NONE) {\r
+                mItems.remove(i);\r
+                i--;\r
+\r
+                if (!isUpdating) {\r
+                    mAdapter.startUpdate(this);\r
+                    isUpdating = true;\r
+                }\r
+\r
+                mAdapter.destroyItem(this, ii.position, ii.object);\r
+                needPopulate = true;\r
+\r
+                if (mCurItem == ii.position) {\r
+                    // Keep the current item in the valid range\r
+                    newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));\r
+                    needPopulate = true;\r
+                }\r
+                continue;\r
+            }\r
+\r
+            if (ii.position != newPos) {\r
+                if (ii.position == mCurItem) {\r
+                    // Our current item changed position. Follow it.\r
+                    newCurrItem = newPos;\r
+                }\r
+\r
+                ii.position = newPos;\r
+                needPopulate = true;\r
+            }\r
+        }\r
+\r
+        if (isUpdating) {\r
+            mAdapter.finishUpdate(this);\r
+        }\r
+\r
+        Collections.sort(mItems, COMPARATOR);\r
+\r
+        if (needPopulate) {\r
+            // Reset our known page widths; populate will recompute them.\r
+            final int childCount = getChildCount();\r
+            for (int i = 0; i < childCount; i++) {\r
+                final View child = getChildAt(i);\r
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                if (!lp.isDecor) {\r
+                    if (isHorizontal()) {\r
+                        lp.widthFactor = 0.f;\r
+                    } else {\r
+                        lp.heightFactor = 0.f;\r
+                    }\r
+                }\r
+            }\r
+\r
+            setCurrentItemInternal(newCurrItem, false, true);\r
+            requestLayout();\r
+        }\r
+    }\r
+\r
+    void populate() {\r
+        populate(mCurItem);\r
+    }\r
+\r
+    void populate(int newCurrentItem) {\r
+        ItemInfo oldCurInfo = null;\r
+        int focusDirection = View.FOCUS_FORWARD;\r
+        if (mCurItem != newCurrentItem) {\r
+            focusDirection = mCurItem < newCurrentItem ? View.FOCUS_DOWN : View.FOCUS_UP;\r
+            oldCurInfo = infoForPosition(mCurItem);\r
+            mCurItem = newCurrentItem;\r
+        }\r
+\r
+        if (mAdapter == null) {\r
+            sortChildDrawingOrder();\r
+            return;\r
+        }\r
+\r
+        // Bail now if we are waiting to populate.  This is to hold off\r
+        // on creating views from the time the user releases their finger to\r
+        // fling to a new position until we have finished the scroll to\r
+        // that position, avoiding glitches from happening at that point.\r
+        if (mPopulatePending) {\r
+            if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");\r
+            sortChildDrawingOrder();\r
+            return;\r
+        }\r
+\r
+        // Also, don't populate until we are attached to a window.  This is to\r
+        // avoid trying to populate before we have restored our view hierarchy\r
+        // state and conflicting with what is restored.\r
+        if (getWindowToken() == null) {\r
+            return;\r
+        }\r
+\r
+        mAdapter.startUpdate(this);\r
+\r
+        final int pageLimit = mOffscreenPageLimit;\r
+        final int startPos = Math.max(0, mCurItem - pageLimit);\r
+        final int N = mAdapter.getCount();\r
+        final int endPos = Math.min(N - 1, mCurItem + pageLimit);\r
+\r
+        if (N != mExpectedAdapterCount) {\r
+            String resName;\r
+            try {\r
+                resName = getResources().getResourceName(getId());\r
+            } catch (Resources.NotFoundException e) {\r
+                resName = Integer.toHexString(getId());\r
+            }\r
+            throw new IllegalStateException("The application's PagerAdapter changed the adapter's" +\r
+                    " contents without calling PagerAdapter#notifyDataSetChanged!" +\r
+                    " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N +\r
+                    " Pager id: " + resName +\r
+                    " Pager class: " + getClass() +\r
+                    " Problematic adapter: " + mAdapter.getClass());\r
+        }\r
+\r
+        // Locate the currently focused item or add it if needed.\r
+        int curIndex = -1;\r
+        ItemInfo curItem = null;\r
+        for (curIndex = 0; curIndex < mItems.size(); curIndex++) {\r
+            final ItemInfo ii = mItems.get(curIndex);\r
+            if (ii.position >= mCurItem) {\r
+                if (ii.position == mCurItem) curItem = ii;\r
+                break;\r
+            }\r
+        }\r
+\r
+        if (curItem == null && N > 0) {\r
+            curItem = addNewItem(mCurItem, curIndex);\r
+        }\r
+\r
+        // Fill 3x the available width or up to the number of offscreen\r
+        // pages requested to either side, whichever is larger.\r
+        // If we have no current item we have no work to do.\r
+        if (curItem != null) {\r
+            if (isHorizontal()) {\r
+                float extraWidthLeft = 0.f;\r
+                int itemIndex = curIndex - 1;\r
+                ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\r
+                final int clientWidth = getClientWidth();\r
+                final float leftWidthNeeded = clientWidth <= 0 ? 0 :\r
+                        2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;\r
+                for (int pos = mCurItem - 1; pos >= 0; pos--) {\r
+                    if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {\r
+                        if (ii == null) {\r
+                            break;\r
+                        }\r
+                        if (pos == ii.position && !ii.scrolling) {\r
+                            mItems.remove(itemIndex);\r
+                            mAdapter.destroyItem(this, pos, ii.object);\r
+                            if (DEBUG) {\r
+                                Log.i(TAG, logDestroyItem(pos, ((View) ii.object)));\r
+                            }\r
+                            itemIndex--;\r
+                            curIndex--;\r
+                            ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\r
+                        }\r
+                    } else if (ii != null && pos == ii.position) {\r
+                        extraWidthLeft += ii.widthFactor;\r
+                        itemIndex--;\r
+                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\r
+                    } else {\r
+                        ii = addNewItem(pos, itemIndex + 1);\r
+                        extraWidthLeft += ii.widthFactor;\r
+                        curIndex++;\r
+                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\r
+                    }\r
+                }\r
+\r
+                float extraWidthRight = curItem.widthFactor;\r
+                itemIndex = curIndex + 1;\r
+                if (extraWidthRight < 2.f) {\r
+                    ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\r
+                    final float rightWidthNeeded = clientWidth <= 0 ? 0 :\r
+                            (float) getPaddingRight() / (float) clientWidth + 2.f;\r
+                    for (int pos = mCurItem + 1; pos < N; pos++) {\r
+                        if (extraWidthRight >= rightWidthNeeded && pos > endPos) {\r
+                            if (ii == null) {\r
+                                break;\r
+                            }\r
+                            if (pos == ii.position && !ii.scrolling) {\r
+                                mItems.remove(itemIndex);\r
+                                mAdapter.destroyItem(this, pos, ii.object);\r
+                                if (DEBUG) {\r
+                                    Log.i(TAG, logDestroyItem(pos, ((View) ii.object)));\r
+                                }\r
+                                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\r
+                            }\r
+                        } else if (ii != null && pos == ii.position) {\r
+                            extraWidthRight += ii.widthFactor;\r
+                            itemIndex++;\r
+                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\r
+                        } else {\r
+                            ii = addNewItem(pos, itemIndex);\r
+                            itemIndex++;\r
+                            extraWidthRight += ii.widthFactor;\r
+                            ii = itemIndex < mItems.size()\r
+                                    ? mItems.get(itemIndex) : null;\r
+                        }\r
+                    }\r
+                }\r
+            } else {\r
+                float extraHeightTop = 0.f;\r
+                int itemIndex = curIndex - 1;\r
+                ItemInfo ii\r
+                        = itemIndex >= 0 ? mItems.get(itemIndex) : null;\r
+                final int clientHeight = getClientHeight();\r
+                final float topHeightNeeded = clientHeight <= 0 ? 0 :\r
+                        2.f - curItem.heightFactor\r
+                                + (float) getPaddingLeft() / (float) clientHeight;\r
+                for (int pos = mCurItem - 1; pos >= 0; pos--) {\r
+                    if (extraHeightTop >= topHeightNeeded && pos < startPos) {\r
+                        if (ii == null) {\r
+                            break;\r
+                        }\r
+                        if (pos == ii.position && !ii.scrolling) {\r
+                            mItems.remove(itemIndex);\r
+                            mAdapter.destroyItem(this, pos, ii.object);\r
+                            if (DEBUG) {\r
+                                Log.i(TAG, logDestroyItem(pos, ((View) ii.object)));\r
+                            }\r
+                            itemIndex--;\r
+                            curIndex--;\r
+                            ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\r
+                        }\r
+                    } else if (ii != null && pos == ii.position) {\r
+                        extraHeightTop += ii.heightFactor;\r
+                        itemIndex--;\r
+                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\r
+                    } else {\r
+                        ii = addNewItem(pos, itemIndex + 1);\r
+                        extraHeightTop += ii.heightFactor;\r
+                        curIndex++;\r
+                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\r
+                    }\r
+                }\r
+\r
+                float extraHeightBottom = curItem.heightFactor;\r
+                itemIndex = curIndex + 1;\r
+                if (extraHeightBottom < 2.f) {\r
+                    ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\r
+                    final float bottomHeightNeeded = clientHeight <= 0 ? 0 :\r
+                            (float) getPaddingRight() / (float) clientHeight + 2.f;\r
+                    for (int pos = mCurItem + 1; pos < N; pos++) {\r
+                        if (extraHeightBottom >= bottomHeightNeeded && pos > endPos) {\r
+                            if (ii == null) {\r
+                                break;\r
+                            }\r
+                            if (pos == ii.position && !ii.scrolling) {\r
+                                mItems.remove(itemIndex);\r
+                                mAdapter.destroyItem(this, pos, ii.object);\r
+                                if (DEBUG) {\r
+                                    Log.i(TAG, logDestroyItem(pos, ((View) ii.object)));\r
+                                }\r
+                                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\r
+                            }\r
+                        } else if (ii != null && pos == ii.position) {\r
+                            extraHeightBottom += ii.heightFactor;\r
+                            itemIndex++;\r
+                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\r
+                        } else {\r
+                            ii = addNewItem(pos, itemIndex);\r
+                            itemIndex++;\r
+                            extraHeightBottom += ii.heightFactor;\r
+                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+\r
+            calculatePageOffsets(curItem, curIndex, oldCurInfo);\r
+        }\r
+\r
+        if (DEBUG) {\r
+            Log.i(TAG, "Current page list:");\r
+            for (int i = 0; i < mItems.size(); i++) {\r
+                Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);\r
+            }\r
+        }\r
+\r
+        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);\r
+\r
+        mAdapter.finishUpdate(this);\r
+\r
+        // Check width measurement of current pages and drawing sort order.\r
+        // Update LayoutParams as needed.\r
+        final int childCount = getChildCount();\r
+        if (isHorizontal()) {\r
+            for (int i = 0; i < childCount; i++) {\r
+                final View child = getChildAt(i);\r
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                lp.childIndex = i;\r
+                if (!lp.isDecor && lp.widthFactor == 0.f) {\r
+                    // 0 means requery the adapter for this, it doesn't have a valid width.\r
+                    final ItemInfo ii = infoForChild(child);\r
+                    if (ii != null) {\r
+                        lp.widthFactor = ii.widthFactor;\r
+                        lp.position = ii.position;\r
+                    }\r
+                }\r
+            }\r
+            sortChildDrawingOrder();\r
+\r
+            if (hasFocus()) {\r
+                View currentFocused = findFocus();\r
+                ItemInfo ii\r
+                        = currentFocused != null ? infoForAnyChild(currentFocused) : null;\r
+                if (ii == null || ii.position != mCurItem) {\r
+                    for (int i = 0; i < getChildCount(); i++) {\r
+                        View child = getChildAt(i);\r
+                        ii = infoForChild(child);\r
+                        if (ii != null\r
+                                && ii.position == mCurItem &&\r
+                                child.requestFocus(View.FOCUS_FORWARD)) {\r
+                            break;\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        } else {\r
+            for (int i = 0; i < childCount; i++) {\r
+                final View child = getChildAt(i);\r
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                lp.childIndex = i;\r
+                if (!lp.isDecor && lp.heightFactor == 0.f) {\r
+                    final ItemInfo ii = infoForChild(child);\r
+                    if (ii != null) {\r
+                        lp.heightFactor = ii.heightFactor;\r
+                        lp.position = ii.position;\r
+                    }\r
+                }\r
+            }\r
+            sortChildDrawingOrder();\r
+\r
+            if (hasFocus()) {\r
+                View currentFocused = findFocus();\r
+                ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;\r
+                if (ii == null || ii.position != mCurItem) {\r
+                    for (int i = 0; i < getChildCount(); i++) {\r
+                        View child = getChildAt(i);\r
+                        ii = infoForChild(child);\r
+                        if (ii != null && ii.position == mCurItem\r
+                                && child.requestFocus(focusDirection)) {\r
+//                        if (child.requestFocus(focusDirection)) {\r
+                            break;\r
+                            // }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    private void sortChildDrawingOrder() {\r
+        if (mDrawingOrder != DRAW_ORDER_DEFAULT) {\r
+            if (mDrawingOrderedChildren == null) {\r
+                mDrawingOrderedChildren = new ArrayList<View>();\r
+            } else {\r
+                mDrawingOrderedChildren.clear();\r
+            }\r
+            final int childCount = getChildCount();\r
+            for (int i = 0; i < childCount; i++) {\r
+                final View child = getChildAt(i);\r
+                mDrawingOrderedChildren.add(child);\r
+            }\r
+            Collections.sort(mDrawingOrderedChildren, sPositionComparator);\r
+        }\r
+    }\r
+\r
+    private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {\r
+        final int N = mAdapter.getCount();\r
+        if (isHorizontal()) {\r
+            final int width = getClientWidth();\r
+            final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;\r
+            // Fix up offsets for later layout.\r
+            if (oldCurInfo != null) {\r
+                final int oldCurPosition = oldCurInfo.position;\r
+                // Base offsets off of oldCurInfo.\r
+                if (oldCurPosition < curItem.position) {\r
+                    int itemIndex = 0;\r
+                    ItemInfo ii = null;\r
+                    float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;\r
+                    for (int pos = oldCurPosition + 1;\r
+                             pos <= curItem.position && itemIndex < mItems.size(); pos++) {\r
+                        ii = mItems.get(itemIndex);\r
+                        while (pos > ii.position && itemIndex < mItems.size() - 1) {\r
+                            itemIndex++;\r
+                            ii = mItems.get(itemIndex);\r
+                        }\r
+                        while (pos < ii.position) {\r
+                            // We don't have an item populated for this,\r
+                            // ask the adapter for an offset.\r
+                            offset += mAdapter.getPageWidth(pos) + marginOffset;\r
+                            pos++;\r
+                        }\r
+                        ii.offset = offset;\r
+                        offset += ii.widthFactor + marginOffset;\r
+                    }\r
+                } else if (oldCurPosition > curItem.position) {\r
+                    int itemIndex = mItems.size() - 1;\r
+                    ItemInfo ii = null;\r
+                    float offset = oldCurInfo.offset;\r
+                    for (int pos = oldCurPosition - 1;\r
+                            pos >= curItem.position && itemIndex >= 0; pos--) {\r
+                        ii = mItems.get(itemIndex);\r
+                        while (pos < ii.position && itemIndex > 0) {\r
+                            itemIndex--;\r
+                            ii = mItems.get(itemIndex);\r
+                        }\r
+                        while (pos > ii.position) {\r
+                            // We don't have an item populated for this,\r
+                            // ask the adapter for an offset.\r
+                            offset -= mAdapter.getPageWidth(pos) + marginOffset;\r
+                            pos--;\r
+                        }\r
+                        offset -= ii.widthFactor + marginOffset;\r
+                        ii.offset = offset;\r
+                    }\r
+                }\r
+            }\r
+\r
+            // Base all offsets off of curItem.\r
+            final int itemCount = mItems.size();\r
+            float offset = curItem.offset;\r
+            int pos = curItem.position - 1;\r
+            mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;\r
+            mLastOffset = curItem.position == N - 1 ?\r
+                    curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;\r
+            // Previous pages\r
+            for (int i = curIndex - 1; i >= 0; i--, pos--) {\r
+                final ItemInfo ii = mItems.get(i);\r
+                while (pos > ii.position) {\r
+                    offset -= mAdapter.getPageWidth(pos--) + marginOffset;\r
+                }\r
+                offset -= ii.widthFactor + marginOffset;\r
+                ii.offset = offset;\r
+                if (ii.position == 0) mFirstOffset = offset;\r
+            }\r
+            offset = curItem.offset + curItem.widthFactor + marginOffset;\r
+            pos = curItem.position + 1;\r
+            // Next pages\r
+            for (int i = curIndex + 1; i < itemCount; i++, pos++) {\r
+                final ItemInfo ii = mItems.get(i);\r
+                while (pos < ii.position) {\r
+                    offset += mAdapter.getPageWidth(pos++) + marginOffset;\r
+                }\r
+                if (ii.position == N - 1) {\r
+                    mLastOffset = offset + ii.widthFactor - 1;\r
+                }\r
+                ii.offset = offset;\r
+                offset += ii.widthFactor + marginOffset;\r
+            }\r
+        } else {\r
+            final int height = getClientHeight();\r
+            final float marginOffset = height > 0 ? (float) mPageMargin / height : 0;\r
+            // Fix up offsets for later layout.\r
+            if (oldCurInfo != null) {\r
+                final int oldCurPosition = oldCurInfo.position;\r
+                // Base offsets off of oldCurInfo.\r
+                if (oldCurPosition < curItem.position) {\r
+                    int itemIndex = 0;\r
+                    ItemInfo ii = null;\r
+                    float offset = oldCurInfo.offset + oldCurInfo.heightFactor + marginOffset;\r
+                    for (int pos = oldCurPosition + 1;\r
+                            pos <= curItem.position && itemIndex < mItems.size(); pos++) {\r
+                        ii = mItems.get(itemIndex);\r
+                        while (pos > ii.position && itemIndex < mItems.size() - 1) {\r
+                            itemIndex++;\r
+                            ii = mItems.get(itemIndex);\r
+                        }\r
+                        while (pos < ii.position) {\r
+                            // We don't have an item populated for this,\r
+                            // ask the adapter for an offset.\r
+                            offset += mAdapter.getPageWidth(pos) + marginOffset;\r
+                            pos++;\r
+                        }\r
+                        ii.offset = offset;\r
+                        offset += ii.heightFactor + marginOffset;\r
+                    }\r
+                } else if (oldCurPosition > curItem.position) {\r
+                    int itemIndex = mItems.size() - 1;\r
+                    ItemInfo ii = null;\r
+                    float offset = oldCurInfo.offset;\r
+                    for (int pos = oldCurPosition - 1;\r
+                            pos >= curItem.position && itemIndex >= 0;\r
+                                pos--) {\r
+                        ii = mItems.get(itemIndex);\r
+                        while (pos < ii.position && itemIndex > 0) {\r
+                            itemIndex--;\r
+                            ii = mItems.get(itemIndex);\r
+                        }\r
+                        while (pos > ii.position) {\r
+                            // We don't have an item populated for this,\r
+                            // ask the adapter for an offset.\r
+                            offset -= mAdapter.getPageWidth(pos) + marginOffset;\r
+                            pos--;\r
+                        }\r
+                        offset -= ii.heightFactor + marginOffset;\r
+                        ii.offset = offset;\r
+                    }\r
+                }\r
+            }\r
+\r
+            // Base all offsets off of curItem.\r
+            final int itemCount = mItems.size();\r
+            float offset = curItem.offset;\r
+            int pos = curItem.position - 1;\r
+            mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;\r
+            mLastOffset = curItem.position == N - 1 ?\r
+                    curItem.offset + curItem.heightFactor - 1 : Float.MAX_VALUE;\r
+            // Previous pages\r
+            for (int i = curIndex - 1; i >= 0; i--, pos--) {\r
+                final ItemInfo ii = mItems.get(i);\r
+                while (pos > ii.position) {\r
+                    offset -= mAdapter.getPageWidth(pos--) + marginOffset;\r
+                }\r
+                offset -= ii.heightFactor + marginOffset;\r
+                ii.offset = offset;\r
+                if (ii.position == 0) mFirstOffset = offset;\r
+            }\r
+            offset = curItem.offset + curItem.heightFactor + marginOffset;\r
+            pos = curItem.position + 1;\r
+            // Next pages\r
+            for (int i = curIndex + 1; i < itemCount; i++, pos++) {\r
+                final ItemInfo ii = mItems.get(i);\r
+                while (pos < ii.position) {\r
+                    offset += mAdapter.getPageWidth(pos++) + marginOffset;\r
+                }\r
+                if (ii.position == N - 1) {\r
+                    mLastOffset = offset + ii.heightFactor - 1;\r
+                }\r
+                ii.offset = offset;\r
+                offset += ii.heightFactor + marginOffset;\r
+            }\r
+        }\r
+\r
+        mNeedCalculatePageOffsets = false;\r
+    }\r
+\r
+    /**\r
+     * This is the persistent state that is saved by ViewPager.  Only needed\r
+     * if you are creating a sublass of ViewPager that must save its own\r
+     * state, in which case it should implement a subclass of this which\r
+     * contains that state.\r
+     */\r
+    public static class SavedState extends BaseSavedState {\r
+        int position;\r
+        Parcelable adapterState;\r
+        ClassLoader loader;\r
+\r
+        public SavedState(Parcelable superState) {\r
+            super(superState);\r
+        }\r
+\r
+        @Override\r
+        public void writeToParcel(Parcel out, int flags) {\r
+            super.writeToParcel(out, flags);\r
+            out.writeInt(position);\r
+            out.writeParcelable(adapterState, flags);\r
+        }\r
+\r
+        @Override\r
+        public String toString() {\r
+            return "FragmentPager.SavedState{"\r
+                    + Integer.toHexString(System.identityHashCode(this))\r
+                    + " position=" + position + "}";\r
+        }\r
+\r
+        public static final Parcelable.Creator<SavedState> CREATOR\r
+                = ParcelableCompat\r
+                .newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {\r
+                    @Override\r
+                    public SavedState\r
+                            createFromParcel(Parcel in, ClassLoader loader) {\r
+                                return new SavedState(in, loader);\r
+                            }\r
+\r
+                            @Override\r
+                            public SavedState[] newArray(int size) {\r
+                                return new SavedState[size];\r
+                            }\r
+                });\r
+\r
+        SavedState(Parcel in, ClassLoader loader) {\r
+            super(in);\r
+            if (loader == null) {\r
+                loader = getClass().getClassLoader();\r
+            }\r
+            position = in.readInt();\r
+            adapterState = in.readParcelable(loader);\r
+            this.loader = loader;\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public Parcelable onSaveInstanceState() {\r
+        Parcelable superState = super.onSaveInstanceState();\r
+        SavedState ss = new SavedState(superState);\r
+        ss.position = mCurItem;\r
+        if (mAdapter != null) {\r
+            ss.adapterState = mAdapter.saveState();\r
+        }\r
+        return ss;\r
+    }\r
+\r
+    @Override\r
+    public void onRestoreInstanceState(Parcelable state) {\r
+        if (!(state instanceof SavedState)) {\r
+            super.onRestoreInstanceState(state);\r
+            return;\r
+        }\r
+\r
+        SavedState ss = (SavedState) state;\r
+        super.onRestoreInstanceState(ss.getSuperState());\r
+\r
+        if (mAdapter != null) {\r
+            mAdapter.restoreState(ss.adapterState, ss.loader);\r
+            setCurrentItemInternal(ss.position, false, true);\r
+        } else {\r
+            mRestoredCurItem = ss.position;\r
+            mRestoredAdapterState = ss.adapterState;\r
+            mRestoredClassLoader = ss.loader;\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void addView(View child, int index, ViewGroup.LayoutParams params) {\r
+        if (!checkLayoutParams(params)) {\r
+            params = generateLayoutParams(params);\r
+        }\r
+        final LayoutParams lp = (LayoutParams) params;\r
+        lp.isDecor |= child instanceof Decor;\r
+        if (mInLayout) {\r
+            if (lp != null && lp.isDecor) {\r
+                throw new IllegalStateException("Cannot add pager decor view during layout");\r
+            }\r
+            lp.needsMeasure = true;\r
+            addViewInLayout(child, index, params);\r
+        } else {\r
+            super.addView(child, index, params);\r
+        }\r
+\r
+        if (USE_CACHE) {\r
+            if (child.getVisibility() != GONE) {\r
+                child.setDrawingCacheEnabled(mScrollingCacheEnabled);\r
+            } else {\r
+                child.setDrawingCacheEnabled(false);\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void removeView(View view) {\r
+        if (mInLayout) {\r
+            removeViewInLayout(view);\r
+        } else {\r
+            super.removeView(view);\r
+        }\r
+    }\r
+\r
+    ItemInfo infoForChild(View child) {\r
+        for (int i = 0; i < mItems.size(); i++) {\r
+            ItemInfo ii = mItems.get(i);\r
+            if (mAdapter.isViewFromObject(child, ii.object)) {\r
+                return ii;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    ItemInfo infoForAnyChild(View child) {\r
+        ViewParent parent;\r
+        while ((parent = child.getParent()) != this) {\r
+            if (parent == null || !(parent instanceof View)) {\r
+                return null;\r
+            }\r
+            child = (View) parent;\r
+        }\r
+        return infoForChild(child);\r
+    }\r
+\r
+    ItemInfo infoForPosition(int position) {\r
+        for (int i = 0; i < mItems.size(); i++) {\r
+            ItemInfo ii = mItems.get(i);\r
+            if (ii.position == position) {\r
+                return ii;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    @Override\r
+    protected void onAttachedToWindow() {\r
+        super.onAttachedToWindow();\r
+        mFirstLayout = true;\r
+    }\r
+\r
+    @Override\r
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\r
+        // For simple implementation, our internal size is always 0.\r
+        // We depend on the container to specify the layout size of\r
+        // our view.  We can't really know what it is since we will be\r
+        // adding and removing different arbitrary views and do not\r
+        // want the layout to change as this happens.\r
+        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),\r
+                getDefaultSize(0, heightMeasureSpec));\r
+\r
+        int childWidthSize = 0;\r
+        int childHeightSize = 0;\r
+        if (isHorizontal()) {\r
+            int measuredWidth = getMeasuredWidth();\r
+            int maxGutterSize = measuredWidth / 10;\r
+            mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);\r
+\r
+            // Children are just made to fill our space.\r
+            childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();\r
+            childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();\r
+        } else {\r
+            int measuredHeight = getMeasuredHeight();\r
+            int maxGutterSize = measuredHeight / 10;\r
+            mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);\r
+\r
+            // Children are just made to fill our space.\r
+            childWidthSize = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();\r
+            childHeightSize = measuredHeight - getPaddingTop() - getPaddingBottom();\r
+        }\r
+\r
+        /*\r
+         * Make sure all children have been properly measured. Decor views first.\r
+         * Right now we cheat and make this less complicated by assuming decor\r
+         * views won't intersect. We will pin to edges based on gravity.\r
+         */\r
+        int size = getChildCount();\r
+        for (int i = 0; i < size; ++i) {\r
+            final View child = getChildAt(i);\r
+            if (child.getVisibility() != GONE) {\r
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                if (lp != null && lp.isDecor) {\r
+                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;\r
+                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;\r
+                    int widthMode = MeasureSpec.AT_MOST;\r
+                    int heightMode = MeasureSpec.AT_MOST;\r
+                    boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;\r
+                    boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;\r
+\r
+                    if (consumeVertical) {\r
+                        widthMode = MeasureSpec.EXACTLY;\r
+                    } else if (consumeHorizontal) {\r
+                        heightMode = MeasureSpec.EXACTLY;\r
+                    }\r
+\r
+                    int widthSize = childWidthSize;\r
+                    int heightSize = childHeightSize;\r
+                    if (lp.width != LayoutParams.WRAP_CONTENT) {\r
+                        widthMode = MeasureSpec.EXACTLY;\r
+                        if (lp.width != LayoutParams.FILL_PARENT) {\r
+                            widthSize = lp.width;\r
+                        }\r
+                    }\r
+                    if (lp.height != LayoutParams.WRAP_CONTENT) {\r
+                        heightMode = MeasureSpec.EXACTLY;\r
+                        if (lp.height != LayoutParams.FILL_PARENT) {\r
+                            heightSize = lp.height;\r
+                        }\r
+                    }\r
+                    final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);\r
+                    final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);\r
+                    child.measure(widthSpec, heightSpec);\r
+\r
+                    if (consumeVertical) {\r
+                        childHeightSize -= child.getMeasuredHeight();\r
+                    } else if (consumeHorizontal) {\r
+                        childWidthSize -= child.getMeasuredWidth();\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);\r
+        mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);\r
+\r
+        // Make sure we have created all fragments that we need to have shown.\r
+        mInLayout = true;\r
+        populate();\r
+        mInLayout = false;\r
+\r
+        // Page views next.\r
+        size = getChildCount();\r
+        for (int i = 0; i < size; ++i) {\r
+            final View child = getChildAt(i);\r
+            if (child.getVisibility() != GONE) {\r
+                if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child\r
+                        + ": " + mChildWidthMeasureSpec);\r
+\r
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                if (lp == null || !lp.isDecor) {\r
+                    if (isHorizontal()) {\r
+                        int widthSpec = MeasureSpec.makeMeasureSpec(\r
+                                (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);\r
+                        child.measure(widthSpec, mChildHeightMeasureSpec);\r
+                    } else {\r
+                        int heightSpec = MeasureSpec.makeMeasureSpec(\r
+                                (int) (childHeightSize * lp.heightFactor), MeasureSpec.EXACTLY);\r
+                        child.measure(mChildWidthMeasureSpec, heightSpec);\r
+                    }\r
+                }\r
+\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\r
+        super.onSizeChanged(w, h, oldw, oldh);\r
+\r
+        // Make sure scroll position is set correctly.\r
+\r
+        if (isHorizontal()) {\r
+            if (w != oldw) {\r
+                recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin, 0, 0);\r
+            }\r
+        } else {\r
+            if (h != oldh) {\r
+                recomputeScrollPosition(0, 0, mPageMargin, mPageMargin, h, oldh);\r
+            }\r
+        }\r
+    }\r
+\r
+\r
+    private void recomputeScrollPosition(int width, int oldWidth, int margin,\r
+                                                int oldMargin, int height, int oldHeight) {\r
+        if (isHorizontal()) {\r
+            if (oldWidth > 0 && !mItems.isEmpty()) {\r
+                if (!mScroller.isFinished()) {\r
+                    mScroller.setFinalX(\r
+                            getCurrentItem() * getClientWidth());\r
+                } else {\r
+                    final int widthWithMargin\r
+                            = width - getPaddingLeft() - getPaddingRight() + margin;\r
+                    final int oldWidthWithMargin\r
+                            = oldWidth - getPaddingLeft() - getPaddingRight()\r
+                                + oldMargin;\r
+                    final int xpos = getScrollX();\r
+                    final float pageOffset = (float) xpos / oldWidthWithMargin;\r
+                    final int newOffsetPixels = (int) (pageOffset * widthWithMargin);\r
+\r
+                    scrollTo(newOffsetPixels, getScrollY());\r
+                }\r
+            } else {\r
+                final ItemInfo ii = infoForPosition(mCurItem);\r
+                final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;\r
+                final int scrollPos = (int) (scrollOffset *\r
+                        (width - getPaddingLeft() - getPaddingRight()));\r
+                if (scrollPos != getScrollX()) {\r
+                    completeScroll(false);\r
+                    scrollTo(scrollPos, getScrollY());\r
+                }\r
+            }\r
+        } else {\r
+            final int heightWithMargin = height - getPaddingTop() - getPaddingBottom() + margin;\r
+            final int oldHeightWithMargin = oldHeight - getPaddingTop() - getPaddingBottom()\r
+                    + oldMargin;\r
+            final int ypos = getScrollY();\r
+            final float pageOffset = (float) ypos / oldHeightWithMargin;\r
+            final int newOffsetPixels = (int) (pageOffset * heightWithMargin);\r
+\r
+            scrollTo(getScrollX(), newOffsetPixels);\r
+            if (!mScroller.isFinished()) {\r
+                // We now return to your regularly scheduled scroll, already in progress.\r
+                final int newDuration = mScroller.getDuration() - mScroller.timePassed();\r
+                ItemInfo targetInfo = infoForPosition(mCurItem);\r
+                mScroller.startScroll(0, newOffsetPixels,\r
+                        0, (int) (targetInfo.offset * height), newDuration);\r
+            } else {\r
+                final ItemInfo ii = infoForPosition(mCurItem);\r
+                final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;\r
+                final int scrollPos = (int) (scrollOffset *\r
+                        (height - getPaddingTop() - getPaddingBottom()));\r
+                if (scrollPos != getScrollY()) {\r
+                    completeScroll(false);\r
+                    scrollTo(getScrollX(), scrollPos);\r
+                }\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {\r
+        final int count = getChildCount();\r
+        int width = r - l;\r
+        int height = b - t;\r
+        int paddingLeft = getPaddingLeft();\r
+        int paddingTop = getPaddingTop();\r
+        int paddingRight = getPaddingRight();\r
+        int paddingBottom = getPaddingBottom();\r
+        final int scrollX = getScrollX();\r
+        final int scrollY = getScrollY();\r
+\r
+        int decorCount = 0;\r
+\r
+        // First pass - decor views. We need to do this in two passes so that\r
+        // we have the proper offsets for non-decor views later.\r
+        for (int i = 0; i < count; i++) {\r
+            final View child = getChildAt(i);\r
+            if (child.getVisibility() != GONE) {\r
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                int childLeft = 0;\r
+                int childTop = 0;\r
+                if (lp.isDecor) {\r
+                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;\r
+                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;\r
+                    switch (hgrav) {\r
+                        default:\r
+                            childLeft = paddingLeft;\r
+                            break;\r
+                        case Gravity.LEFT:\r
+                            childLeft = paddingLeft;\r
+                            paddingLeft += child.getMeasuredWidth();\r
+                            break;\r
+                        case Gravity.CENTER_HORIZONTAL:\r
+                            childLeft = Math.max((width - child.getMeasuredWidth()) / 2,\r
+                                    paddingLeft);\r
+                            break;\r
+                        case Gravity.RIGHT:\r
+                            childLeft = width - paddingRight - child.getMeasuredWidth();\r
+                            paddingRight += child.getMeasuredWidth();\r
+                            break;\r
+                    }\r
+                    switch (vgrav) {\r
+                        default:\r
+                            childTop = paddingTop;\r
+                            break;\r
+                        case Gravity.TOP:\r
+                            childTop = paddingTop;\r
+                            paddingTop += child.getMeasuredHeight();\r
+                            break;\r
+                        case Gravity.CENTER_VERTICAL:\r
+                            childTop = Math.max((height - child.getMeasuredHeight()) / 2,\r
+                                    paddingTop);\r
+                            break;\r
+                        case Gravity.BOTTOM:\r
+                            childTop = height - paddingBottom - child.getMeasuredHeight();\r
+                            paddingBottom += child.getMeasuredHeight();\r
+                            break;\r
+                    }\r
+                    if (isHorizontal()) {\r
+                        childLeft += scrollX;\r
+                    } else {\r
+                        childTop += scrollY;\r
+                    }\r
+                    child.layout(childLeft, childTop,\r
+                            childLeft + child.getMeasuredWidth(),\r
+                            childTop + child.getMeasuredHeight());\r
+                    decorCount++;\r
+                }\r
+            }\r
+        }\r
+\r
+        if (isHorizontal()) {\r
+            final int childWidth = width - paddingLeft - paddingRight;\r
+            // Page views. Do this once we have the right padding offsets from above.\r
+            for (int i = 0; i < count; i++) {\r
+                final View child = getChildAt(i);\r
+                if (child.getVisibility() != GONE) {\r
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                    ItemInfo ii;\r
+                    if (!lp.isDecor && (ii = infoForChild(child)) != null) {\r
+                        int loff = (int) (childWidth * ii.offset);\r
+                        int childLeft = paddingLeft + loff;\r
+                        int childTop = paddingTop;\r
+                        if (lp.needsMeasure) {\r
+                            // This was added during layout and needs measurement.\r
+                            // Do it now that we know what we're working with.\r
+                            lp.needsMeasure = false;\r
+                            final int widthSpec = MeasureSpec.makeMeasureSpec(\r
+                                    (int) (childWidth * lp.widthFactor),\r
+                                    MeasureSpec.EXACTLY);\r
+                            final int heightSpec = MeasureSpec.makeMeasureSpec(\r
+                                    (int) (height - paddingTop - paddingBottom),\r
+                                    MeasureSpec.EXACTLY);\r
+                            child.measure(widthSpec, heightSpec);\r
+                        }\r
+                        if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object\r
+                                + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()\r
+                                + "x" + child.getMeasuredHeight());\r
+                        child.layout(childLeft, childTop,\r
+                                childLeft + child.getMeasuredWidth(),\r
+                                childTop + child.getMeasuredHeight());\r
+                    }\r
+                }\r
+            }\r
+            mTopPageBounds = paddingTop;\r
+            mBottomPageBounds = height - paddingBottom;\r
+        } else {\r
+            final int childHeight = height - paddingTop - paddingBottom;\r
+            // Page views. Do this once we have the right padding offsets from above.\r
+            for (int i = 0; i < count; i++) {\r
+                final View child = getChildAt(i);\r
+                if (child.getVisibility() != GONE) {\r
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                    ItemInfo ii;\r
+                    if (!lp.isDecor && (ii = infoForChild(child)) != null) {\r
+                        int toff = (int) (childHeight * ii.offset);\r
+                        int childLeft = paddingLeft;\r
+                        int childTop = paddingTop + toff;\r
+                        if (lp.needsMeasure) {\r
+                            // This was added during layout and needs measurement.\r
+                            // Do it now that we know what we're working with.\r
+                            lp.needsMeasure = false;\r
+                            final int widthSpec = MeasureSpec.makeMeasureSpec(\r
+                                    (int) (width - paddingLeft - paddingRight),\r
+                                    MeasureSpec.EXACTLY);\r
+                            final int heightSpec = MeasureSpec.makeMeasureSpec(\r
+                                    (int) (childHeight * lp.heightFactor),\r
+                                    MeasureSpec.EXACTLY);\r
+                            child.measure(widthSpec, heightSpec);\r
+                        }\r
+                        if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object\r
+                                + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()\r
+                                + "x" + child.getMeasuredHeight());\r
+                        child.layout(childLeft, childTop,\r
+                                childLeft + child.getMeasuredWidth(),\r
+                                childTop + child.getMeasuredHeight());\r
+                    }\r
+                }\r
+            }\r
+            mLeftPageBounds = paddingLeft;\r
+            mRightPageBounds = width - paddingRight;\r
+        }\r
+        mDecorChildCount = decorCount;\r
+\r
+        if (mFirstLayout) {\r
+            scrollToItem(mCurItem, false, 0, false);\r
+        }\r
+        mFirstLayout = false;\r
+    }\r
+\r
+    @Override\r
+    public void computeScroll() {\r
+        mIsScrollStarted = true;\r
+        if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {\r
+            int oldX = getScrollX();\r
+            int oldY = getScrollY();\r
+            int x = mScroller.getCurrX();\r
+            int y = mScroller.getCurrY();\r
+\r
+            if (oldX != x || oldY != y) {\r
+                scrollTo(x, y);\r
+                if (isHorizontal()) {\r
+                    if (!pageScrolled(x, 0)) {\r
+                        mScroller.abortAnimation();\r
+                        scrollTo(0, y);\r
+                    }\r
+                } else {\r
+                    if (!pageScrolled(0, y)) {\r
+                        mScroller.abortAnimation();\r
+                        scrollTo(x, 0);\r
+                    }\r
+                }\r
+            }\r
+\r
+            // Keep on drawing until the animation has finished.\r
+            ViewCompat.postInvalidateOnAnimation(this);\r
+            return;\r
+        }\r
+\r
+        // Done with scroll, clean up state.\r
+        completeScroll(true);\r
+    }\r
+\r
+    private boolean pageScrolled(int xpos, int ypos) {\r
+        if (mItems.size() == 0) {\r
+            if (mFirstLayout) {\r
+                // If we haven't been laid out yet, we probably just haven't been populated yet.\r
+                // Let's skip this call since it doesn't make sense in this state\r
+                return false;\r
+            }\r
+            mCalledSuper = false;\r
+            onPageScrolled(0, 0, 0);\r
+            if (!mCalledSuper) {\r
+                throw new IllegalStateException(\r
+                        "onPageScrolled did not call superclass implementation");\r
+            }\r
+            return false;\r
+        }\r
+        final ItemInfo ii = infoForCurrentScrollPosition();\r
+        int currentPage = 0;\r
+        float pageOffset = 0;\r
+        int offsetPixels = 0;\r
+        if (isHorizontal()) {\r
+            int width = getClientWidth();\r
+            int widthWithMargin = width + mPageMargin;\r
+            float marginOffset = (float) mPageMargin / width;\r
+            currentPage = ii.position;\r
+            pageOffset = (((float) xpos / width) - ii.offset) /\r
+                    (ii.widthFactor + marginOffset);\r
+            offsetPixels = (int) (pageOffset * widthWithMargin);\r
+        } else {\r
+            int height = getClientHeight();\r
+            int heightWithMargin = height + mPageMargin;\r
+            float marginOffset = (float) mPageMargin / height;\r
+            currentPage = ii.position;\r
+            pageOffset = (((float) ypos / height) - ii.offset) /\r
+                    (ii.heightFactor + marginOffset);\r
+            offsetPixels = (int) (pageOffset * heightWithMargin);\r
+        }\r
+\r
+        mCalledSuper = false;\r
+        onPageScrolled(currentPage, pageOffset, offsetPixels);\r
+        if (!mCalledSuper) {\r
+            throw new IllegalStateException(\r
+                    "onPageScrolled did not call superclass implementation");\r
+        }\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * This method will be invoked when the current page is scrolled, either as part\r
+     * of a programmatically initiated smooth scroll or a user initiated touch scroll.\r
+     * If you override this method you must call through to the superclass implementation\r
+     * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled\r
+     * returns.\r
+     *\r
+     * @param position     Position index of the first page currently being displayed.\r
+     *                     Page position+1 will be visible if positionOffset is nonzero.\r
+     * @param offset       Value from [0, 1) indicating the offset from the page at position.\r
+     * @param offsetPixels Value in pixels indicating the offset from position.\r
+     */\r
+    @CallSuper\r
+    protected void onPageScrolled(int position, float offset, int offsetPixels) {\r
+        // Offset any decor views if needed - keep them on-screen at all times.\r
+        if (isHorizontal()) {\r
+            if (mDecorChildCount > 0) {\r
+                final int scrollX = getScrollX();\r
+                int paddingLeft = getPaddingLeft();\r
+                int paddingRight = getPaddingRight();\r
+                final int width = getWidth();\r
+                final int childCount = getChildCount();\r
+                for (int i = 0; i < childCount; i++) {\r
+                    final View child = getChildAt(i);\r
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                    if (!lp.isDecor) continue;\r
+\r
+                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;\r
+                    int childLeft = 0;\r
+                    switch (hgrav) {\r
+                        default:\r
+                            childLeft = paddingLeft;\r
+                            break;\r
+                        case Gravity.LEFT:\r
+                            childLeft = paddingLeft;\r
+                            paddingLeft += child.getWidth();\r
+                            break;\r
+                        case Gravity.CENTER_HORIZONTAL:\r
+                            childLeft = Math.max((width - child.getMeasuredWidth()) / 2,\r
+                                    paddingLeft);\r
+                            break;\r
+                        case Gravity.RIGHT:\r
+                            childLeft = width - paddingRight - child.getMeasuredWidth();\r
+                            paddingRight += child.getMeasuredWidth();\r
+                            break;\r
+                    }\r
+                    childLeft += scrollX;\r
+\r
+                    final int childOffset = childLeft - child.getLeft();\r
+                    if (childOffset != 0) {\r
+                        child.offsetLeftAndRight(childOffset);\r
+                    }\r
+                }\r
+            }\r
+\r
+            dispatchOnPageScrolled(position, offset, offsetPixels);\r
+\r
+            if (mPageTransformer != null) {\r
+                final int scrollX = getScrollX();\r
+                final int childCount = getChildCount();\r
+                for (int i = 0; i < childCount; i++) {\r
+                    final View child = getChildAt(i);\r
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+\r
+                    if (lp.isDecor) continue;\r
+                    final float transformPos\r
+                            = (float) (child.getLeft() - scrollX) / getClientWidth();\r
+                    mPageTransformer.transformPage(child, transformPos);\r
+                }\r
+            }\r
+        } else {\r
+            if (mDecorChildCount > 0) {\r
+                final int scrollY = getScrollY();\r
+                int paddingTop = getPaddingTop();\r
+                int paddingBottom = getPaddingBottom();\r
+                final int height = getHeight();\r
+                final int childCount = getChildCount();\r
+                for (int i = 0; i < childCount; i++) {\r
+                    final View child = getChildAt(i);\r
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+                    if (!lp.isDecor) continue;\r
+\r
+                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;\r
+                    int childTop = 0;\r
+                    switch (vgrav) {\r
+                        default:\r
+                            childTop = paddingTop;\r
+                            break;\r
+                        case Gravity.TOP:\r
+                            childTop = paddingTop;\r
+                            paddingTop += child.getHeight();\r
+                            break;\r
+                        case Gravity.CENTER_VERTICAL:\r
+                            childTop = Math.max((height - child.getMeasuredHeight()) / 2,\r
+                                    paddingTop);\r
+                            break;\r
+                        case Gravity.BOTTOM:\r
+                            childTop = height - paddingBottom - child.getMeasuredHeight();\r
+                            paddingBottom += child.getMeasuredHeight();\r
+                            break;\r
+                    }\r
+                    childTop += scrollY;\r
+\r
+                    final int childOffset = childTop - child.getTop();\r
+                    if (childOffset != 0) {\r
+                        child.offsetTopAndBottom(childOffset);\r
+                    }\r
+                }\r
+            }\r
+\r
+            if (mOnPageChangeListener != null) {\r
+                mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);\r
+            }\r
+            if (mInternalPageChangeListener != null) {\r
+                mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);\r
+            }\r
+\r
+            if (mPageTransformer != null) {\r
+                final int scrollY = getScrollY();\r
+                final int childCount = getChildCount();\r
+                for (int i = 0; i < childCount; i++) {\r
+                    final View child = getChildAt(i);\r
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();\r
+\r
+                    if (lp.isDecor) continue;\r
+\r
+                    final float transformPos\r
+                            = (float) (child.getTop() - scrollY) / getClientHeight();\r
+                    mPageTransformer.transformPage(child, transformPos);\r
+                }\r
+            }\r
+        }\r
+\r
+        mCalledSuper = true;\r
+    }\r
+\r
+    private void dispatchOnPageScrolled(int position, float offset, int offsetPixels) {\r
+        if (mOnPageChangeListener != null) {\r
+            mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);\r
+        }\r
+        if (mOnPageChangeListeners != null) {\r
+            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {\r
+                OnPageChangeListener listener = mOnPageChangeListeners.get(i);\r
+                if (listener != null) {\r
+                    listener.onPageScrolled(position, offset, offsetPixels);\r
+                }\r
+            }\r
+        }\r
+        if (mInternalPageChangeListener != null) {\r
+            mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);\r
+        }\r
+    }\r
+\r
+    private void dispatchOnPageSelected(int position) {\r
+        if (mOnPageChangeListener != null) {\r
+            mOnPageChangeListener.onPageSelected(position);\r
+        }\r
+        if (mOnPageChangeListeners != null) {\r
+            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {\r
+                OnPageChangeListener listener = mOnPageChangeListeners.get(i);\r
+                if (listener != null) {\r
+                    listener.onPageSelected(position);\r
+                }\r
+            }\r
+        }\r
+        if (mInternalPageChangeListener != null) {\r
+            mInternalPageChangeListener.onPageSelected(position);\r
+        }\r
+    }\r
+\r
+    private void dispatchOnScrollStateChanged(int state) {\r
+        if (mOnPageChangeListener != null) {\r
+            mOnPageChangeListener.onPageScrollStateChanged(state);\r
+        }\r
+        if (mOnPageChangeListeners != null) {\r
+            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {\r
+                OnPageChangeListener listener = mOnPageChangeListeners.get(i);\r
+                if (listener != null) {\r
+                    listener.onPageScrollStateChanged(state);\r
+                }\r
+            }\r
+        }\r
+        if (mInternalPageChangeListener != null) {\r
+            mInternalPageChangeListener.onPageScrollStateChanged(state);\r
+        }\r
+    }\r
+\r
+    private void completeScroll(boolean postEvents) {\r
+        boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;\r
+        if (needPopulate) {\r
+            // Done with scroll, no longer want to cache view drawing.\r
+            setScrollingCacheEnabled(false);\r
+            boolean wasScrolling = !mScroller.isFinished();\r
+            if (wasScrolling) {\r
+                mScroller.abortAnimation();\r
+                int oldX = getScrollX();\r
+                int oldY = getScrollY();\r
+                int x = mScroller.getCurrX();\r
+                int y = mScroller.getCurrY();\r
+                if (oldX != x || oldY != y) {\r
+                    scrollTo(x, y);\r
+                    if (isHorizontal() && x != oldX) {\r
+                        pageScrolled(x, 0);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        mPopulatePending = false;\r
+        for (int i = 0; i < mItems.size(); i++) {\r
+            ItemInfo ii = mItems.get(i);\r
+            if (ii.scrolling) {\r
+                needPopulate = true;\r
+                ii.scrolling = false;\r
+            }\r
+        }\r
+        if (needPopulate) {\r
+            if (postEvents) {\r
+                ViewCompat.postOnAnimation(this, mEndScrollRunnable);\r
+            } else {\r
+                mEndScrollRunnable.run();\r
+            }\r
+        }\r
+    }\r
+\r
+    private boolean isGutterDrag(float x, float dx, float y, float dy) {\r
+        if (isHorizontal()) {\r
+            return (x < mGutterSize && dx > 0) || (x > getWidth() - mGutterSize && dx < 0);\r
+        } else {\r
+            return (y < mGutterSize && dy > 0) || (y > getHeight() - mGutterSize && dy < 0);\r
+        }\r
+    }\r
+\r
+    private void enableLayers(boolean enable) {\r
+        final int childCount = getChildCount();\r
+        for (int i = 0; i < childCount; i++) {\r
+            final int layerType = enable ?\r
+                    ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_NONE;\r
+            ViewCompat.setLayerType(getChildAt(i), layerType, null);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public boolean onInterceptTouchEvent(MotionEvent ev) {\r
+        /*\r
+         * This method JUST determines whether we want to intercept the motion.\r
+         * If we return true, onMotionEvent will be called and we do the actual\r
+         * scrolling there.\r
+         */\r
+\r
+        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;\r
+\r
+        // Always take care of the touch gesture being complete.\r
+        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {\r
+            // Release the drag.\r
+            if (DEBUG) Log.v(TAG, "Intercept done!");\r
+            if (isHorizontal()) {\r
+                resetTouch();\r
+            } else {\r
+                mIsBeingDragged = false;\r
+                mIsUnableToDrag = false;\r
+                mActivePointerId = INVALID_POINTER;\r
+                if (mVelocityTracker != null) {\r
+                    mVelocityTracker.recycle();\r
+                    mVelocityTracker = null;\r
+                }\r
+            }\r
+            return false;\r
+        }\r
+\r
+        // Nothing more to do here if we have decided whether or not we\r
+        // are dragging.\r
+        if (action != MotionEvent.ACTION_DOWN) {\r
+            if (mIsBeingDragged) {\r
+                if (DEBUG) Log.v(TAG, "Intercept returning true!");\r
+                return true;\r
+            }\r
+            if (mIsUnableToDrag) {\r
+                if (DEBUG) Log.v(TAG, "Intercept returning false!");\r
+                return false;\r
+            }\r
+        }\r
+\r
+        if (isHorizontal()) {\r
+\r
+            switch (action) {\r
+                case MotionEvent.ACTION_MOVE: {\r
+                /*\r
+                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check\r
+                 * whether the user has moved far enough from his original down touch.\r
+                 */\r
+\r
+                /*\r
+                * Locally do absolute value. mLastMotionY is set to the y value\r
+                * of the down event.\r
+                */\r
+                    final int activePointerId = mActivePointerId;\r
+                    if (activePointerId == INVALID_POINTER) {\r
+                        break;\r
+                    }\r
+\r
+                    final int pointerIndex\r
+                            = MotionEventCompat.findPointerIndex(ev, activePointerId);\r
+                    final float x = MotionEventCompat.getX(ev, pointerIndex);\r
+                    final float dx = x - mLastMotionX;\r
+                    final float xDiff = Math.abs(dx);\r
+                    final float y = MotionEventCompat.getY(ev, pointerIndex);\r
+                    final float yDiff = Math.abs(y - mInitialMotionY);\r
+\r
+                    if (dx != 0 && !isGutterDrag(mLastMotionX, dx, 0, 0) &&\r
+                            canScroll(this, false, (int) dx, 0, (int) x, (int) y)) {\r
+                        // Nested view has scrollable\r
+                        // area under this point. Let it be handled there.\r
+                        mLastMotionX = x;\r
+                        mLastMotionY = y;\r
+                        mIsUnableToDrag = true;\r
+                        return false;\r
+                    }\r
+                    if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {\r
+                        if (DEBUG) Log.v(TAG, getContext().getString(R.string.debug_start_drag));\r
+                        mIsBeingDragged = true;\r
+                        requestParentDisallowInterceptTouchEvent(true);\r
+                        setScrollState(SCROLL_STATE_DRAGGING);\r
+                        mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :\r
+                                mInitialMotionX - mTouchSlop;\r
+                        mLastMotionY = y;\r
+                        setScrollingCacheEnabled(true);\r
+                    } else if (yDiff > mTouchSlop) {\r
+                        // The finger has moved enough in the vertical\r
+                        // direction to be counted as a drag...  abort\r
+                        // any attempt to drag horizontally, to work correctly\r
+                        // with children that have scrolling containers.\r
+                        if (DEBUG)\r
+                            Log.v(TAG, getContext().getString(R.string.debug_start_unable_drag));\r
+                        mIsUnableToDrag = true;\r
+                    }\r
+                    if (mIsBeingDragged && performDrag(x, 0)) {\r
+                        // Scroll to follow the motion event\r
+                        ViewCompat.postInvalidateOnAnimation(this);\r
+                    }\r
+                    break;\r
+                }\r
+\r
+                case MotionEvent.ACTION_DOWN: {\r
+                /*\r
+                 * Remember location of down touch.\r
+                 * ACTION_DOWN always refers to pointer index 0.\r
+                 */\r
+                    mLastMotionX = mInitialMotionX = ev.getX();\r
+                    mLastMotionY = mInitialMotionY = ev.getY();\r
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\r
+                    mIsUnableToDrag = false;\r
+\r
+                    mIsScrollStarted = true;\r
+                    mScroller.computeScrollOffset();\r
+                    if (mScrollState == SCROLL_STATE_SETTLING &&\r
+                            Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {\r
+                        // Let the user 'catch' the pager as it animates.\r
+                        mScroller.abortAnimation();\r
+                        mPopulatePending = false;\r
+                        populate();\r
+                        mIsBeingDragged = true;\r
+                        requestParentDisallowInterceptTouchEvent(true);\r
+                        setScrollState(SCROLL_STATE_DRAGGING);\r
+                    } else {\r
+                        completeScroll(false);\r
+                        mIsBeingDragged = false;\r
+                    }\r
+\r
+                    if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY\r
+                            + " mIsBeingDragged=" + mIsBeingDragged\r
+                            + "mIsUnableToDrag=" + mIsUnableToDrag);\r
+                    break;\r
+                }\r
+\r
+                case MotionEventCompat.ACTION_POINTER_UP:\r
+                    onSecondaryPointerUp(ev);\r
+                    break;\r
+            }\r
+\r
+           /* if (mVelocityTracker == null) {\r
+                mVelocityTracker = VelocityTracker.obtain();\r
+            }\r
+            mVelocityTracker.addMovement(ev);\r
+*/\r
+        /*\r
+         * The only time we want to intercept motion events is if we are in the\r
+         * drag mode.\r
+         */\r
+        } else {\r
+            switch (action) {\r
+                case MotionEvent.ACTION_MOVE: {\r
+                /*\r
+                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check\r
+                 * whether the user has moved far enough from his original down touch.\r
+                 */\r
+\r
+                /*\r
+                * Locally do absolute value. mLastMotionY is set to the y value\r
+                * of the down event.\r
+                */\r
+                    final int activePointerId = mActivePointerId;\r
+                    if (activePointerId == INVALID_POINTER) {\r
+                        // If we don't have a valid id, the touch down wasn't on content.\r
+                        break;\r
+                    }\r
+\r
+                    final int pointerIndex\r
+                            = MotionEventCompat.findPointerIndex(ev, activePointerId);\r
+                    final float y = MotionEventCompat.getY(ev, pointerIndex);\r
+                    final float dy = y - mLastMotionY;\r
+                    final float yDiff = Math.abs(dy);\r
+                    final float x\r
+                                = MotionEventCompat.getX(ev, pointerIndex);\r
+                    final float xDiff = Math.abs(x - mInitialMotionX);\r
+\r
+                    if (dy != 0 && !isGutterDrag(0, 0, mLastMotionY, dy) &&\r
+                            canScroll(this, false, 0,\r
+                                    (int) dy, (int) x, (int) y)) {\r
+                        // Nested view has scrollable\r
+                        // area under this point.\r
+                        // Let it be handled there.\r
+                        mLastMotionX = x;\r
+                        mLastMotionY = y;\r
+                        mIsUnableToDrag = true;\r
+                        return false;\r
+                    }\r
+                    if (yDiff > mTouchSlop && yDiff * 0.5f > xDiff) {\r
+                        if (DEBUG) Log.v(TAG, getContext().getString(R.string.debug_start_drag));\r
+                        mIsBeingDragged = true;\r
+                        requestParentDisallowInterceptTouchEvent(true);\r
+                        setScrollState(SCROLL_STATE_DRAGGING);\r
+                        mLastMotionY = dy > 0 ? mInitialMotionY + mTouchSlop :\r
+                                mInitialMotionY - mTouchSlop;\r
+                        mLastMotionX = x;\r
+                        setScrollingCacheEnabled(true);\r
+                    } else if (xDiff > mTouchSlop) {\r
+                        // The finger has moved enough in the vertical\r
+                        // direction to be counted as a drag...  abort\r
+                        // any attempt to drag horizontally, to work correctly\r
+                        // with children that have scrolling containers.\r
+                        if (DEBUG)\r
+                            Log.v(TAG, getContext().getString(R.string.debug_start_unable_drag));\r
+                        mIsUnableToDrag = true;\r
+                    }\r
+                    if (mIsBeingDragged && performDrag(0, y)) {\r
+                        // Scroll to follow the motion event\r
+                        ViewCompat.postInvalidateOnAnimation(this);\r
+                    }\r
+                    break;\r
+                }\r
+\r
+                case MotionEvent.ACTION_DOWN: {\r
+                /*\r
+                 * Remember location of down touch.\r
+                 * ACTION_DOWN always refers to pointer index 0.\r
+                 */\r
+                    mLastMotionX = mInitialMotionX = ev.getX();\r
+                    mLastMotionY = mInitialMotionY = ev.getY();\r
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\r
+                    mIsUnableToDrag = false;\r
+\r
+                    mScroller.computeScrollOffset();\r
+                    if (mScrollState == SCROLL_STATE_SETTLING &&\r
+                            Math.abs(mScroller.getFinalY() - mScroller.getCurrY()) > mCloseEnough) {\r
+                        // Let the user 'catch' the pager as it animates.\r
+                        mScroller.abortAnimation();\r
+                        mPopulatePending = false;\r
+                        populate();\r
+                        mIsBeingDragged = true;\r
+                        requestParentDisallowInterceptTouchEvent(true);\r
+                        setScrollState(SCROLL_STATE_DRAGGING);\r
+                    } else {\r
+                        completeScroll(false);\r
+                        mIsBeingDragged = false;\r
+                    }\r
+\r
+                    if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY\r
+                            + " mIsBeingDragged=" + mIsBeingDragged\r
+                            + "mIsUnableToDrag=" + mIsUnableToDrag);\r
+                    break;\r
+                }\r
+\r
+                case MotionEventCompat.ACTION_POINTER_UP:\r
+                    onSecondaryPointerUp(ev);\r
+                    break;\r
+            }\r
+\r
+        }\r
+        if (mVelocityTracker == null) {\r
+            mVelocityTracker = VelocityTracker.obtain();\r
+        }\r
+        mVelocityTracker.addMovement(ev);\r
+        return mIsBeingDragged;\r
+    }\r
+\r
+    @Override\r
+    public boolean onTouchEvent(MotionEvent ev) {\r
+        if (mFakeDragging) {\r
+            // A fake drag is in progress already, ignore this real one\r
+            // but still eat the touch events.\r
+            // (It is likely that the user is multi-touching the screen.)\r
+            return true;\r
+        }\r
+\r
+        if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {\r
+            // Don't handle edge touches immediately -- they may actually belong to one of our\r
+            // descendants.\r
+            return false;\r
+        }\r
+\r
+        if (mAdapter == null || mAdapter.getCount() == 0) {\r
+            // Nothing to present or scroll; nothing to touch.\r
+            return false;\r
+        }\r
+\r
+        if (mVelocityTracker == null) {\r
+            mVelocityTracker = VelocityTracker.obtain();\r
+        }\r
+        mVelocityTracker.addMovement(ev);\r
+\r
+        final int action = ev.getAction();\r
+        boolean needsInvalidate = false;\r
+\r
+        if (isHorizontal()) {\r
+            switch (action & MotionEventCompat.ACTION_MASK) {\r
+                case MotionEvent.ACTION_DOWN: {\r
+                    mScroller.abortAnimation();\r
+                    mPopulatePending = false;\r
+                    populate();\r
+\r
+                    // Remember where the motion event started\r
+                    mLastMotionX = mInitialMotionX = ev.getX();\r
+                    mLastMotionY = mInitialMotionY = ev.getY();\r
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\r
+                    break;\r
+                }\r
+                case MotionEvent.ACTION_MOVE:\r
+                    if (!mIsBeingDragged) {\r
+                        final int pointerIndex\r
+                                = MotionEventCompat.findPointerIndex(ev,\r
+                                    mActivePointerId);\r
+                        if (pointerIndex == -1) {\r
+                            // A child has consumed some\r
+                            // touch events and put us into an inconsistent state.\r
+                            needsInvalidate = resetTouch();\r
+                            break;\r
+                        }\r
+                        final float x = MotionEventCompat.getX(ev, pointerIndex);\r
+                        final float xDiff = Math.abs(x - mLastMotionX);\r
+                        final float y\r
+                                = MotionEventCompat.getY(ev,\r
+                                    pointerIndex);\r
+                        final float yDiff = Math.abs(y - mLastMotionY);\r
+                        if (xDiff > mTouchSlop && xDiff > yDiff) {\r
+                            if (DEBUG)\r
+                                Log.v(TAG, getContext().getString(R.string.debug_start_drag));\r
+                            mIsBeingDragged = true;\r
+                            requestParentDisallowInterceptTouchEvent(true);\r
+                            mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :\r
+                                    mInitialMotionX - mTouchSlop;\r
+                            mLastMotionY = y;\r
+                            setScrollState(SCROLL_STATE_DRAGGING);\r
+                            setScrollingCacheEnabled(true);\r
+\r
+                            // Disallow Parent Intercept, just in case\r
+                            ViewParent parent = getParent();\r
+                            if (parent != null) {\r
+                                parent.requestDisallowInterceptTouchEvent(true);\r
+                            }\r
+                        }\r
+                    }\r
+                    // Not else! Note that mIsBeingDragged can be set above.\r
+                    if (mIsBeingDragged) {\r
+                        // Scroll to follow the motion event\r
+                        final int activePointerIndex = MotionEventCompat.findPointerIndex(\r
+                                ev, mActivePointerId);\r
+                        final float x = MotionEventCompat.getX(ev, activePointerIndex);\r
+                        needsInvalidate |= performDrag(x, 0);\r
+                    }\r
+                    break;\r
+                case MotionEvent.ACTION_UP:\r
+                    if (mIsBeingDragged) {\r
+                        final VelocityTracker velocityTracker = mVelocityTracker;\r
+                        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\r
+                        int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(\r
+                                velocityTracker, mActivePointerId);\r
+                        mPopulatePending = true;\r
+                        final int width = getClientWidth();\r
+                        final int scrollX = getScrollX();\r
+                        final ItemInfo ii = infoForCurrentScrollPosition();\r
+                        final float marginOffset = (float) mPageMargin / width;\r
+                        final int currentPage = ii.position;\r
+                        final float pageOffset = (((float) scrollX / width) - ii.offset)\r
+                                / (ii.widthFactor + marginOffset);\r
+                        final int activePointerIndex =\r
+                                MotionEventCompat.findPointerIndex(ev, mActivePointerId);\r
+                        final float x = MotionEventCompat.getX(ev, activePointerIndex);\r
+                        final int totalDelta = (int) (x - mInitialMotionX);\r
+                        int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,\r
+                                totalDelta, 0);\r
+                        setCurrentItemInternal(nextPage, true, true, initialVelocity);\r
+\r
+                        needsInvalidate = resetTouch();\r
+                    }\r
+                    break;\r
+                case MotionEvent.ACTION_CANCEL:\r
+                    if (mIsBeingDragged) {\r
+                        scrollToItem(mCurItem, true, 0, false);\r
+                        needsInvalidate = resetTouch();\r
+                    }\r
+                    break;\r
+                case MotionEventCompat.ACTION_POINTER_DOWN: {\r
+                    final int index = MotionEventCompat.getActionIndex(ev);\r
+                    final float x = MotionEventCompat.getX(ev, index);\r
+                    mLastMotionX = x;\r
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, index);\r
+                    break;\r
+                }\r
+                case MotionEventCompat.ACTION_POINTER_UP:\r
+                    onSecondaryPointerUp(ev);\r
+                    mLastMotionX = MotionEventCompat.getX(ev,\r
+                            MotionEventCompat.findPointerIndex(ev, mActivePointerId));\r
+                    break;\r
+            }\r
+        } else {\r
+            switch (action & MotionEventCompat.ACTION_MASK) {\r
+                case MotionEvent.ACTION_DOWN: {\r
+                    mScroller.abortAnimation();\r
+                    mPopulatePending = false;\r
+                    populate();\r
+\r
+                    // Remember where the motion event started\r
+                    mLastMotionX = mInitialMotionX = ev.getX();\r
+                    mLastMotionY = mInitialMotionY = ev.getY();\r
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\r
+                    break;\r
+                }\r
+                case MotionEvent.ACTION_MOVE:\r
+                    if (!mIsBeingDragged) {\r
+                        final int pointerIndex =\r
+                                MotionEventCompat.findPointerIndex(ev, mActivePointerId);\r
+                        final float y = MotionEventCompat.getY(ev, pointerIndex);\r
+                        final float yDiff\r
+                                = Math.abs(y - mLastMotionY);\r
+                        final float x\r
+                                = MotionEventCompat.getX(ev, pointerIndex);\r
+                        final float xDiff = Math.abs(x - mLastMotionX);\r
+\r
+                        if (yDiff > mTouchSlop && yDiff > xDiff) {\r
+                            if (DEBUG)\r
+                                Log.v(TAG, getContext().getString(R.string.debug_start_drag));\r
+                            mIsBeingDragged = true;\r
+                            requestParentDisallowInterceptTouchEvent(true);\r
+                            mLastMotionY = y - mInitialMotionY > 0 ? mInitialMotionY + mTouchSlop :\r
+                                    mInitialMotionY - mTouchSlop;\r
+                            mLastMotionX = x;\r
+                            setScrollState(SCROLL_STATE_DRAGGING);\r
+                            setScrollingCacheEnabled(true);\r
+\r
+                            // Disallow Parent Intercept, just in case\r
+                            ViewParent parent = getParent();\r
+                            if (parent != null) {\r
+                                parent.requestDisallowInterceptTouchEvent(true);\r
+                            }\r
+                        }\r
+                    }\r
+                    // Not else! Note that mIsBeingDragged can be set above.\r
+                    if (mIsBeingDragged) {\r
+                        // Scroll to follow the motion event\r
+                        final int activePointerIndex = MotionEventCompat.findPointerIndex(\r
+                                ev, mActivePointerId);\r
+                        final float y = MotionEventCompat.getY(ev, activePointerIndex);\r
+                        needsInvalidate |= performDrag(0, y);\r
+                    }\r
+                    break;\r
+                case MotionEvent.ACTION_UP:\r
+                    if (mIsBeingDragged) {\r
+                        final VelocityTracker velocityTracker = mVelocityTracker;\r
+                        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\r
+                        int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(\r
+                                velocityTracker, mActivePointerId);\r
+                        mPopulatePending = true;\r
+                        final int height = getClientHeight();\r
+                        final int scrollY = getScrollY();\r
+                        final ItemInfo ii = infoForCurrentScrollPosition();\r
+                        final int currentPage = ii.position;\r
+                        final float pageOffset =\r
+                                (((float) scrollY / height) - ii.offset) / ii.heightFactor;\r
+                        final int activePointerIndex =\r
+                                MotionEventCompat.findPointerIndex(ev, mActivePointerId);\r
+                        final float y = MotionEventCompat.getY(ev, activePointerIndex);\r
+                        final int totalDelta = (int) (y - mInitialMotionY);\r
+                        int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,\r
+                                0, totalDelta);\r
+                        setCurrentItemInternal(nextPage, true, true, initialVelocity);\r
+\r
+                        mActivePointerId = INVALID_POINTER;\r
+                        endDrag();\r
+                        needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease();\r
+                    }\r
+                    break;\r
+                case MotionEvent.ACTION_CANCEL:\r
+                    if (mIsBeingDragged) {\r
+                        scrollToItem(mCurItem, true, 0, false);\r
+                        mActivePointerId = INVALID_POINTER;\r
+                        endDrag();\r
+                        needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease();\r
+                    }\r
+                    break;\r
+                case MotionEventCompat.ACTION_POINTER_DOWN: {\r
+                    final int index = MotionEventCompat.getActionIndex(ev);\r
+                    final float y = MotionEventCompat.getY(ev, index);\r
+                    mLastMotionY = y;\r
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, index);\r
+                    break;\r
+                }\r
+                case MotionEventCompat.ACTION_POINTER_UP:\r
+                    onSecondaryPointerUp(ev);\r
+                    mLastMotionY = MotionEventCompat.getY(ev,\r
+                            MotionEventCompat.findPointerIndex(ev, mActivePointerId));\r
+                    break;\r
+            }\r
+        }\r
+        if (needsInvalidate) {\r
+            ViewCompat.postInvalidateOnAnimation(this);\r
+        }\r
+        return true;\r
+    }\r
+\r
+    private boolean resetTouch() {\r
+        boolean needsInvalidate;\r
+        mActivePointerId = INVALID_POINTER;\r
+        endDrag();\r
+        needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();\r
+        return needsInvalidate;\r
+    }\r
+\r
+    private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {\r
+        final ViewParent parent = getParent();\r
+        if (parent != null) {\r
+            parent.requestDisallowInterceptTouchEvent(disallowIntercept);\r
+        }\r
+    }\r
+\r
+    private boolean performDrag(float x, float y) {\r
+        boolean needsInvalidate = false;\r
+        if (isHorizontal()) {\r
+            final float deltaX = mLastMotionX - x;\r
+            mLastMotionX = x;\r
+\r
+            float oldScrollX = getScrollX();\r
+            float scrollX = oldScrollX + deltaX;\r
+            final int width = getClientWidth();\r
+\r
+            float leftBound = width * mFirstOffset;\r
+            float rightBound = width * mLastOffset;\r
+            boolean leftAbsolute = true;\r
+            boolean rightAbsolute = true;\r
+\r
+            final ItemInfo firstItem = mItems.get(0);\r
+            final ItemInfo lastItem = mItems.get(mItems.size() - 1);\r
+            if (firstItem.position != 0) {\r
+                leftAbsolute = false;\r
+                leftBound = firstItem.offset * width;\r
+            }\r
+            if (lastItem.position != mAdapter.getCount() - 1) {\r
+                rightAbsolute = false;\r
+                rightBound = lastItem.offset * width;\r
+            }\r
+\r
+            if (scrollX < leftBound) {\r
+                if (leftAbsolute) {\r
+                    float over = leftBound - scrollX;\r
+                    needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);\r
+                }\r
+                scrollX = leftBound;\r
+            } else if (scrollX > rightBound) {\r
+                if (rightAbsolute) {\r
+                    float over = scrollX - rightBound;\r
+                    needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);\r
+                }\r
+                scrollX = rightBound;\r
+            }\r
+            // Don't lose the rounded component\r
+            mLastMotionX += scrollX - (int) scrollX;\r
+            scrollTo((int) scrollX, getScrollY());\r
+            pageScrolled((int) scrollX, 0);\r
+        } else {\r
+\r
+            final float deltaY = mLastMotionY - y;\r
+            mLastMotionY = y;\r
+\r
+            float oldScrollY = getScrollY();\r
+            float scrollY = oldScrollY + deltaY;\r
+            final int height = getClientHeight();\r
+\r
+            float topBound = height * mFirstOffset;\r
+            float bottomBound = height * mLastOffset;\r
+            boolean topAbsolute = true;\r
+            boolean bottomAbsolute = true;\r
+\r
+            final ItemInfo firstItem = mItems.get(0);\r
+            final ItemInfo lastItem = mItems.get(mItems.size() - 1);\r
+            if (firstItem.position != 0) {\r
+                topAbsolute = false;\r
+                topBound = firstItem.offset * height;\r
+            }\r
+            if (lastItem.position != mAdapter.getCount() - 1) {\r
+                bottomAbsolute = false;\r
+                bottomBound = lastItem.offset * height;\r
+            }\r
+\r
+            if (scrollY < topBound) {\r
+                if (topAbsolute) {\r
+                    float over = topBound - scrollY;\r
+                    needsInvalidate = mTopEdge.onPull(Math.abs(over) / height);\r
+                }\r
+                scrollY = topBound;\r
+            } else if (scrollY > bottomBound) {\r
+                if (bottomAbsolute) {\r
+                    float over = scrollY - bottomBound;\r
+                    needsInvalidate = mBottomEdge.onPull(Math.abs(over) / height);\r
+                }\r
+                scrollY = bottomBound;\r
+            }\r
+            // Don't lose the rounded component\r
+            mLastMotionX += scrollY - (int) scrollY;\r
+            scrollTo(getScrollX(), (int) scrollY);\r
+            pageScrolled(0, (int) scrollY);\r
+        }\r
+\r
+        return needsInvalidate;\r
+    }\r
+\r
+    /**\r
+     * @return Info about the page at the current scroll position.\r
+     * This can be synthetic for a missing middle page; the 'object' field can be null.\r
+     */\r
+    private ItemInfo infoForCurrentScrollPosition() {\r
+        ItemInfo lastItem = null;\r
+        if (isHorizontal()) {\r
+            final int width = getClientWidth();\r
+            final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;\r
+            final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;\r
+            int lastPos = -1;\r
+            float lastOffset = 0.f;\r
+            float lastWidth = 0.f;\r
+            boolean first = true;\r
+\r
+            for (int i = 0; i < mItems.size(); i++) {\r
+                ItemInfo ii = mItems.get(i);\r
+                float offset;\r
+                if (!first && ii.position != lastPos + 1) {\r
+                    // Create a synthetic item for a missing page.\r
+                    ii = mTempItem;\r
+                    ii.offset = lastOffset + lastWidth + marginOffset;\r
+                    ii.position = lastPos + 1;\r
+                    ii.widthFactor = mAdapter.getPageWidth(ii.position);\r
+                    i--;\r
+                }\r
+                offset = ii.offset;\r
+\r
+                final float leftBound = offset;\r
+                final float rightBound = offset + ii.widthFactor + marginOffset;\r
+                if (first || scrollOffset >= leftBound) {\r
+                    if (scrollOffset < rightBound || i == mItems.size() - 1) {\r
+                        return ii;\r
+                    }\r
+                } else {\r
+                    return lastItem;\r
+                }\r
+                first = false;\r
+                lastPos = ii.position;\r
+                lastOffset = offset;\r
+                lastWidth = ii.widthFactor;\r
+                lastItem = ii;\r
+            }\r
+        } else {\r
+            final int height = getClientHeight();\r
+            final float scrollOffset = height > 0 ? (float) getScrollY() / height : 0;\r
+            final float marginOffset = height > 0 ? (float) mPageMargin / height : 0;\r
+            int lastPos = -1;\r
+            float lastOffset = 0.f;\r
+            float lastHeight = 0.f;\r
+            boolean first = true;\r
+\r
+            for (int i = 0; i < mItems.size(); i++) {\r
+                ItemInfo ii = mItems.get(i);\r
+                float offset;\r
+                if (!first && ii.position != lastPos + 1) {\r
+                    // Create a synthetic item for a missing page.\r
+                    ii = mTempItem;\r
+                    ii.offset = lastOffset + lastHeight + marginOffset;\r
+                    ii.position = lastPos + 1;\r
+                    ii.heightFactor = mAdapter.getPageWidth(ii.position);\r
+                    i--;\r
+                }\r
+                offset = ii.offset;\r
+\r
+                final float topBound = offset;\r
+                final float bottomBound = offset + ii.heightFactor + marginOffset;\r
+                if (first || scrollOffset >= topBound) {\r
+                    if (scrollOffset < bottomBound || i == mItems.size() - 1) {\r
+                        return ii;\r
+                    }\r
+                } else {\r
+                    return lastItem;\r
+                }\r
+                first = false;\r
+                lastPos = ii.position;\r
+                lastOffset = offset;\r
+                lastHeight = ii.heightFactor;\r
+                lastItem = ii;\r
+            }\r
+        }\r
+\r
+        return lastItem;\r
+    }\r
+\r
+    private int determineTargetPage(int currentPage, float pageOffset,\r
+                                        int velocity, int deltaX, int deltaY) {\r
+        int targetPage;\r
+        if (isHorizontal()) {\r
+            if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {\r
+                targetPage = velocity > 0 ? currentPage : currentPage + 1;\r
+            } else {\r
+                final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;\r
+                targetPage = (int) (currentPage + pageOffset + truncator);\r
+            }\r
+        } else {\r
+            if (Math.abs(deltaY) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {\r
+                targetPage = velocity > 0 ? currentPage : currentPage + 1;\r
+            } else {\r
+                final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;\r
+                targetPage = (int) (currentPage + pageOffset + truncator);\r
+            }\r
+        }\r
+\r
+        if (mItems.size() > 0) {\r
+            final ItemInfo firstItem = mItems.get(0);\r
+            final ItemInfo lastItem = mItems.get(mItems.size() - 1);\r
+\r
+            // Only let the user target pages we have items for\r
+            targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));\r
+        }\r
+\r
+        return targetPage;\r
+    }\r
+\r
+    @Override\r
+    public void draw(Canvas canvas) {\r
+        super.draw(canvas);\r
+        boolean needsInvalidate = false;\r
+\r
+        final int overScrollMode = ViewCompat.getOverScrollMode(this);\r
+        if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||\r
+                (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&\r
+                        mAdapter != null && mAdapter.getCount() > 1)) {\r
+            if (isHorizontal()) {\r
+                if (!mLeftEdge.isFinished()) {\r
+                    final int restoreCount = canvas.save();\r
+                    final int height = getHeight() - getPaddingTop() - getPaddingBottom();\r
+                    final int width = getWidth();\r
+\r
+                    canvas.rotate(270);\r
+                    canvas.translate(-height + getPaddingTop(), mFirstOffset * width);\r
+                    mLeftEdge.setSize(height, width);\r
+                    needsInvalidate |= mLeftEdge.draw(canvas);\r
+                    canvas.restoreToCount(restoreCount);\r
+                }\r
+                if (!mRightEdge.isFinished()) {\r
+                    final int restoreCount = canvas.save();\r
+                    final int width = getWidth();\r
+                    final int height = getHeight() - getPaddingTop() - getPaddingBottom();\r
+\r
+                    canvas.rotate(90);\r
+                    canvas.translate(-getPaddingTop(), -(mLastOffset + 1) * width);\r
+                    mRightEdge.setSize(height, width);\r
+                    needsInvalidate |= mRightEdge.draw(canvas);\r
+                    canvas.restoreToCount(restoreCount);\r
+                } else {\r
+                    mLeftEdge.finish();\r
+                    mRightEdge.finish();\r
+                }\r
+            } else {\r
+                if (!mTopEdge.isFinished()) {\r
+                    final int restoreCount = canvas.save();\r
+                    final int height = getHeight();\r
+                    final int width = getWidth() - getPaddingLeft() - getPaddingRight();\r
+\r
+                    canvas.translate(getPaddingLeft(), mFirstOffset * height);\r
+                    mTopEdge.setSize(width, height);\r
+                    needsInvalidate |= mTopEdge.draw(canvas);\r
+                    canvas.restoreToCount(restoreCount);\r
+                }\r
+                if (!mBottomEdge.isFinished()) {\r
+                    final int restoreCount = canvas.save();\r
+                    final int height = getHeight();\r
+                    final int width = getWidth() - getPaddingLeft() - getPaddingRight();\r
+\r
+                    canvas.rotate(180);\r
+                    canvas.translate(-width - getPaddingLeft(), -(mLastOffset + 1) * height);\r
+                    mBottomEdge.setSize(width, height);\r
+                    needsInvalidate |= mBottomEdge.draw(canvas);\r
+                    canvas.restoreToCount(restoreCount);\r
+                } else {\r
+                    mTopEdge.finish();\r
+                    mBottomEdge.finish();\r
+                }\r
+            }\r
+        }\r
+\r
+\r
+        if (needsInvalidate) {\r
+            // Keep animating\r
+            ViewCompat.postInvalidateOnAnimation(this);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    protected void onDraw(Canvas canvas) {\r
+        super.onDraw(canvas);\r
+\r
+        // Draw the margin drawable between pages if needed.\r
+        if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {\r
+            if (isHorizontal()) {\r
+                final int scrollX = getScrollX();\r
+                final int width = getWidth();\r
+\r
+                final float marginOffset = (float) mPageMargin / width;\r
+                int itemIndex = 0;\r
+                ItemInfo ii = mItems.get(0);\r
+                float offset = ii.offset;\r
+                final int itemCount = mItems.size();\r
+                final int firstPos = ii.position;\r
+                final int lastPos = mItems.get(itemCount - 1).position;\r
+                for (int pos = firstPos; pos < lastPos; pos++) {\r
+                    while (pos > ii.position && itemIndex < itemCount) {\r
+                        ii = mItems.get(++itemIndex);\r
+                    }\r
+\r
+                    float drawAt;\r
+                    if (pos == ii.position) {\r
+                        drawAt = (ii.offset + ii.widthFactor) * width;\r
+                        offset = ii.offset + ii.widthFactor + marginOffset;\r
+                    } else {\r
+                        float widthFactor = mAdapter.getPageWidth(pos);\r
+                        drawAt = (offset + widthFactor) * width;\r
+                        offset += widthFactor + marginOffset;\r
+                    }\r
+\r
+                    if (drawAt + mPageMargin > scrollX) {\r
+                        mMarginDrawable.setBounds(Math.round(drawAt), mTopPageBounds,\r
+                                Math.round(drawAt + mPageMargin), mBottomPageBounds);\r
+                        mMarginDrawable.draw(canvas);\r
+                    }\r
+\r
+                    if (drawAt > scrollX + width) {\r
+                        break; // No more visible, no sense in continuing\r
+                    }\r
+                }\r
+            } else {\r
+                final int scrollY = getScrollY();\r
+                final int height = getHeight();\r
+\r
+                final float marginOffset = (float) mPageMargin / height;\r
+                int itemIndex = 0;\r
+                ItemInfo ii = mItems.get(0);\r
+                float offset = ii.offset;\r
+                final int itemCount = mItems.size();\r
+                final int firstPos = ii.position;\r
+                final int lastPos = mItems.get(itemCount - 1).position;\r
+                for (int pos = firstPos; pos < lastPos; pos++) {\r
+                    while (pos > ii.position && itemIndex < itemCount) {\r
+                        ii = mItems.get(++itemIndex);\r
+                    }\r
+\r
+                    float drawAt;\r
+                    if (pos == ii.position) {\r
+                        drawAt = (ii.offset + ii.heightFactor) * height;\r
+                        offset = ii.offset + ii.heightFactor + marginOffset;\r
+                    } else {\r
+                        float heightFactor = mAdapter.getPageWidth(pos);\r
+                        drawAt = (offset + heightFactor) * height;\r
+                        offset += heightFactor + marginOffset;\r
+                    }\r
+\r
+                    if (drawAt + mPageMargin > scrollY) {\r
+                        mMarginDrawable.setBounds(mLeftPageBounds, (int) drawAt,\r
+                                mRightPageBounds, (int) (drawAt + mPageMargin + 0.5f));\r
+                        mMarginDrawable.draw(canvas);\r
+                    }\r
+\r
+                    if (drawAt > scrollY + height) {\r
+                        break; // No more visible, no sense in continuing\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Start a fake drag of the pager.\r
+     * <p>\r
+     * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager\r
+     * with the touch scrolling of another view, while still letting the ViewPager\r
+     * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)\r
+     * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call\r
+     * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.\r
+     * <p>\r
+     * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag\r
+     * is already in progress, this method will return false.\r
+     *\r
+     * @return true if the fake drag began successfully, false if it could not be started.\r
+     * @see #fakeDragBy(float)\r
+     * @see #endFakeDrag()\r
+     */\r
+    public boolean beginFakeDrag() {\r
+        if (mIsBeingDragged) {\r
+            return false;\r
+        }\r
+        mFakeDragging = true;\r
+        setScrollState(SCROLL_STATE_DRAGGING);\r
+        if (isHorizontal()) {\r
+            mInitialMotionX = mLastMotionX = 0;\r
+        } else {\r
+            mInitialMotionY = mLastMotionY = 0;\r
+        }\r
+        if (mVelocityTracker == null) {\r
+            mVelocityTracker = VelocityTracker.obtain();\r
+        } else {\r
+            mVelocityTracker.clear();\r
+        }\r
+        final long time = SystemClock.uptimeMillis();\r
+        final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);\r
+        mVelocityTracker.addMovement(ev);\r
+        ev.recycle();\r
+        mFakeDragBeginTime = time;\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * End a fake drag of the pager.\r
+     *\r
+     * @see #beginFakeDrag()\r
+     * @see #endFakeDrag()\r
+     */\r
+    public void endFakeDrag() {\r
+        if (!mFakeDragging) {\r
+            throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");\r
+        }\r
+\r
+        if (mAdapter != null) {\r
+            if (isHorizontal()) {\r
+                final VelocityTracker velocityTracker = mVelocityTracker;\r
+                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\r
+                int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(\r
+                        velocityTracker, mActivePointerId);\r
+                mPopulatePending = true;\r
+                final int width = getClientWidth();\r
+                final int scrollX = getScrollX();\r
+                final ItemInfo ii = infoForCurrentScrollPosition();\r
+                final int currentPage = ii.position;\r
+                final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;\r
+                final int totalDelta = (int) (mLastMotionX - mInitialMotionX);\r
+                int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,\r
+                        totalDelta, 0);\r
+                setCurrentItemInternal(nextPage, true, true, initialVelocity);\r
+            } else {\r
+                final VelocityTracker velocityTracker = mVelocityTracker;\r
+                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\r
+                int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(\r
+                        velocityTracker, mActivePointerId);\r
+                mPopulatePending = true;\r
+                final int height = getClientHeight();\r
+                final int scrollY = getScrollY();\r
+                final ItemInfo ii = infoForCurrentScrollPosition();\r
+                final int currentPage = ii.position;\r
+                final float pageOffset = (((float) scrollY / height) - ii.offset) / ii.heightFactor;\r
+                final int totalDelta = (int) (mLastMotionY - mInitialMotionY);\r
+                int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,\r
+                        0, totalDelta);\r
+                setCurrentItemInternal(nextPage, true, true, initialVelocity);\r
+            }\r
+        }\r
+        endDrag();\r
+\r
+        mFakeDragging = false;\r
+    }\r
+\r
+    /**\r
+     * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.\r
+     *\r
+     * @param xOffset Offset in pixels to drag by.\r
+     * @see #beginFakeDrag()\r
+     * @see #endFakeDrag()\r
+     */\r
+    public void fakeDragBy(float xOffset, float yOffset) {\r
+        MotionEvent ev = null;\r
+        if (!mFakeDragging) {\r
+            throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");\r
+        }\r
+\r
+        if (mAdapter == null) {\r
+            return;\r
+        }\r
+\r
+        if (isHorizontal()) {\r
+            mLastMotionX += xOffset;\r
+\r
+            float oldScrollX = getScrollX();\r
+            float scrollX = oldScrollX - xOffset;\r
+            final int width = getClientWidth();\r
+\r
+            float leftBound = width * mFirstOffset;\r
+            float rightBound = width * mLastOffset;\r
+\r
+            final ItemInfo firstItem = mItems.get(0);\r
+            final ItemInfo lastItem = mItems.get(mItems.size() - 1);\r
+            if (firstItem.position != 0) {\r
+                leftBound = firstItem.offset * width;\r
+            }\r
+            if (lastItem.position != mAdapter.getCount() - 1) {\r
+                rightBound = lastItem.offset * width;\r
+            }\r
+\r
+            if (scrollX < leftBound) {\r
+                scrollX = leftBound;\r
+            } else if (scrollX > rightBound) {\r
+                scrollX = rightBound;\r
+            }\r
+            // Don't lose the rounded component\r
+            mLastMotionX += scrollX - (int) scrollX;\r
+            scrollTo((int) scrollX, getScrollY());\r
+            pageScrolled((int) scrollX, 0);\r
+\r
+            // Synthesize an event for the VelocityTracker.\r
+            final long time = SystemClock.uptimeMillis();\r
+            ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,\r
+                    mLastMotionX, 0, 0);\r
+        } else {\r
+            mLastMotionY += yOffset;\r
+\r
+            float oldScrollY = getScrollY();\r
+            float scrollY = oldScrollY - yOffset;\r
+            final int height = getClientHeight();\r
+\r
+            float topBound = height * mFirstOffset;\r
+            float bottomBound = height * mLastOffset;\r
+\r
+            final ItemInfo firstItem = mItems.get(0);\r
+            final ItemInfo lastItem = mItems.get(mItems.size() - 1);\r
+            if (firstItem.position != 0) {\r
+                topBound = firstItem.offset * height;\r
+            }\r
+            if (lastItem.position != mAdapter.getCount() - 1) {\r
+                bottomBound = lastItem.offset * height;\r
+            }\r
+\r
+            if (scrollY < topBound) {\r
+                scrollY = topBound;\r
+            } else if (scrollY > bottomBound) {\r
+                scrollY = bottomBound;\r
+            }\r
+            // Don't lose the rounded component\r
+            mLastMotionY += scrollY - (int) scrollY;\r
+            scrollTo(getScrollX(), (int) scrollY);\r
+            pageScrolled(0, (int) scrollY);\r
+\r
+            // Synthesize an event for the VelocityTracker.\r
+            final long time = SystemClock.uptimeMillis();\r
+            ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,\r
+                    0, mLastMotionY, 0);\r
+        }\r
+        mVelocityTracker.addMovement(ev);\r
+        ev.recycle();\r
+    }\r
+\r
+    /**\r
+     * Returns true if a fake drag is in progress.\r
+     *\r
+     * @return true if currently in a fake drag, false otherwise.\r
+     * @see #beginFakeDrag()\r
+     * @see #fakeDragBy(float)\r
+     * @see #endFakeDrag()\r
+     */\r
+    public boolean isFakeDragging() {\r
+        return mFakeDragging;\r
+    }\r
+\r
+    private void onSecondaryPointerUp(MotionEvent ev) {\r
+        final int pointerIndex = MotionEventCompat.getActionIndex(ev);\r
+        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);\r
+        if (pointerId == mActivePointerId) {\r
+            // This was our active pointer going up. Choose a new\r
+            // active pointer and adjust accordingly.\r
+            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\r
+            if (isHorizontal()) {\r
+                mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);\r
+            } else {\r
+                mLastMotionY = MotionEventCompat.getY(ev, newPointerIndex);\r
+            }\r
+            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);\r
+            if (mVelocityTracker != null) {\r
+                mVelocityTracker.clear();\r
+            }\r
+        }\r
+    }\r
+\r
+    private void endDrag() {\r
+        mIsBeingDragged = false;\r
+        mIsUnableToDrag = false;\r
+\r
+        if (mVelocityTracker != null) {\r
+            mVelocityTracker.recycle();\r
+            mVelocityTracker = null;\r
+        }\r
+    }\r
+\r
+    private void setScrollingCacheEnabled(boolean enabled) {\r
+        if (mScrollingCacheEnabled != enabled) {\r
+            mScrollingCacheEnabled = enabled;\r
+            if (USE_CACHE) {\r
+                final int size = getChildCount();\r
+                for (int i = 0; i < size; ++i) {\r
+                    final View child = getChildAt(i);\r
+                    if (child.getVisibility() != GONE) {\r
+                        child.setDrawingCacheEnabled(enabled);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    public boolean canScrollHorizontally(int direction) {\r
+        if (mAdapter == null) {\r
+            return false;\r
+        }\r
+\r
+        final int width = getClientWidth();\r
+        final int scrollX = getScrollX();\r
+        if (direction < 0) {\r
+            return (scrollX > (int) (width * mFirstOffset));\r
+        } else if (direction > 0) {\r
+            return (scrollX < (int) (width * mLastOffset));\r
+        } else {\r
+            return false;\r
+        }\r
+    }\r
+\r
+    public boolean internalCanScrollVertically(int direction) {\r
+        if (mAdapter == null) {\r
+            return false;\r
+        }\r
+\r
+        final int height = getClientHeight();\r
+        final int scrollY = getScrollY();\r
+        if (direction < 0) {\r
+            return (scrollY > (int) (height * mFirstOffset));\r
+        } else if (direction > 0) {\r
+            return (scrollY < (int) (height * mLastOffset));\r
+        } else {\r
+            return false;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Tests scrollability within child views of v given a delta of dx.\r
+     *\r
+     * @param v      View to test for horizontal scrollability\r
+     * @param checkV Whether the view v passed should itself be checked for scrollability (true),\r
+     *               or just its children (false).\r
+     * @param dx     Delta scrolled in pixels\r
+     * @param x      X coordinate of the active touch point\r
+     * @param y      Y coordinate of the active touch point\r
+     * @return true if child views of v can be scrolled by delta of dx.\r
+     */\r
+    protected boolean canScroll(View v, boolean checkV, int dx, int dy, int x, int y) {\r
+        if (v instanceof ViewGroup) {\r
+            if (isHorizontal()) {\r
+                final ViewGroup group = (ViewGroup) v;\r
+                final int scrollX = v.getScrollX();\r
+                final int scrollY = v.getScrollY();\r
+                final int count = group.getChildCount();\r
+                // Count backwards - let topmost views consume scroll distance first.\r
+                for (int i = count - 1; i >= 0; i--) {\r
+                    // TODO: Add versioned support here for transformed views.\r
+                    // This will not work for transformed views in Honeycomb+\r
+                    final View child = group.getChildAt(i);\r
+                    if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&\r
+                            y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&\r
+                            canScroll(child, true, dx, 0, x + scrollX - child.getLeft(),\r
+                                    y + scrollY - child.getTop())) {\r
+                        return true;\r
+                    }\r
+                }\r
+                return checkV && ViewCompat.canScrollHorizontally(v, -dx);\r
+            } else {\r
+                final ViewGroup group = (ViewGroup) v;\r
+                final int scrollX = v.getScrollX();\r
+                final int scrollY = v.getScrollY();\r
+                final int count = group.getChildCount();\r
+                // Count backwards - let topmost views consume scroll distance first.\r
+                for (int i = count - 1; i >= 0; i--) {\r
+                    // TODO: Add versioned support here for transformed views.\r
+                    // This will not work for transformed views in Honeycomb+\r
+                    final View child = group.getChildAt(i);\r
+                    if (y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&\r
+                            x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&\r
+                            canScroll(child, true, 0, dy, x + scrollX - child.getLeft(),\r
+                                    y + scrollY - child.getTop())) {\r
+                        return true;\r
+                    }\r
+                }\r
+\r
+                return checkV && ViewCompat.canScrollVertically(v, -dy);\r
+\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    public boolean dispatchKeyEvent(KeyEvent event) {\r
+        // Let the focused view and/or our descendants get the key first\r
+        return super.dispatchKeyEvent(event) || executeKeyEvent(event);\r
+    }\r
+\r
+    /**\r
+     * You can call this function yourself to have the scroll view perform\r
+     * scrolling from a key event, just as if the event had been dispatched to\r
+     * it by the view hierarchy.\r
+     *\r
+     * @param event The key event to execute.\r
+     * @return Return true if the event was handled, else false.\r
+     */\r
+    public boolean executeKeyEvent(KeyEvent event) {\r
+        boolean handled = false;\r
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {\r
+            switch (event.getKeyCode()) {\r
+                case KeyEvent.KEYCODE_DPAD_LEFT:\r
+                    handled = arrowScroll(FOCUS_LEFT);\r
+                    break;\r
+                case KeyEvent.KEYCODE_DPAD_RIGHT:\r
+                    handled = arrowScroll(FOCUS_RIGHT);\r
+                    break;\r
+                case KeyEvent.KEYCODE_TAB:\r
+                    if (Build.VERSION.SDK_INT >= 11) {\r
+                        // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD\r
+                        // before Android 3.0. Ignore the tab key on those devices.\r
+                        if (KeyEvent.metaStateHasNoModifiers(event.getMetaState())) {\r
+                            handled = arrowScroll(FOCUS_FORWARD);\r
+                        } else if (KeyEvent.metaStateHasNoModifiers(event.getMetaState())) {\r
+                            handled = arrowScroll(FOCUS_BACKWARD);\r
+                        }\r
+                    }\r
+                    break;\r
+            }\r
+        }\r
+        return handled;\r
+    }\r
+\r
+    public boolean arrowScroll(int direction) {\r
+        View currentFocused = findFocus();\r
+        if (currentFocused == this) {\r
+            currentFocused = null;\r
+        } else if (currentFocused != null) {\r
+            boolean isChild = false;\r
+            for (ViewParent parent\r
+                    = currentFocused.getParent();\r
+                        parent instanceof ViewGroup;\r
+                            parent = parent.getParent()) {\r
+                if (parent == this) {\r
+                    isChild = true;\r
+                    break;\r
+                }\r
+            }\r
+            if (!isChild) {\r
+                // This would cause the focus search down below to fail in fun ways.\r
+                final StringBuilder sb = new StringBuilder();\r
+                sb.append(currentFocused.getClass().getSimpleName());\r
+                for (ViewParent parent\r
+                        = currentFocused.getParent();\r
+                                parent instanceof ViewGroup;\r
+                                   parent = parent.getParent()) {\r
+                    sb.append(" => ").append(parent.getClass().getSimpleName());\r
+                }\r
+                Log.e(TAG, "arrowScroll tried to find focus based on non-child " +\r
+                        "current focused view " + sb.toString());\r
+                currentFocused = null;\r
+            }\r
+        }\r
+\r
+        boolean handled = false;\r
+\r
+        View nextFocused\r
+                    = FocusFinder.getInstance().findNextFocus(this, currentFocused,\r
+                        direction);\r
+        if (nextFocused != null && nextFocused != currentFocused) {\r
+            if (isHorizontal()) {\r
+                if (direction == View.FOCUS_LEFT) {\r
+                    // If there is nothing\r
+                    // to the left, or this is causing us to\r
+                    // jump to the right,\r
+                    // then what we really want to do is page left.\r
+                    final int nextLeft\r
+                            = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;\r
+                    final int currLeft\r
+                            = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;\r
+                    if (currentFocused != null && nextLeft >= currLeft) {\r
+                        handled = pageLeft();\r
+                    } else {\r
+                        handled = nextFocused.requestFocus();\r
+                    }\r
+                } else if (direction == View.FOCUS_RIGHT) {\r
+                    // If there is nothing to the right,\r
+                    // or this is causing us to\r
+                    // jump to the left,\r
+                    // then what we really\r
+                    // want to do is page right.\r
+                    final int nextLeft\r
+                            = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;\r
+                    final int currLeft\r
+                            = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;\r
+                    if (currentFocused != null && nextLeft <= currLeft) {\r
+                        handled = pageRight();\r
+                    } else {\r
+                        handled = nextFocused.requestFocus();\r
+                    }\r
+                } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {\r
+                    // Trying to move left and nothing there; try to page.\r
+                    handled = pageLeft();\r
+                } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {\r
+                    // Trying to move right and nothing there; try to page.\r
+                    handled = pageRight();\r
+                }\r
+            } else {\r
+                if (direction == View.FOCUS_UP) {\r
+                    // If there is nothing to the left,\r
+                    // or this is causing us to\r
+                    // jump to the right,\r
+                    // then what we really want to do is page left.\r
+                    final int nextTop\r
+                            = getChildRectInPagerCoordinates(mTempRect, nextFocused).top;\r
+                    final int currTop\r
+                            = getChildRectInPagerCoordinates(mTempRect, currentFocused).top;\r
+                    if (currentFocused != null && nextTop >= currTop) {\r
+                        handled = pageUp();\r
+                    } else {\r
+                        handled = nextFocused.requestFocus();\r
+                    }\r
+                } else if (direction == View.FOCUS_DOWN) {\r
+                    final int nextDown =\r
+                            getChildRectInPagerCoordinates(mTempRect, nextFocused).bottom;\r
+                    final int currDown =\r
+                            getChildRectInPagerCoordinates(mTempRect, currentFocused).bottom;\r
+                    if (currentFocused != null && nextDown <= currDown) {\r
+                        handled = pageDown();\r
+                    } else {\r
+                        handled = nextFocused.requestFocus();\r
+                    }\r
+                } else if (direction == FOCUS_UP || direction == FOCUS_BACKWARD) {\r
+                    // Trying to move left and nothing there; try to page.\r
+                    handled = pageUp();\r
+                } else if (direction == FOCUS_DOWN || direction == FOCUS_FORWARD) {\r
+                    // Trying to move right and nothing there; try to page.\r
+                    handled = pageDown();\r
+\r
+                }\r
+            }\r
+            if (handled) {\r
+                playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));\r
+            }\r
+            return handled;\r
+        }\r
+        return handled;\r
+    }\r
+\r
+\r
+    private Rect getChildRectInPagerCoordinates(Rect outRect, View child) {\r
+        if (outRect == null) {\r
+            outRect = new Rect();\r
+        }\r
+        if (child == null) {\r
+            outRect.set(0, 0, 0, 0);\r
+            return outRect;\r
+        }\r
+        outRect.left = child.getLeft();\r
+        outRect.right = child.getRight();\r
+        outRect.top = child.getTop();\r
+        outRect.bottom = child.getBottom();\r
+\r
+        ViewParent parent = child.getParent();\r
+        while (parent instanceof ViewGroup && parent != this) {\r
+            final ViewGroup group = (ViewGroup) parent;\r
+            outRect.left += group.getLeft();\r
+            outRect.right += group.getRight();\r
+            outRect.top += group.getTop();\r
+            outRect.bottom += group.getBottom();\r
+\r
+            parent = group.getParent();\r
+        }\r
+        return outRect;\r
+    }\r
+\r
+    boolean pageLeft() {\r
+        if (mCurItem > 0) {\r
+            setCurrentItem(mCurItem - 1, true);\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    boolean pageRight() {\r
+        if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {\r
+            setCurrentItem(mCurItem + 1, true);\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    boolean pageUp() {\r
+        if (mCurItem > 0) {\r
+            setCurrentItem(mCurItem - 1, true);\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    boolean pageDown() {\r
+        if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {\r
+            setCurrentItem(mCurItem + 1, true);\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * We only want the current page that is being shown to be focusable.\r
+     */\r
+    @Override\r
+    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {\r
+        final int focusableCount = views.size();\r
+\r
+        final int descendantFocusability = getDescendantFocusability();\r
+\r
+        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {\r
+            for (int i = 0; i < getChildCount(); i++) {\r
+                final View child = getChildAt(i);\r
+                if (child.getVisibility() == VISIBLE) {\r
+                    ItemInfo ii = infoForChild(child);\r
+                    if (ii != null && ii.position == mCurItem) {\r
+                        child.addFocusables(views, direction, focusableMode);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        // we add ourselves (if focusable) in all cases except for when we are\r
+        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is\r
+        // to avoid the focus search finding layouts when a more precise search\r
+        // among the focusable children would be more interesting.\r
+        if (\r
+                descendantFocusability != FOCUS_AFTER_DESCENDANTS ||\r
+                        // No focusable descendants\r
+                        (focusableCount == views.size())) {\r
+            // Note that we can't call the superclass here, because it will\r
+            // add all views in.  So we need to do the same thing View does.\r
+            if (!isFocusable()) {\r
+                return;\r
+            }\r
+            if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&\r
+                    isInTouchMode() && !isFocusableInTouchMode()) {\r
+                return;\r
+            }\r
+            if (views != null) {\r
+                views.add(this);\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * We only want the current page that is being shown to be touchable.\r
+     */\r
+    @Override\r
+    public void addTouchables(ArrayList<View> views) {\r
+        // Note that we don't call super.addTouchables(), which means that\r
+        // we don't call View.addTouchables().  This is okay because a ViewPager\r
+        // is itself not touchable.\r
+        for (int i = 0; i < getChildCount(); i++) {\r
+            final View child = getChildAt(i);\r
+            if (child.getVisibility() == VISIBLE) {\r
+                ItemInfo ii = infoForChild(child);\r
+                if (ii != null && ii.position == mCurItem) {\r
+                    child.addTouchables(views);\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * We only want the current page that is being shown to be focusable.\r
+     */\r
+    @Override\r
+    protected boolean onRequestFocusInDescendants(int direction,\r
+                                                  Rect previouslyFocusedRect) {\r
+        int index;\r
+        int increment;\r
+        int end;\r
+        int count = getChildCount();\r
+        if ((direction & FOCUS_FORWARD) != 0) {\r
+            index = 0;\r
+            increment = 1;\r
+            end = count;\r
+        } else {\r
+            index = count - 1;\r
+            increment = -1;\r
+            end = -1;\r
+        }\r
+        for (int i = index; i != end; i += increment) {\r
+            View child = getChildAt(i);\r
+            if (child.getVisibility() == VISIBLE) {\r
+                ItemInfo ii = infoForChild(child);\r
+                if (ii != null && ii.position ==\r
+                            mCurItem && child.requestFocus(direction, previouslyFocusedRect)) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {\r
+        // Dispatch scroll events from this ViewPager.\r
+        if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {\r
+            return super.dispatchPopulateAccessibilityEvent(event);\r
+        }\r
+\r
+        // Dispatch all other accessibility events from the current page.\r
+        final int childCount = getChildCount();\r
+        for (int i = 0; i < childCount; i++) {\r
+            final View child = getChildAt(i);\r
+            if (child.getVisibility() == VISIBLE) {\r
+                final ItemInfo ii = infoForChild(child);\r
+                if (ii != null && ii.position == mCurItem &&\r
+                        child.dispatchPopulateAccessibilityEvent(event)) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {\r
+        return new LayoutParams();\r
+    }\r
+\r
+    @Override\r
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {\r
+        return generateDefaultLayoutParams();\r
+    }\r
+\r
+    @Override\r
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {\r
+        return p instanceof LayoutParams && super.checkLayoutParams(p);\r
+    }\r
+\r
+    @Override\r
+    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {\r
+        return new LayoutParams(getContext(), attrs);\r
+    }\r
+\r
+    class MyAccessibilityDelegate extends AccessibilityDelegateCompat {\r
+\r
+        @Override\r
+        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {\r
+            super.onInitializeAccessibilityEvent(host, event);\r
+            event.setClassName(DirectionalViewpager.class.getName());\r
+            AccessibilityRecordCompat recordCompat = null;\r
+            if (isHorizontal()) {\r
+                recordCompat =\r
+                        AccessibilityEventCompat.asRecord(event);\r
+            } else {\r
+                recordCompat = AccessibilityRecordCompat.obtain();\r
+            }\r
+            recordCompat.setScrollable(canScroll());\r
+            if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED\r
+                    && mAdapter != null) {\r
+                recordCompat.setItemCount(mAdapter.getCount());\r
+                recordCompat.setFromIndex(mCurItem);\r
+                recordCompat.setToIndex(mCurItem);\r
+            }\r
+        }\r
+\r
+        @Override\r
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {\r
+            super.onInitializeAccessibilityNodeInfo(host, info);\r
+            info.setClassName(DirectionalViewpager.class.getName());\r
+            info.setScrollable(canScroll());\r
+            if (isHorizontal()) {\r
+                if (canScrollHorizontally(1)) {\r
+                    info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);\r
+                }\r
+                if (canScrollHorizontally(-1)) {\r
+                    info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);\r
+                }\r
+            } else {\r
+                if (internalCanScrollVertically(1)) {\r
+                    info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);\r
+                }\r
+                if (internalCanScrollVertically(-1)) {\r
+                    info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);\r
+                }\r
+            }\r
+        }\r
+\r
+        @Override\r
+        public boolean performAccessibilityAction(View host, int action, Bundle args) {\r
+            if (super.performAccessibilityAction(host, action, args)) {\r
+                return true;\r
+            }\r
+\r
+            if (isHorizontal()) {\r
+                switch (action) {\r
+                    case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {\r
+                        if (canScrollHorizontally(1)) {\r
+                            setCurrentItem(mCurItem + 1);\r
+                            return true;\r
+                        }\r
+                    }\r
+                    return false;\r
+                    case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {\r
+                        if (canScrollHorizontally(-1)) {\r
+                            setCurrentItem(mCurItem - 1);\r
+                            return true;\r
+                        }\r
+                    }\r
+                    return false;\r
+                }\r
+            } else {\r
+                switch (action) {\r
+                    case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {\r
+                        if (internalCanScrollVertically(1)) {\r
+                            setCurrentItem(mCurItem + 1);\r
+                            return true;\r
+                        }\r
+                    }\r
+                    return false;\r
+                    case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {\r
+                        if (internalCanScrollVertically(-1)) {\r
+                            setCurrentItem(mCurItem - 1);\r
+                            return true;\r
+                        }\r
+                    }\r
+                    return false;\r
+                }\r
+            }\r
+            return false;\r
+        }\r
+\r
+        private boolean canScroll() {\r
+            return (mAdapter != null) && (mAdapter.getCount() > 1);\r
+        }\r
+    }\r
+\r
+    private class PagerObserver extends DataSetObserver {\r
+        @Override\r
+        public void onChanged() {\r
+            dataSetChanged();\r
+        }\r
+\r
+        @Override\r
+        public void onInvalidated() {\r
+            dataSetChanged();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Layout parameters that should be supplied for views added to a\r
+     * ViewPager.\r
+     */\r
+    public static class LayoutParams extends ViewGroup.LayoutParams {\r
+        /**\r
+         * true if this view is a decoration on the pager itself and not\r
+         * a view supplied by the adapter.\r
+         */\r
+        public boolean isDecor;\r
+\r
+        /**\r
+         * Gravity setting for use on decor views only:\r
+         * Where to position the view page within the overall ViewPager\r
+         * container; constants are defined in {@link android.view.Gravity}.\r
+         */\r
+        public int gravity;\r
+\r
+        /**\r
+         * Width as a 0-1 multiplier of the measured pager width\r
+         */\r
+        float widthFactor = 0.f;\r
+\r
+        float heightFactor = 0.f;\r
+\r
+        /**\r
+         * true if this view was added during layout and needs to be measured\r
+         * before being positioned.\r
+         */\r
+        boolean needsMeasure;\r
+\r
+        /**\r
+         * Adapter position this view is for if !isDecor\r
+         */\r
+        int position;\r
+\r
+        /**\r
+         * Current child index within the ViewPager that this view occupies\r
+         */\r
+        int childIndex;\r
+\r
+        public LayoutParams() {\r
+            super(FILL_PARENT, FILL_PARENT);\r
+        }\r
+\r
+        public LayoutParams(Context context, AttributeSet attrs) {\r
+            super(context, attrs);\r
+\r
+            final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);\r
+            gravity = a.getInteger(0, Gravity.TOP);\r
+            a.recycle();\r
+        }\r
+    }\r
+\r
+    static class ViewPositionComparator implements Comparator<View> {\r
+        @Override\r
+        public int compare(View lhs, View rhs) {\r
+            final LayoutParams llp = (LayoutParams) lhs.getLayoutParams();\r
+            final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams();\r
+            if (llp.isDecor != rlp.isDecor) {\r
+                return llp.isDecor ? 1 : -1;\r
+            }\r
+            return llp.position - rlp.position;\r
+        }\r
+    }\r
+\r
+    public void setDirection(Direction direction) {\r
+        mDirection = direction.name();\r
+        initViewPager();\r
+    }\r
+\r
+    private String logDestroyItem(int pos, View object) {\r
+        return "populate() - destroyItem() with pos: " + pos + " view: " + object;\r
+    }\r
+}\r