PagedView.java revision 6f13342ffd3f968de9ff86b988621cc91d94adff
1321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/*
2321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Copyright (C) 2010 The Android Open Source Project
3321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
4321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * you may not use this file except in compliance with the License.
6321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * You may obtain a copy of the License at
7321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
8321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
10321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Unless required by applicable law or agreed to in writing, software
11321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * See the License for the specific language governing permissions and
14321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * limitations under the License.
15321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
16321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
17321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpackage com.android.launcher2;
18321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
190499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport java.util.ArrayList;
209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
21228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.Animator;
22228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorInflater;
23228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorListenerAdapter;
24228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.ObjectAnimator;
25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context;
269c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray;
27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas;
28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect;
29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel;
30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable;
31321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet;
325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode;
33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent;
34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker;
35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View;
36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration;
37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup;
38321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent;
39e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator;
405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable;
41321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller;
42321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
430499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R;
4480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
45321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/**
46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a
470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages"
48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
49321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup {
50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final String TAG = "PagedView";
510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final int INVALID_PAGE = -1;
52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
5386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    // the min drag distance for a fling to register, to prevent random page shifts
549cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_FLING = 25;
559cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    // The min drag distance to trigger a page shift (regardless of velocity)
569cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_MOVE = 200;
57321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
58e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int PAGE_SNAP_ANIMATION_DURATION = 550;
590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final float NANOTIME_DIV = 1000000000.0f;
600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
61e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final float OVERSCROLL_DAMP_FACTOR = 0.08f;
62e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int MINIMUM_SNAP_VELOCITY = 2200;
63e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int MIN_FLING_VELOCITY = 250;
64b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen    private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
6568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // the velocity at which a fling gesture will cause us to snap to the next page
670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mSnapVelocity = 500;
680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mSmoothingTime;
700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mTouchX;
71321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFirstLayout = true;
730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mCurrentPage;
750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mNextPage = INVALID_PAGE;
76a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung    protected int mRestorePage = -1;
7768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected int mMaxScrollX;
780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected Scroller mScroller;
79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private VelocityTracker mVelocityTracker;
80321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
81321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mDownMotionX;
827426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected float mLastMotionX;
83c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung    protected float mLastMotionXRemainder;
847426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected float mLastMotionY;
85aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen    protected float mTotalMotionX;
86f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    private int mLastScreenCenter = -1;
87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_REST = 0;
890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_SCROLLING = 1;
900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_PREV_PAGE = 2;
910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
92e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen    protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
93321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mTouchState = TOUCH_STATE_REST;
95321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected OnLongClickListener mLongClickListener;
97321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
987426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected boolean mAllowLongPress = true;
99321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1007426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected int mTouchSlop;
101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mPagingTouchSlop;
102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mMaximumVelocity;
1039c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageSpacing;
1049c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingTop;
1059c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingBottom;
1069c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingLeft;
1079c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingRight;
108df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutWidthGap;
109df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutHeightGap;
1109c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mCellCountX;
1119c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mCellCountY;
1127da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung    protected boolean mCenterPagesVertically;
11368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected boolean mAllowOverScroll = true;
11468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected int mUnboundedScrollX;
115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1168c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // parameter that adjusts the layout to be optimized for pages with that scale factor
117d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    protected float mLayoutScale = 1.0f;
118d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
1195f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected static final int INVALID_POINTER = -1;
120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1215f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected int mActivePointerId = INVALID_POINTER;
122321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
12386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private PageSwitchListener mPageSwitchListener;
124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
12586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private ArrayList<Boolean> mDirtyPageContent;
12686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private boolean mDirtyPageAlpha;
127321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1285f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // choice modes
1295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_NONE = 0;
1305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_SINGLE = 1;
1315f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // Multiple selection mode is not supported by all Launcher actions atm
1325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_MULTIPLE = 2;
1339f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
134e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka    protected int mChoiceMode;
1355f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private ActionMode mActionMode;
1365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
1370499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung    // NOTE: This is a shared icon cache across all the PagedViews.  Currently it is only used in
1380499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung    // AllApps and Customize, and allows them to share holographic icons for the application view
1390499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung    // (which is in both).
1400499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung    protected static PagedViewIconCache mPageViewIconCache = new PagedViewIconCache();
141241c3b451d7841ba08247beea784953eca4e8582Winson Chung
1420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, syncPages and syncPageItems will be called to refresh pages
1430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mContentIsRefreshable = true;
1440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, modify alpha of neighboring pages as user scrolls left/right
1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFadeInAdjacentScreens = true;
1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
1490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // to switch to a new page
1500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mUsePagingTouchSlop = true;
1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, the subclass should directly update mScrollX itself in its computeScroll method
1530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // (SmoothPagedView does this)
1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mDeferScrollUpdate = false;
1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1561262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected boolean mIsPageMoving = false;
1571262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
15886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public interface PageSwitchListener {
15986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        void onPageSwitch(View newPage, int newPageIndex);
160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context) {
163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, null);
164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs) {
167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, attrs, 0);
168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs, int defStyle) {
171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super(context, attrs, defStyle);
1725f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        mChoiceMode = CHOICE_MODE_NONE;
173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1749c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
1759c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView, defStyle, 0);
1769c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0);
1779c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingTop = a.getDimensionPixelSize(
1789c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingTop, 10);
1799c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingBottom = a.getDimensionPixelSize(
1809c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingBottom, 10);
1819c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingLeft = a.getDimensionPixelSize(
1829c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingLeft, 10);
1839c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingRight = a.getDimensionPixelSize(
1849c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingRight, 10);
185df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutWidthGap = a.getDimensionPixelSize(
186df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung                R.styleable.PagedView_pageLayoutWidthGap, -1);
187df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutHeightGap = a.getDimensionPixelSize(
188df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung                R.styleable.PagedView_pageLayoutHeightGap, -1);
1899c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        a.recycle();
1909c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen
191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setHapticFeedbackEnabled(false);
1920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        init();
193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Initializes various states for this workspace.
197321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void init() {
19986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent = new ArrayList<Boolean>();
20086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent.ensureCapacity(32);
201e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        mScroller = new Scroller(getContext(), new ScrollInterpolator());
20286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = 0;
2037da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung        mCenterPagesVertically = true;
204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mTouchSlop = configuration.getScaledTouchSlop();
207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
21186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
21286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mPageSwitchListener = pageSwitchListener;
21386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
21486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
21986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Returns the index of the currently displayed page.
220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
22186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * @return The index of the currently displayed page.
222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
22386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getCurrentPage() {
22486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        return mCurrentPage;
225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
22786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getPageCount() {
228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildCount();
229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
23186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    View getPageAt(int index) {
232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildAt(index);
233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    int getScrollWidth() {
236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getWidth();
237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
239be9093426aec3a6d412811a12d2400e645613242Adam Cohen    public int getTouchState() {
240be9093426aec3a6d412811a12d2400e645613242Adam Cohen        return mTouchState;
241be9093426aec3a6d412811a12d2400e645613242Adam Cohen    }
242be9093426aec3a6d412811a12d2400e645613242Adam Cohen
243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
244bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * Updates the scroll of the current page immediately to its final scroll position.  We use this
245bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
246bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * the previous tab page.
247bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     */
248bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    protected void updateCurrentPageScroll() {
249bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
250bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        scrollTo(newX, 0);
251bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        mScroller.setFinalX(newX);
252bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    }
253bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung
254bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    /**
25586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Sets the current page.
256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
25786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    void setCurrentPage(int currentPage) {
25872e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        if (!mScroller.isFinished()) {
25972e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            mScroller.abortAnimation();
26072e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
261d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
262d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // the default
263d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        if (getChildCount() == 0) {
26472e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            return;
26572e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
26786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
268bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        updateCurrentPageScroll();
26986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        notifyPageSwitchListener();
270a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung        invalidate();
271321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void notifyPageSwitchListener() {
27486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
27586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
279ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka    protected void pageBeginMoving() {
2801262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        mIsPageMoving = true;
2811262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        onPageBeginMoving();
2821262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
2831262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
284ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka    protected void pageEndMoving() {
2851262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        onPageEndMoving();
2861262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        mIsPageMoving = false;
2871262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
2881262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
2890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2901262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageBeginMoving() {
2910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2941262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageEndMoving() {
2950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
29886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Registers the specified listener on each page contained in this workspace.
299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @param l The listener used to respond to long clicks.
301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void setOnLongClickListener(OnLongClickListener l) {
304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mLongClickListener = l;
30586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        final int count = getPageCount();
306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < count; i++) {
30786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(i).setOnLongClickListener(l);
308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
311321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
31268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    public void scrollBy(int x, int y) {
31368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        scrollTo(mUnboundedScrollX + x, mScrollY + y);
31468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    }
31568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
31668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    @Override
3170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void scrollTo(int x, int y) {
31868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        mUnboundedScrollX = x;
31968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
32068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        if (x < 0) {
32168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(0, y);
32268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            if (mAllowOverScroll) {
32368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                overScroll(x);
32468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            }
32568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else if (x > mMaxScrollX) {
32668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(mMaxScrollX, y);
32768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            if (mAllowOverScroll) {
32868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                overScroll(x - mMaxScrollX);
32968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            }
33068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else {
33168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(x, y);
33268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        }
33368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
3340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mTouchX = x;
3350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
3360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // we moved this functionality to a helper function so SmoothPagedView can reuse it
3390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean computeScrollHelper() {
340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.computeScrollOffset()) {
3415f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
342321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
3430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            invalidate();
3440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
34586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        } else if (mNextPage != INVALID_PAGE) {
3465f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
34786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
34886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mNextPage = INVALID_PAGE;
3490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            notifyPageSwitchListener();
35073aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            // We don't want to trigger a page end moving unless the page has settled
35173aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            // and the user has stopped scrolling
35273aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            if (mTouchState == TOUCH_STATE_REST) {
35373aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen                pageEndMoving();
35473aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            }
3550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
3570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        return false;
3580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    @Override
3610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void computeScroll() {
3620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        computeScrollHelper();
363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
365321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
366321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
367321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
368321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
369321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (widthMode != MeasureSpec.EXACTLY) {
370321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
371321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
372321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
3736b879f0a5885274a85333531e091283405d490ccAdam Lesinski        /* Allow the height to be set as WRAP_CONTENT. This allows the particular case
3746b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * of the All apps view on XLarge displays to not take up more space then it needs. Width
3756b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
3766b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * each page to have the same width.
3776b879f0a5885274a85333531e091283405d490ccAdam Lesinski         */
378321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
3796b879f0a5885274a85333531e091283405d490ccAdam Lesinski        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
3806b879f0a5885274a85333531e091283405d490ccAdam Lesinski        int maxChildHeight = 0;
3816b879f0a5885274a85333531e091283405d490ccAdam Lesinski
3826b879f0a5885274a85333531e091283405d490ccAdam Lesinski        final int verticalPadding = mPaddingTop + mPaddingBottom;
383321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
384321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        // The children are given the same width and height as the workspace
3855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        // unless they were set to WRAP_CONTENT
386321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
3885f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            // disallowing padding in paged view (just pass 0)
3895f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final View child = getChildAt(i);
3905f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
3915f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3925f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childWidthMode;
3935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.width == LayoutParams.WRAP_CONTENT) {
3945f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.AT_MOST;
3955f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
3965f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.EXACTLY;
3975f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
3985f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3995f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childHeightMode;
4005f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.height == LayoutParams.WRAP_CONTENT) {
4015f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.AT_MOST;
4025f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
4035f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.EXACTLY;
4045f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
4055f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4065f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childWidthMeasureSpec =
4075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                MeasureSpec.makeMeasureSpec(widthSize, childWidthMode);
4085f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childHeightMeasureSpec =
4096b879f0a5885274a85333531e091283405d490ccAdam Lesinski                MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
4105f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4115f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4126b879f0a5885274a85333531e091283405d490ccAdam Lesinski            maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
4136b879f0a5885274a85333531e091283405d490ccAdam Lesinski        }
4146b879f0a5885274a85333531e091283405d490ccAdam Lesinski
4156b879f0a5885274a85333531e091283405d490ccAdam Lesinski        if (heightMode == MeasureSpec.AT_MOST) {
4166b879f0a5885274a85333531e091283405d490ccAdam Lesinski            heightSize = maxChildHeight + verticalPadding;
417321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
418faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        if (childCount > 0) {
419faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen            mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
420faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        } else {
421faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen            mMaxScrollX = 0;
422faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        }
423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
424321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setMeasuredDimension(widthSize, heightSize);
425cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    }
426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
4278c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) {
428af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
429af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        int delta = newX - mScrollX;
430af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka
4318c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        final int pageCount = getChildCount();
4328c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        for (int i = 0; i < pageCount; i++) {
4338c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            View page = (View) getChildAt(i);
4348c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            page.setX(page.getX() + delta);
435af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        }
436af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        setCurrentPage(newCurrentPage);
437af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka    }
438af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka
4398c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a
4408c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and
441d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    // tightens the layout accordingly
442d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    public void setLayoutScale(float childrenScale) {
443d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        mLayoutScale = childrenScale;
444d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
445d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // Now we need to do a re-layout, but preserving absolute X and Y coordinates
446d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        int childCount = getChildCount();
447d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        float childrenX[] = new float[childCount];
448d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        float childrenY[] = new float[childCount];
449d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        for (int i = 0; i < childCount; i++) {
450d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final View child = getChildAt(i);
451d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            childrenX[i] = child.getX();
452d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            childrenY[i] = child.getY();
453d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
454d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        onLayout(false, mLeft, mTop, mRight, mBottom);
455d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        for (int i = 0; i < childCount; i++) {
456d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final View child = getChildAt(i);
457d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            child.setX(childrenX[i]);
458d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            child.setY(childrenY[i]);
459d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
460d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // Also, the page offset has changed  (since the pages are now smaller);
461d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // update the page offset, but again preserving absolute X and Y coordinates
4628c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        scrollToNewPageWithoutMovingPages(mCurrentPage);
463d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    }
464d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
465cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    @Override
46628750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
4675f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(false);
469cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
470cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            scrollTo(newX, 0);
471cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            mScroller.setFinalX(newX);
472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(true);
473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mFirstLayout = false;
474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
4766b879f0a5885274a85333531e091283405d490ccAdam Lesinski        final int verticalPadding = mPaddingTop + mPaddingBottom;
477321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int childLeft = 0;
479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (childCount > 0) {
480e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            childLeft = getRelativeChildOffset(0);
481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
483321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final View child = getChildAt(i);
485321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (child.getVisibility() != View.GONE) {
486d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                final int childWidth = getScaledMeasuredWidth(child);
4876b879f0a5885274a85333531e091283405d490ccAdam Lesinski                final int childHeight = child.getMeasuredHeight();
4886b879f0a5885274a85333531e091283405d490ccAdam Lesinski                int childTop = mPaddingTop;
4896b879f0a5885274a85333531e091283405d490ccAdam Lesinski                if (mCenterPagesVertically) {
4906b879f0a5885274a85333531e091283405d490ccAdam Lesinski                    childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
4916b879f0a5885274a85333531e091283405d490ccAdam Lesinski                }
492d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
4936b879f0a5885274a85333531e091283405d490ccAdam Lesinski                child.layout(childLeft, childTop,
494d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                        childLeft + child.getMeasuredWidth(), childTop + childHeight);
4959c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                childLeft += childWidth + mPageSpacing;
496321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
497321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
498d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
499d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            mFirstLayout = false;
500d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
501321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
502321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
50303929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung    protected void forceUpdateAdjacentPagesAlpha() {
50403929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung        mDirtyPageAlpha = true;
50503929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung        updateAdjacentPagesAlpha();
50603929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung    }
50703929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung
508e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void updateAdjacentPagesAlpha() {
5090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mFadeInAdjacentScreens) {
5100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
511e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int halfScreenSize = getMeasuredWidth() / 2;
512e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int screenCenter = mScrollX + halfScreenSize;
5130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int childCount = getChildCount();
5140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < childCount; ++i) {
5150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    View layout = (View) getChildAt(i);
516d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                    int childWidth = getScaledMeasuredWidth(layout);
5170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int halfChildWidth = (childWidth / 2);
5180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childCenter = getChildOffset(i) + halfChildWidth;
519e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
520b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // On the first layout, we may not have a width nor a proper offset, so for now
521b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // we should just assume full page width (and calculate the offset according to
522b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // that).
523b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    if (childWidth <= 0) {
524b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                        childWidth = getMeasuredWidth();
525b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                        childCenter = (i * childWidth) + (childWidth / 2);
526b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    }
527b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung
528e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int d = halfChildWidth;
529e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int distanceFromScreenCenter = childCenter - screenCenter;
530e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    if (distanceFromScreenCenter > 0) {
531e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i > 0) {
532d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                            d += getScaledMeasuredWidth(getChildAt(i - 1)) / 2;
533e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
5340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
535e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i < childCount - 1) {
536d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                            d += getScaledMeasuredWidth(getChildAt(i + 1)) / 2;
537e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
5380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
5399c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                    d += mPageSpacing;
540e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
541b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // Preventing potential divide-by-zero
542b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    d = Math.max(1, d);
543b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung
544e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d;
545e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
546e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float alpha = 1.0f - dimAlpha;
547e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
548f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    if (alpha < ALPHA_QUANTIZE_LEVEL) {
549f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                        alpha = 0.0f;
550f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) {
551f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                        alpha = 1.0f;
552f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    }
553f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
554a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // Due to the way we're setting alpha on our children in PagedViewCellLayout,
555a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // this optimization causes alpha to not be properly updated sometimes (repro
556a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // case: in xlarge mode, swipe to second page in All Apps, then click on "My
557a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // Apps" tab. the page will have alpha 0 until you swipe it). Removing
558a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // optimization fixes the issue, but we should fix this in a better manner
559cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka                    //if (Float.compare(alpha, layout.getAlpha()) != 0) {
5600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        layout.setAlpha(alpha);
561cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka                    //}
562affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung                }
5630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageAlpha = false;
564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
566e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
5670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
568f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    protected void screenScrolled(int screenCenter) {
569f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    }
570f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
571e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    @Override
572e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void dispatchDraw(Canvas canvas) {
573f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int halfScreenSize = getMeasuredWidth() / 2;
574f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int screenCenter = mScrollX + halfScreenSize;
575f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
576f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        if (screenCenter != mLastScreenCenter) {
577f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            screenScrolled(screenCenter);
578f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            updateAdjacentPagesAlpha();
579f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            mLastScreenCenter = screenCenter;
580f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        }
5810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
582e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        // Find out which screens are visible; as an optimization we only call draw on them
5830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // As an optimization, this code assumes that all pages have the same width as the 0th
5840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // page.
5850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int pageCount = getChildCount();
586c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka        if (pageCount > 0) {
587d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final int pageWidth = getScaledMeasuredWidth(getChildAt(0));
588c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final int screenWidth = getMeasuredWidth();
589c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int x = getRelativeChildOffset(0) + pageWidth;
590c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int leftScreen = 0;
591c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int rightScreen = 0;
592c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            while (x <= mScrollX) {
593c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                leftScreen++;
594c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                x += getScaledMeasuredWidth(getChildAt(leftScreen)) + mPageSpacing;
595c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
596c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = leftScreen;
597c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung            while (x < mScrollX + screenWidth && rightScreen < pageCount) {
598c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                rightScreen++;
599c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                if (rightScreen < pageCount) {
600c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                    x += getScaledMeasuredWidth(getChildAt(rightScreen)) + mPageSpacing;
601c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                }
602c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
603c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = Math.min(getChildCount() - 1, rightScreen);
6040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
605c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final long drawingTime = getDrawingTime();
60629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            // Clip to the bounds
60729d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.save();
60829d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft,
60929d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung                    mScrollY + mBottom - mTop);
61029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung
611c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            for (int i = leftScreen; i <= rightScreen; i++) {
612c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                drawChild(canvas, getChildAt(i), drawingTime);
613c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
61429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.restore();
6150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
617321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
618321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
619321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
62086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
62186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page != mCurrentPage || !mScroller.isFinished()) {
62286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
626321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
63086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int focusablePage;
63186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mNextPage != INVALID_PAGE) {
63286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mNextPage;
633321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
63486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mCurrentPage;
635321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
63686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View v = getPageAt(focusablePage);
637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
638321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            v.requestFocus(direction, previouslyFocusedRect);
639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean dispatchUnhandledMove(View focused, int direction) {
645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
64686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() > 0) {
64786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() - 1);
648321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
649321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT) {
65186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() < getPageCount() - 1) {
65286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() + 1);
653321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
654321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
655321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
656321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return super.dispatchUnhandledMove(focused, direction);
657321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
66186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
66286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(mCurrentPage).addFocusables(views, direction);
663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
66586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) {
66686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage - 1).addFocusables(views, direction);
667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT){
66986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getPageCount() - 1) {
67086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage + 1).addFocusables(views, direction);
671321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
673321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
674321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
675321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * If one of our descendant views decides that it could be focused now, only
67786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * pass that along if it's on the current page.
678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
67986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This happens when live folders requery, and if they're off page, they
68086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * end up calling requestFocus, which pulls it on page.
681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
683321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void focusableViewAvailable(View focused) {
68486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View current = getPageAt(mCurrentPage);
685321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        View v = focused;
686321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        while (true) {
687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == current) {
688321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                super.focusableViewAvailable(focused);
689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
690321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == this) {
692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent parent = v.getParent();
695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (parent instanceof View) {
696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                v = (View)v.getParent();
697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else {
698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * {@inheritDoc}
705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (disallowIntercept) {
709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // We need to make sure to cancel our long press if
710321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // a scrollable widget takes over touch events
71186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            final View currentPage = getChildAt(mCurrentPage);
71286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage.cancelLongPress();
713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
714321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestDisallowInterceptTouchEvent(disallowIntercept);
715321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
716321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
717d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    /**
718d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     * Return true if a tap at (x, y) should trigger a flip to the previous page.
719d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     */
720d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    protected boolean hitsPreviousPage(float x, float y) {
721d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy        return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
722d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    }
723d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy
724d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    /**
725d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     * Return true if a tap at (x, y) should trigger a flip to the next page.
726d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     */
727d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    protected boolean hitsNextPage(float x, float y) {
728d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy        return  (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
729d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    }
730d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy
731321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * This method JUST determines whether we want to intercept the motion.
735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * If we return true, onTouchEvent will be called and we do the actual
736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * scrolling there.
737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
73945e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
74045e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
74145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Shortcut the most recurring case: the user is in the dragging
744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * state and he is moving his finger.  We want to intercept this
745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * motion.
746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if ((action == MotionEvent.ACTION_MOVE) &&
749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                (mTouchState == TOUCH_STATE_SCROLLING)) {
750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
753321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_MOVE: {
755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * whether the user has moved far enough from his original down touch.
758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
7591ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                if (mActivePointerId != INVALID_POINTER) {
7601ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    determineScrollingStart(ev);
7611ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    break;
7621ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                }
7631ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
7641ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
7651ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // i.e. fall through to the next case (don't break)
7661ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
7671ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // while it's small- this was causing a crash before we checked for INVALID_POINTER)
768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_DOWN: {
771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX();
772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float y = ev.getY();
773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Remember location of down touch
774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mDownMotionX = x;
775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionY = y;
777c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                mLastMotionXRemainder = 0;
778aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                mTotalMotionX = 0;
779321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = ev.getPointerId(0);
780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = true;
781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * If being flinged and user touches the screen, initiate drag;
784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * otherwise don't.  mScroller.isFinished should be false when
785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * being flinged.
786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
787fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka                final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
7885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
7895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                if (finishedScrolling) {
7905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_REST;
7915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mScroller.abortAnimation();
7925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                } else {
7935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_SCROLLING;
7945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
79686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                // check if this can be the beginning of a tap on the side of the pages
797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // to scroll the current page
798d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (getChildCount() > 0) {
800d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                        if (hitsPreviousPage(x, y)) {
801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_PREV_PAGE;
802d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                        } else if (hitsNextPage(x, y)) {
803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_NEXT_PAGE;
804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        }
805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_UP:
8111d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown                onWallpaperTap(ev);
8121d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            case MotionEvent.ACTION_CANCEL:
813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_REST;
814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = INVALID_POINTER;
816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_POINTER_UP:
819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                onSecondaryPointerUp(ev);
820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * The only time we want to intercept motion events is if we are in the
825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * drag mode.
826321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
827321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mTouchState != TOUCH_STATE_REST;
828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
83080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    protected void animateClickFeedback(View v, final Runnable r) {
83180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // animate the view slightly to show click feedback running some logic after it is "pressed"
832228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.
833228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung                loadAnimator(mContext, R.anim.paged_view_click_feedback);
834228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.setTarget(v);
835228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.addListener(new AnimatorListenerAdapter() {
836228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung            public void onAnimationRepeat(Animator animation) {
83780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                r.run();
83880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
83980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        });
840228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.start();
84180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
84280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
843f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void determineScrollingStart(MotionEvent ev) {
844f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        determineScrollingStart(ev, 1.0f);
845f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    }
846f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen
847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /*
848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Determines if we should change the touch state to start scrolling after the
849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * user moves their touch point too far.
850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
851f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Locally do absolute value. mLastMotionX is set to the y value
854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * of the down event.
855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float x = ev.getX(pointerIndex);
858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float y = ev.getY(pointerIndex);
859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int xDiff = (int) Math.abs(x - mLastMotionX);
860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int yDiff = (int) Math.abs(y - mLastMotionY);
861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
862f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xPaged = xDiff > mPagingTouchSlop;
864321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xMoved = xDiff > touchSlop;
865321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean yMoved = yDiff > touchSlop;
866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
867f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        if (xMoved || xPaged || yMoved) {
8680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mUsePagingTouchSlop ? xPaged : xMoved) {
869321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll if the user moved far enough along the X axis
870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_SCROLLING;
871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
872c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                mLastMotionXRemainder = 0;
8730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mTouchX = mScrollX;
8740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
8750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Either way, cancel any pending longpress
878f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            cancelCurrentPageLongPress();
879f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        }
880f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    }
881f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen
882f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void cancelCurrentPageLongPress() {
883f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        if (mAllowLongPress) {
884f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            mAllowLongPress = false;
885f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // Try canceling the long press. It could also have been scheduled
886f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // by a distant descendant, so use the mAllowLongPress flag to block
887f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // everything
888f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            final View currentPage = getPageAt(mCurrentPage);
889f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            if (currentPage != null) {
890f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen                currentPage.cancelLongPress();
891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
895e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // This curve determines how the effect of scrolling over the limits of the page dimishes
896e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // as the user pulls further and further from the bounds
897e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private float overScrollInfluenceCurve(float f) {
898e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f -= 1.0f;
899e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        return f * f * f + 1.0f;
900e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
901e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
90268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected void overScroll(float amount) {
903e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int screenSize = getMeasuredWidth();
904e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
905e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float f = (amount / screenSize);
906e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
907e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        if (f == 0) return;
908e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
909e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
9107bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        // Clamp this factor, f, to -1 < f < 1
9117bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        if (Math.abs(f) >= 1) {
9127bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen            f /= Math.abs(f);
9137bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        }
9147bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen
915e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
91668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        if (amount < 0) {
91768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            mScrollX = overScrollAmount;
91868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else {
91968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            mScrollX = mMaxScrollX + overScrollAmount;
92068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        }
92168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        invalidate();
92268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    }
92368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
924c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka    protected float maxOverScroll() {
925c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
926c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        // exceed). Used to find out how much extra wallpaper we need for the overscroll effect
927c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        float f = 1.0f;
928c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
929c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        return OVERSCROLL_DAMP_FACTOR * f;
930c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka    }
931c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka
932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onTouchEvent(MotionEvent ev) {
93445e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
93545e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onTouchEvent(ev);
93645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
937b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        acquireVelocityTrackerAndAddMovement(ev);
938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_DOWN:
943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            /*
944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * If being flinged and user touches, stop the fling. isFinished
945321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * will be false if being flinged.
946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             */
947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (!mScroller.isFinished()) {
948321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mScroller.abortAnimation();
949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Remember where the motion event started
952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mDownMotionX = mLastMotionX = ev.getX();
953c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung            mLastMotionXRemainder = 0;
954aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen            mTotalMotionX = 0;
955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(0);
9560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
9570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
9580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_MOVE:
962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll to follow the motion event
964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
966c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                final float deltaX = mLastMotionX + mLastMotionXRemainder - x;
967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
968aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                mTotalMotionX += Math.abs(deltaX);
969aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
970c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // Only scroll and update mLastMotionX if we have moved some discrete amount.  We
971c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // keep the remainder because we are actually testing if we've moved from the last
972c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // scrolled position (which is discrete).
973c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                if (Math.abs(deltaX) >= 1.0f) {
97468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    mTouchX += deltaX;
97568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
97668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    if (!mDeferScrollUpdate) {
977c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                        scrollBy((int) deltaX, 0);
97868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    } else {
97968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                        invalidate();
980321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
981c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                    mLastMotionX = x;
982c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                    mLastMotionXRemainder = deltaX - (int) deltaX;
983321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
984321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    awakenScrollBars();
985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
986564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen            } else {
987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                determineScrollingStart(ev);
988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
989321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
990321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_UP:
992321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
993321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int activePointerId = mActivePointerId;
994321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(activePointerId);
995321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
996321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final VelocityTracker velocityTracker = mVelocityTracker;
997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
998321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
9999cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                final int deltaX = (int) (x - mDownMotionX);
1000b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE;
10010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int snapVelocity = mSnapVelocity;
1002aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1003b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
1004b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen
1005aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // In the case that the page is moved far to one direction and then is flung
1006aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // in the opposite direction, we use a threshold to determine whether we should
1007aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // just return to the starting page, or if we should skip one further.
1008aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                boolean returnToOriginalPage = false;
1009aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                final int pageWidth = getScaledMeasuredWidth(getChildAt(mCurrentPage));
1010b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
1011aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        Math.signum(velocityX) != Math.signum(deltaX)) {
1012aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    returnToOriginalPage = true;
1013aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                }
1014aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1015b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
1016aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        Math.abs(velocityX) > snapVelocity;
1017aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1018aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                int finalPage;
1019aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // We give flings precedence over large moves, which is why we short-circuit our
1020aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // test for a large move if a fling has been registered. That is, a large
1021aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // move to the left and fling to the right will register as a fling to the right.
1022aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                if (((isSignificantMove && deltaX > 0 && !isFling) ||
1023aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        (isFling && velocityX > 0)) && mCurrentPage > 0) {
1024aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
1025aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    snapToPageWithVelocity(finalPage, velocityX);
1026aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
1027aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        (isFling && velocityX < 0)) &&
102886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                        mCurrentPage < getChildCount() - 1) {
1029aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
1030aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    snapToPageWithVelocity(finalPage, velocityX);
1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1033321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1034d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
1035321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
1036321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
1037321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
103886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.max(0, mCurrentPage - 1);
103986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
104086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
1041321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1042321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1043321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1044d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
1045321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
1046321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
1047321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
104886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
104986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
105086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
1051321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1052321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1053321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
10541d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            } else {
10551d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown                onWallpaperTap(ev);
1056321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
1058321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
1059b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
1060321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1061321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1062321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_CANCEL:
1063b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
1064b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka                snapToDestination();
1065b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            }
1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
1067321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
1068b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
1069321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1070321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1071321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_POINTER_UP:
1072321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            onSecondaryPointerUp(ev);
1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1075321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1076321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return true;
1077321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1078321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1079b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
1080b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker == null) {
1081b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = VelocityTracker.obtain();
1082b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
1083b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        mVelocityTracker.addMovement(ev);
1084b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
1085b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
1086b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void releaseVelocityTracker() {
1087b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker != null) {
1088b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker.recycle();
1089b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = null;
1090b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
1091b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
1092b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
1093321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void onSecondaryPointerUp(MotionEvent ev) {
1094321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1095321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1096321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerId = ev.getPointerId(pointerIndex);
1097321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (pointerId == mActivePointerId) {
1098321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // This was our active pointer going up. Choose a new
1099321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // active pointer and adjust accordingly.
1100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // TODO: Make this decision more intelligent.
1101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
1103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionY = ev.getY(newPointerIndex);
1104c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung            mLastMotionXRemainder = 0;
1105321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(newPointerIndex);
1106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mVelocityTracker != null) {
1107321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mVelocityTracker.clear();
1108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
11101d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown        if (mTouchState == TOUCH_STATE_REST) {
11111d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            onWallpaperTap(ev);
11121d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown        }
11131d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown    }
11141d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown
11151d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown    protected void onWallpaperTap(MotionEvent ev) {
1116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1117321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1118321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
1119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestChildFocus(View child, View focused) {
1120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestChildFocus(child, focused);
112186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
112286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page >= 0 && !isInTouchMode()) {
112386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1127e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getChildIndexForRelativeOffset(int relativeOffset) {
1128e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int childCount = getChildCount();
11299c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int left;
11309c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int right;
1131e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        for (int i = 0; i < childCount; ++i) {
11329c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            left = getRelativeChildOffset(i);
1133d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            right = (left + getScaledMeasuredWidth(getChildAt(i)));
1134e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            if (left <= relativeOffset && relativeOffset <= right) {
1135e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                return i;
1136e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            }
1137e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        }
1138e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return -1;
1139e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1140e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
1141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getRelativeChildOffset(int index) {
1142321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2;
1143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1144321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getChildOffset(int index) {
1146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (getChildCount() == 0)
1147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return 0;
1148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int offset = getRelativeChildOffset(0);
1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < index; ++i) {
1151d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            offset += getScaledMeasuredWidth(getChildAt(i)) + mPageSpacing;
1152321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1153321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return offset;
1154321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1156d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    protected int getScaledMeasuredWidth(View child) {
1157d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        return (int) (child.getMeasuredWidth() * mLayoutScale + 0.5f);
1158d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    }
1159d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
1160d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    int getPageNearestToCenterOfScreen() {
1161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenter = getMeasuredWidth();
1162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenterIndex = -1;
1163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int screenCenter = mScrollX + (getMeasuredWidth() / 2);
1164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
1165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; ++i) {
11660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            View layout = (View) getChildAt(i);
1167d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            int childWidth = getScaledMeasuredWidth(layout);
1168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int halfChildWidth = (childWidth / 2);
1169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childCenter = getChildOffset(i) + halfChildWidth;
1170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
1171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
1172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenter = distanceFromScreenCenter;
1173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenterIndex = i;
1174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1176d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        return minDistanceFromScreenCenterIndex;
1177d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    }
1178d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen
1179d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    protected void snapToDestination() {
1180d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
1181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1183e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static class ScrollInterpolator implements Interpolator {
1184e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        public ScrollInterpolator() {
1185e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1186e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1187e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        public float getInterpolation(float t) {
1188e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            t -= 1.0f;
1189e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            return t*t*t*t*t + 1;
1190e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1191e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1192e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1193e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // We want the duration of the page snap animation to be influenced by the distance that
1194e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // the screen has to travel, however, we don't want this duration to be effected in a
1195e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
1196e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // of travel has on the overall snap duration.
1197e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    float distanceInfluenceForSnapDuration(float f) {
1198e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f -= 0.5f; // center the values about 0.
1199e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f *= 0.3f * Math.PI / 2.0f;
1200e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        return (float) Math.sin(f);
1201e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1202e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
12030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPageWithVelocity(int whichPage, int velocity) {
1204e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
1205e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int halfScreenSize = getMeasuredWidth() / 2;
1206e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1207e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
1208e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int delta = newX - mUnboundedScrollX;
1209e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int duration = 0;
1210e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1211e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        if (Math.abs(velocity) < MIN_FLING_VELOCITY) {
1212e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            // If the velocity is low enough, then treat this more as an automatic page advance
1213e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            // as opposed to an apparent physical response to flinging
1214e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1215e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            return;
1216e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1217e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1218e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // Here we compute a "distance" that will be used in the computation of the overall
1219e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // snap duration. This is a function of the actual distance that needs to be traveled;
1220e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // we keep this value close to half screen size in order to reduce the variance in snap
1221e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // duration as a function of the distance the page needs to travel.
1222e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float distanceRatio = 1.0f * Math.abs(delta) / 2 * halfScreenSize;
1223e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float distance = halfScreenSize + halfScreenSize *
1224e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen                distanceInfluenceForSnapDuration(distanceRatio);
1225e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1226e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        velocity = Math.abs(velocity);
1227e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
1228e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1229e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // we want the page's snap velocity to approximately match the velocity at which the
1230e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // user flings, so we scale the duration by a value near to the derivative of the scroll
1231e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // interpolator at zero, ie. 5. We use 6 to make it a little slower.
1232e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        duration = 6 * Math.round(1000 * Math.abs(distance / velocity));
1233e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1234e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        snapToPage(whichPage, delta, duration);
12350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
12360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
12370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage) {
12385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
12410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int duration) {
124286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
1243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
124486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
124568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        final int sX = mUnboundedScrollX;
1246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int delta = newX - sX;
12470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage, delta, duration);
12480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
12490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
12500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int delta, int duration) {
12510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mNextPage = whichPage;
12520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
12530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        View focusedChild = getFocusedChild();
12540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (focusedChild != null && whichPage != mCurrentPage &&
12550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                focusedChild == getChildAt(mCurrentPage)) {
12560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            focusedChild.clearFocus();
12570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
12580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
12590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        pageBeginMoving();
1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        awakenScrollBars(duration);
1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (duration == 0) {
1262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            duration = Math.abs(delta);
1263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
126668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
126780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
126880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // only load some associated pages
126986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        loadAssociatedPages(mNextPage);
12700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        notifyPageSwitchListener();
1271321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
1272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollLeft() {
1275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
127686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
1277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
127886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage > 0) snapToPage(mNextPage - 1);
1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollRight() {
1283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
128486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
128686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
129086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public int getPageForView(View v) {
1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int result = -1;
1292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
1293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent vp = v.getParent();
1294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int count = getChildCount();
1295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            for (int i = 0; i < count; i++) {
1296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (vp == getChildAt(i)) {
1297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    return i;
1298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return result;
1302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
1305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @return True is long presses are still allowed for the current touch
1306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean allowLongPress() {
1308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mAllowLongPress;
1309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
13110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
13120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * Set true to allow long-press events to be triggered, usually checked by
13130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * {@link Launcher} to accept or block dpad-initiated long-presses.
13140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     */
13150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setAllowLongPress(boolean allowLongPress) {
13160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mAllowLongPress = allowLongPress;
13170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
13180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1319321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public static class SavedState extends BaseSavedState {
132086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int currentPage = -1;
1321321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState(Parcelable superState) {
1323321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(superState);
1324321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1326321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        private SavedState(Parcel in) {
1327321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(in);
132886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage = in.readInt();
1329321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1330321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1331321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        @Override
1332321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public void writeToParcel(Parcel out, int flags) {
1333321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super.writeToParcel(out, flags);
133486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            out.writeInt(currentPage);
1335321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1336321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public static final Parcelable.Creator<SavedState> CREATOR =
1338321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                new Parcelable.Creator<SavedState>() {
1339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState createFromParcel(Parcel in) {
1340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState(in);
1341321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1342321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState[] newArray(int size) {
1344321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState[size];
1345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        };
1347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1348321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
134986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void loadAssociatedPages(int page) {
13500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
13510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
13520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (page < count) {
1353e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int lowerPageBound = getAssociatedLowerPageBound(page);
1354e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int upperPageBound = getAssociatedUpperPageBound(page);
13550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < count; ++i) {
13568245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                    Page layout = (Page) getChildAt(i);
13578245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                    final int childCount = layout.getPageChildCount();
13580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (lowerPageBound <= i && i <= upperPageBound) {
13590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (mDirtyPageContent.get(i)) {
13600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            syncPageItems(i);
13610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            mDirtyPageContent.set(i, false);
13620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
13630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
13640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (childCount > 0) {
13658245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                            layout.removeAllViewsOnPage();
13660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
13670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mDirtyPageContent.set(i, true);
136880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                    }
136980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                }
137080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
137180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        }
137280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
137380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1374e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedLowerPageBound(int page) {
1375e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.max(0, page - 1);
1376e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1377e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedUpperPageBound(int page) {
1378e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int count = getChildCount();
1379e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.min(page + 1, count - 1);
1380e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1381e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
13825f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void startChoiceMode(int mode, ActionMode.Callback callback) {
1383430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        if (isChoiceMode(CHOICE_MODE_NONE)) {
1384430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mChoiceMode = mode;
1385430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mActionMode = startActionMode(callback);
1386430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        }
13875f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
13885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
13892b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public void endChoiceMode() {
13905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        if (!isChoiceMode(CHOICE_MODE_NONE)) {
13915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            mChoiceMode = CHOICE_MODE_NONE;
13925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            resetCheckedGrandchildren();
1393e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka            if (mActionMode != null) mActionMode.finish();
13949f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            mActionMode = null;
13955f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
13965f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
13975f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
13985f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected boolean isChoiceMode(int mode) {
13995f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return mChoiceMode == mode;
14005f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
14015f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
14025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected ArrayList<Checkable> getCheckedGrandchildren() {
14035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        ArrayList<Checkable> checked = new ArrayList<Checkable>();
14045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final int childCount = getChildCount();
14055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < childCount; ++i) {
14068245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka            Page layout = (Page) getChildAt(i);
14078245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka            final int grandChildCount = layout.getPageChildCount();
14085f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            for (int j = 0; j < grandChildCount; ++j) {
14098245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                final View v = layout.getChildOnPageAt(j);
14109f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                if (v instanceof Checkable && ((Checkable) v).isChecked()) {
14115f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    checked.add((Checkable) v);
14125f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
14135f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            }
14145f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
14155f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return checked;
14165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
14175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
14189f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    /**
14199f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * If in CHOICE_MODE_SINGLE and an item is checked, returns that item.
14209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * Otherwise, returns null.
14219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     */
14229f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    protected Checkable getSingleCheckedGrandchild() {
14236f13342ffd3f968de9ff86b988621cc91d94adffPatrick Dubroy        if (mChoiceMode != CHOICE_MODE_MULTIPLE) {
14249f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            final int childCount = getChildCount();
14259f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            for (int i = 0; i < childCount; ++i) {
14268245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                Page layout = (Page) getChildAt(i);
14278245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                final int grandChildCount = layout.getPageChildCount();
14289f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                for (int j = 0; j < grandChildCount; ++j) {
14298245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                    final View v = layout.getChildOnPageAt(j);
14309f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    if (v instanceof Checkable && ((Checkable) v).isChecked()) {
14319f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                        return (Checkable) v;
14329f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    }
14339f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                }
14349f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            }
14359f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        }
14369f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        return null;
14379f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    }
14389f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
14395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void resetCheckedGrandchildren() {
14405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        // loop through children, and set all of their children to _not_ be checked
14415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final ArrayList<Checkable> checked = getCheckedGrandchildren();
14425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < checked.size(); ++i) {
14435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final Checkable c = checked.get(i);
14445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            c.setChecked(false);
14455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
14465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
14475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
1448a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung    public void setRestorePage(int restorePage) {
1449a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung        mRestorePage = restorePage;
1450a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung    }
1451a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung
145286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
145386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called ONLY to synchronize the number of pages that the paged view has.
145486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * To actually fill the pages with information, implement syncPageItems() below.  It is
145586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * guaranteed that syncPageItems() will be called for a particular page before it is shown,
145686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * and therefore, individual page items do not need to be updated in this method.
145786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1458321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPages();
145986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
146086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
146186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called to synchronize the items that are on a particular page.  If views on
146286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * the page can be reused, then they should be updated within this method.
146386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1464321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPageItems(int page);
146586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
1466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void invalidatePageData() {
14670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
14680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Update all the pages
14690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            syncPages();
147086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
14710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Mark each of the pages as dirty
14720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
14730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageContent.clear();
14740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            for (int i = 0; i < count; ++i) {
14750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageContent.add(true);
14760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
147786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
1478a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung            // Use the restore page if necessary
1479a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung            if (mRestorePage > -1) {
1480a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung                mCurrentPage = mRestorePage;
1481a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung                mRestorePage = -1;
1482a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung            }
1483a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung
14840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Load any pages that are necessary for the current window of views
14850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            loadAssociatedPages(mCurrentPage);
14860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageAlpha = true;
1487b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung            updateAdjacentPagesAlpha();
14880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            requestLayout();
14890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
1490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung}
1492