PagedView.java revision e326f186af6b00e4ea32849f1527254c669d0600
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
19228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.Animator;
20228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorInflater;
21228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorListenerAdapter;
22228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.ObjectAnimator;
23bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chungimport android.animation.ValueAnimator;
24321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context;
259c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray;
26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas;
27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect;
28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel;
29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable;
30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet;
31785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chungimport android.util.Log;
325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode;
33185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.InputDevice;
34185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.KeyEvent;
35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent;
36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker;
37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View;
38321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration;
39321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup;
40321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent;
416a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityEvent;
42c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chungimport android.view.accessibility.AccessibilityManager;
436a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityNodeInfo;
44e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator;
455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable;
46007c69867d821ea2b271398577a8b3440b3a7046Winson Chungimport android.widget.ImageView;
47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller;
48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
490499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R;
5080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
516a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport java.util.ArrayList;
526a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
53321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/**
54321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a
550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages"
56321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
57321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup {
58321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final String TAG = "PagedView";
59785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung    private static final boolean DEBUG = false;
600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final int INVALID_PAGE = -1;
61321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
6286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    // the min drag distance for a fling to register, to prevent random page shifts
639cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_FLING = 25;
64321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
65e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int PAGE_SNAP_ANIMATION_DURATION = 550;
660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final float NANOTIME_DIV = 1000000000.0f;
670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
68b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
69b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung    private static final float OVERSCROLL_DAMP_FACTOR = 0.14f;
70e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int MINIMUM_SNAP_VELOCITY = 2200;
71e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int MIN_FLING_VELOCITY = 250;
72b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen    private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
7300481b3ed322b4f36b669201d2287964febda65fAdam Cohen    // The page is moved more than halfway, automatically move to the next page on touch up.
7400481b3ed322b4f36b669201d2287964febda65fAdam Cohen    private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
7568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // the velocity at which a fling gesture will cause us to snap to the next page
770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mSnapVelocity = 500;
780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
79b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    protected float mDensity;
800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mSmoothingTime;
810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mTouchX;
82321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFirstLayout = true;
840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mCurrentPage;
860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mNextPage = INVALID_PAGE;
8768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected int mMaxScrollX;
880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected Scroller mScroller;
89321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private VelocityTracker mVelocityTracker;
90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
91321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mDownMotionX;
927426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected float mLastMotionX;
93c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung    protected float mLastMotionXRemainder;
947426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected float mLastMotionY;
95aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen    protected float mTotalMotionX;
96f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    private int mLastScreenCenter = -1;
9773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    private int[] mChildOffsets;
9873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    private int[] mChildRelativeOffsets;
9973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    private int[] mChildOffsetsWithLayoutScale;
100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_REST = 0;
1020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_SCROLLING = 1;
1030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_PREV_PAGE = 2;
1040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
105e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen    protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mTouchState = TOUCH_STATE_REST;
1082591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen    protected boolean mForceScreenScrolled = false;
109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected OnLongClickListener mLongClickListener;
111321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1127426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected boolean mAllowLongPress = true;
113321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1147426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected int mTouchSlop;
115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mPagingTouchSlop;
116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mMaximumVelocity;
1171908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung    private int mMinimumWidth;
1189c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageSpacing;
1199c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingTop;
1209c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingBottom;
1219c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingLeft;
1229c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingRight;
123df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutWidthGap;
124df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutHeightGap;
12587b1490c5a100619648b251cb2be05c457bede08Michael Jurka    protected int mCellCountX = 0;
12687b1490c5a100619648b251cb2be05c457bede08Michael Jurka    protected int mCellCountY = 0;
1277da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung    protected boolean mCenterPagesVertically;
12868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected boolean mAllowOverScroll = true;
12968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected int mUnboundedScrollX;
130dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka    protected int[] mTempVisiblePagesRange = new int[2];
131321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
132ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    // mOverScrollX is equal to mScrollX when we're within the normal scroll range. Otherwise
133ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    // it is equal to the scaled overscroll position. We use a separate value so as to prevent
134ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    // the screens from continuing to translate beyond the normal bounds.
135ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    protected int mOverScrollX;
136ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen
1378c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // parameter that adjusts the layout to be optimized for pages with that scale factor
138d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    protected float mLayoutScale = 1.0f;
139d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
1405f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected static final int INVALID_POINTER = -1;
141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1425f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected int mActivePointerId = INVALID_POINTER;
143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
14486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private PageSwitchListener mPageSwitchListener;
145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
146e326f186af6b00e4ea32849f1527254c669d0600Michael Jurka    protected ArrayList<Boolean> mDirtyPageContent;
147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // choice modes
1495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_NONE = 0;
1505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_SINGLE = 1;
1515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // Multiple selection mode is not supported by all Launcher actions atm
1525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_MULTIPLE = 2;
1539f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
154e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka    protected int mChoiceMode;
1555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private ActionMode mActionMode;
1565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
1570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, syncPages and syncPageItems will be called to refresh pages
1580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mContentIsRefreshable = true;
1590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, modify alpha of neighboring pages as user scrolls left/right
1610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFadeInAdjacentScreens = true;
1620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
1640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // to switch to a new page
1650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mUsePagingTouchSlop = true;
1660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, the subclass should directly update mScrollX itself in its computeScroll method
1680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // (SmoothPagedView does this)
1690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mDeferScrollUpdate = false;
1700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1711262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected boolean mIsPageMoving = false;
1721262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
173f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    // All syncs and layout passes are deferred until data is ready.
174f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    protected boolean mIsDataReady = false;
175f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
176007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    // Scrolling indicator
177bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chung    private ValueAnimator mScrollIndicatorAnimator;
178007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private ImageView mScrollIndicator;
179f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung    private int mScrollIndicatorPaddingLeft;
180f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung    private int mScrollIndicatorPaddingRight;
181007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private boolean mHasScrollIndicator = true;
182a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung    protected static final int sScrollIndicatorFadeInDuration = 150;
183a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung    protected static final int sScrollIndicatorFadeOutDuration = 650;
184a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung    protected static final int sScrollIndicatorFlashDuration = 650;
185007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
186b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung    // If set, will defer loading associated pages until the scrolling settles
1874e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung    private boolean mDeferLoadAssociatedPagesUntilScrollCompletes;
188b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung
18986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public interface PageSwitchListener {
19086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        void onPageSwitch(View newPage, int newPageIndex);
191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context) {
194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, null);
195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
197321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs) {
198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, attrs, 0);
199321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs, int defStyle) {
202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super(context, attrs, defStyle);
2035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        mChoiceMode = CHOICE_MODE_NONE;
204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2059c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
2069c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView, defStyle, 0);
20760b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen        setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
2089c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingTop = a.getDimensionPixelSize(
2091908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung                R.styleable.PagedView_pageLayoutPaddingTop, 0);
2109c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingBottom = a.getDimensionPixelSize(
2111908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung                R.styleable.PagedView_pageLayoutPaddingBottom, 0);
2129c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingLeft = a.getDimensionPixelSize(
2131908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung                R.styleable.PagedView_pageLayoutPaddingLeft, 0);
2149c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingRight = a.getDimensionPixelSize(
2151908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung                R.styleable.PagedView_pageLayoutPaddingRight, 0);
216df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutWidthGap = a.getDimensionPixelSize(
2177d7541e7b48fdc114c24b3b0aa75e70d7228041eWinson Chung                R.styleable.PagedView_pageLayoutWidthGap, 0);
218df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutHeightGap = a.getDimensionPixelSize(
2197d7541e7b48fdc114c24b3b0aa75e70d7228041eWinson Chung                R.styleable.PagedView_pageLayoutHeightGap, 0);
220f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung        mScrollIndicatorPaddingLeft =
221f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung            a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
222f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung        mScrollIndicatorPaddingRight =
223f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung            a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
2249c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        a.recycle();
2259c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen
226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setHapticFeedbackEnabled(false);
2270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        init();
228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Initializes various states for this workspace.
232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
2330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void init() {
23486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent = new ArrayList<Boolean>();
23586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent.ensureCapacity(32);
236e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        mScroller = new Scroller(getContext(), new ScrollInterpolator());
23786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = 0;
2387da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung        mCenterPagesVertically = true;
239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mTouchSlop = configuration.getScaledTouchSlop();
242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
244b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mDensity = getResources().getDisplayMetrics().density;
245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
24786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
24886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mPageSwitchListener = pageSwitchListener;
24986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
25086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
255f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung     * Called by subclasses to mark that data is ready, and that we can begin loading and laying
256f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung     * out pages.
257f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung     */
258f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    protected void setDataIsReady() {
259f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        mIsDataReady = true;
260f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    }
261f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    protected boolean isDataReady() {
262f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        return mIsDataReady;
263f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    }
264f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
265f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    /**
26686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Returns the index of the currently displayed page.
267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
26886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * @return The index of the currently displayed page.
269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
27086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getCurrentPage() {
27186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        return mCurrentPage;
272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
27486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getPageCount() {
275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildCount();
276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
27886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    View getPageAt(int index) {
279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildAt(index);
280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
282ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen    protected int indexToPage(int index) {
283ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen        return index;
284ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen    }
285ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen
286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
287bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * Updates the scroll of the current page immediately to its final scroll position.  We use this
288bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
289bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * the previous tab page.
290bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     */
291bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    protected void updateCurrentPageScroll() {
292bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
293bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        scrollTo(newX, 0);
294bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        mScroller.setFinalX(newX);
295bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    }
296bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung
297bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    /**
29886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Sets the current page.
299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
30086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    void setCurrentPage(int currentPage) {
30172e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        if (!mScroller.isFinished()) {
30272e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            mScroller.abortAnimation();
30372e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
304d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
305d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // the default
306d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        if (getChildCount() == 0) {
30772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            return;
30872e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
31086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
311bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        updateCurrentPageScroll();
3125a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        updateScrollingIndicator();
31386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        notifyPageSwitchListener();
314a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung        invalidate();
315321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
316321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
3170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void notifyPageSwitchListener() {
31886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
31986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
320321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
321321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
323ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka    protected void pageBeginMoving() {
324d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka        if (!mIsPageMoving) {
325d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka            mIsPageMoving = true;
326d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka            onPageBeginMoving();
327d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka        }
3281262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
3291262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
330ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka    protected void pageEndMoving() {
331d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka        if (mIsPageMoving) {
332d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka            mIsPageMoving = false;
333d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka            onPageEndMoving();
334d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka        }
3351262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
3361262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
33726976d9b53161f80faf822d482ac771b8621cf31Adam Cohen    protected boolean isPageMoving() {
33826976d9b53161f80faf822d482ac771b8621cf31Adam Cohen        return mIsPageMoving;
33926976d9b53161f80faf822d482ac771b8621cf31Adam Cohen    }
34026976d9b53161f80faf822d482ac771b8621cf31Adam Cohen
3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
3421262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageBeginMoving() {
343430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka        showScrollingIndicator(false);
3440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
3471262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageEndMoving() {
348007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        hideScrollingIndicator(false);
3490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
35286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Registers the specified listener on each page contained in this workspace.
353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @param l The listener used to respond to long clicks.
355321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
357321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void setOnLongClickListener(OnLongClickListener l) {
358321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mLongClickListener = l;
35986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        final int count = getPageCount();
360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < count; i++) {
36186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(i).setOnLongClickListener(l);
362321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
365321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
36668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    public void scrollBy(int x, int y) {
36768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        scrollTo(mUnboundedScrollX + x, mScrollY + y);
36868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    }
36968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
37068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    @Override
3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void scrollTo(int x, int y) {
37268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        mUnboundedScrollX = x;
37368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
37468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        if (x < 0) {
37568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(0, y);
37668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            if (mAllowOverScroll) {
37768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                overScroll(x);
37868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            }
37968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else if (x > mMaxScrollX) {
38068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(mMaxScrollX, y);
38168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            if (mAllowOverScroll) {
38268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                overScroll(x - mMaxScrollX);
38368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            }
38468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else {
385ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mOverScrollX = x;
38668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(x, y);
38768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        }
38868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
3890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mTouchX = x;
3900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
3910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // we moved this functionality to a helper function so SmoothPagedView can reuse it
3940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean computeScrollHelper() {
395321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.computeScrollOffset()) {
396557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung            // Don't bother scrolling if the page does not need to be moved
397557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung            if (mScrollX != mScroller.getCurrX() || mScrollY != mScroller.getCurrY()) {
398557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
399557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung            }
4000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            invalidate();
4010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
40286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        } else if (mNextPage != INVALID_PAGE) {
40386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
40486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mNextPage = INVALID_PAGE;
4050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            notifyPageSwitchListener();
406b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung
407b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung            // Load the associated pages if necessary
4084e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung            if (mDeferLoadAssociatedPagesUntilScrollCompletes) {
409b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung                loadAssociatedPages(mCurrentPage);
4104e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung                mDeferLoadAssociatedPagesUntilScrollCompletes = false;
411b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung            }
412b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung
41373aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            // We don't want to trigger a page end moving unless the page has settled
41473aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            // and the user has stopped scrolling
41573aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            if (mTouchState == TOUCH_STATE_REST) {
41673aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen                pageEndMoving();
41773aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            }
418c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung
419c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung            // Notify the user when the page changes
420c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung            if (AccessibilityManager.getInstance(getContext()).isEnabled()) {
421c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung                AccessibilityEvent ev =
422c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung                    AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
423c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung                ev.getText().add(getCurrentPageDescription());
424c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung                sendAccessibilityEventUnchecked(ev);
425c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung            }
4260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
4280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        return false;
4290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
4300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
4310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    @Override
4320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void computeScroll() {
4330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        computeScrollHelper();
434321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
436321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
438f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        if (!mIsDataReady) {
439f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
440f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung            return;
441f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        }
442f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
444321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
445321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (widthMode != MeasureSpec.EXACTLY) {
446321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
447321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
448321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
4496b879f0a5885274a85333531e091283405d490ccAdam Lesinski        /* Allow the height to be set as WRAP_CONTENT. This allows the particular case
4506b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * of the All apps view on XLarge displays to not take up more space then it needs. Width
4516b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
4526b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * each page to have the same width.
4536b879f0a5885274a85333531e091283405d490ccAdam Lesinski         */
454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
4556b879f0a5885274a85333531e091283405d490ccAdam Lesinski        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
4566b879f0a5885274a85333531e091283405d490ccAdam Lesinski        int maxChildHeight = 0;
4576b879f0a5885274a85333531e091283405d490ccAdam Lesinski
4586b879f0a5885274a85333531e091283405d490ccAdam Lesinski        final int verticalPadding = mPaddingTop + mPaddingBottom;
459ea359c6aee44c0fe3bb94f7002c3b49208b32b7fWinson Chung        final int horizontalPadding = mPaddingLeft + mPaddingRight;
460321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
46136fcb74425bebc1d88c9e3102484ec902b68f202Michael Jurka
462321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        // The children are given the same width and height as the workspace
4635f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        // unless they were set to WRAP_CONTENT
464785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
4675f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            // disallowing padding in paged view (just pass 0)
46822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            final View child = getPageAt(i);
4695f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4705f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4715f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childWidthMode;
4725f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.width == LayoutParams.WRAP_CONTENT) {
4735f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.AT_MOST;
4745f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
4755f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.EXACTLY;
4765f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
4775f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4785f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childHeightMode;
4795f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.height == LayoutParams.WRAP_CONTENT) {
4805f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.AT_MOST;
4815f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
4825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.EXACTLY;
4835f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
4845f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childWidthMeasureSpec =
486ea359c6aee44c0fe3bb94f7002c3b49208b32b7fWinson Chung                MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode);
4875f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childHeightMeasureSpec =
4886b879f0a5885274a85333531e091283405d490ccAdam Lesinski                MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
4895f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4905f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4916b879f0a5885274a85333531e091283405d490ccAdam Lesinski            maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
492785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung            if (DEBUG) Log.d(TAG, "\tmeasure-child" + i + ": " + child.getMeasuredWidth() + ", "
493785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                    + child.getMeasuredHeight());
4946b879f0a5885274a85333531e091283405d490ccAdam Lesinski        }
4956b879f0a5885274a85333531e091283405d490ccAdam Lesinski
4966b879f0a5885274a85333531e091283405d490ccAdam Lesinski        if (heightMode == MeasureSpec.AT_MOST) {
4976b879f0a5885274a85333531e091283405d490ccAdam Lesinski            heightSize = maxChildHeight + verticalPadding;
498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
499ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung
500ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung        setMeasuredDimension(widthSize, heightSize);
501ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung
50225b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen        // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions.
50325b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen        // We also wait until we set the measured dimensions before flushing the cache as well, to
50425b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen        // ensure that the cache is filled with good values.
50525b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen        invalidateCachedOffsets();
50625b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen        updateScrollingIndicatorPosition();
50725b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen
508faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        if (childCount > 0) {
509faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen            mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
510faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        } else {
511faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen            mMaxScrollX = 0;
512faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        }
513cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    }
514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
5158c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) {
516af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
517af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        int delta = newX - mScrollX;
518af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka
5198c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        final int pageCount = getChildCount();
5208c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        for (int i = 0; i < pageCount; i++) {
52122f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            View page = (View) getPageAt(i);
5228c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            page.setX(page.getX() + delta);
523af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        }
524af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        setCurrentPage(newCurrentPage);
525af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka    }
526af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka
5278c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a
5288c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and
529d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    // tightens the layout accordingly
530d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    public void setLayoutScale(float childrenScale) {
531d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        mLayoutScale = childrenScale;
53273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        invalidateCachedOffsets();
533d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
534d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // Now we need to do a re-layout, but preserving absolute X and Y coordinates
535d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        int childCount = getChildCount();
536d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        float childrenX[] = new float[childCount];
537d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        float childrenY[] = new float[childCount];
538d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        for (int i = 0; i < childCount; i++) {
53922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            final View child = getPageAt(i);
540d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            childrenX[i] = child.getX();
541d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            childrenY[i] = child.getY();
542d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
543b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        // Trigger a full re-layout (never just call onLayout directly!)
544b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
545b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY);
546b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        requestLayout();
547b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        measure(widthSpec, heightSpec);
548b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        layout(mLeft, mTop, mRight, mBottom);
549d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        for (int i = 0; i < childCount; i++) {
55022f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            final View child = getPageAt(i);
551d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            child.setX(childrenX[i]);
552d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            child.setY(childrenY[i]);
553d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
554b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung
555d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // Also, the page offset has changed  (since the pages are now smaller);
556d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // update the page offset, but again preserving absolute X and Y coordinates
5578c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        scrollToNewPageWithoutMovingPages(mCurrentPage);
558d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    }
559d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
56060b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen    public void setPageSpacing(int pageSpacing) {
56160b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen        mPageSpacing = pageSpacing;
56260b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen        invalidateCachedOffsets();
56360b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen    }
56460b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen
565cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    @Override
56628750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
567f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        if (!mIsDataReady) {
568f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung            return;
569f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        }
570f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
571785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
5726b879f0a5885274a85333531e091283405d490ccAdam Lesinski        final int verticalPadding = mPaddingTop + mPaddingBottom;
573321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
574321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int childLeft = 0;
575321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (childCount > 0) {
576785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung            if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", "
577785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                    + getChildWidth(0));
578e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            childLeft = getRelativeChildOffset(0);
579ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung
580ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung            // Calculate the variable page spacing if necessary
581ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung            if (mPageSpacing < 0) {
58260b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen                setPageSpacing(((right - left) - getChildAt(0).getMeasuredWidth()) / 2);
583ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung            }
584321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
585321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
586321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
58722f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            final View child = getPageAt(i);
588321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (child.getVisibility() != View.GONE) {
589d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                final int childWidth = getScaledMeasuredWidth(child);
5906b879f0a5885274a85333531e091283405d490ccAdam Lesinski                final int childHeight = child.getMeasuredHeight();
5916b879f0a5885274a85333531e091283405d490ccAdam Lesinski                int childTop = mPaddingTop;
5926b879f0a5885274a85333531e091283405d490ccAdam Lesinski                if (mCenterPagesVertically) {
5936b879f0a5885274a85333531e091283405d490ccAdam Lesinski                    childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
5946b879f0a5885274a85333531e091283405d490ccAdam Lesinski                }
595d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
596785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
5976b879f0a5885274a85333531e091283405d490ccAdam Lesinski                child.layout(childLeft, childTop,
598d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                        childLeft + child.getMeasuredWidth(), childTop + childHeight);
5999c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                childLeft += childWidth + mPageSpacing;
600321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
601321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
602c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung
603c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
604c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung            setHorizontalScrollBarEnabled(false);
605c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung            int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
606c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung            scrollTo(newX, 0);
607c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung            mScroller.setFinalX(newX);
608c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung            setHorizontalScrollBarEnabled(true);
609c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung            mFirstLayout = false;
610c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung        }
611c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung
612d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
613d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            mFirstLayout = false;
614d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
61773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    protected void screenScrolled(int screenCenter) {
61873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        if (isScrollingIndicatorEnabled()) {
61973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            updateScrollingIndicator();
62073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        }
6210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mFadeInAdjacentScreens) {
62273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            for (int i = 0; i < getChildCount(); i++) {
62373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                View child = getChildAt(i);
62473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                if (child != null) {
62573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                    float scrollProgress = getScrollProgress(screenCenter, child, i);
62673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                    float alpha = 1 - Math.abs(scrollProgress);
62773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                    child.setFastAlpha(alpha);
62873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                    child.fastInvalidate();
629affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung                }
630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
63173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            invalidate();
632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
633e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
6340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
635e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    @Override
6362591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen    protected void onViewAdded(View child) {
6372591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen        super.onViewAdded(child);
6382591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen
6392591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen        // This ensures that when children are added, they get the correct transforms / alphas
6402591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen        // in accordance with any scroll effects.
6412591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen        mForceScreenScrolled = true;
6422591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen        invalidate();
64325b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen        invalidateCachedOffsets();
6442591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen    }
6452591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen
64673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    protected void invalidateCachedOffsets() {
64773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        int count = getChildCount();
64825b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen        if (count == 0) {
64925b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen            mChildOffsets = null;
65025b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen            mChildRelativeOffsets = null;
65125b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen            mChildOffsetsWithLayoutScale = null;
65225b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen            return;
65325b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen        }
65473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen
65573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        mChildOffsets = new int[count];
65673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        mChildRelativeOffsets = new int[count];
65773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        mChildOffsetsWithLayoutScale = new int[count];
65873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        for (int i = 0; i < count; i++) {
65973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            mChildOffsets[i] = -1;
66073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            mChildRelativeOffsets[i] = -1;
66173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            mChildOffsetsWithLayoutScale[i] = -1;
66273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        }
66373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    }
66473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen
66573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    protected int getChildOffset(int index) {
66673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ?
66773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                mChildOffsets : mChildOffsetsWithLayoutScale;
66873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen
66973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        if (childOffsets != null && childOffsets[index] != -1) {
67073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            return childOffsets[index];
67173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        } else {
67273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            if (getChildCount() == 0)
67373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                return 0;
67473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen
67573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            int offset = getRelativeChildOffset(0);
67673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            for (int i = 0; i < index; ++i) {
67773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing;
67873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            }
67973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            if (childOffsets != null) {
68073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                childOffsets[index] = offset;
68173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            }
68273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            return offset;
68373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        }
68473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    }
68573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen
68673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    protected int getRelativeChildOffset(int index) {
68773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) {
68873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            return mChildRelativeOffsets[index];
68973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        } else {
69073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            final int padding = mPaddingLeft + mPaddingRight;
69173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            final int offset = mPaddingLeft +
69273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                    (getMeasuredWidth() - padding - getChildWidth(index)) / 2;
69373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            if (mChildRelativeOffsets != null) {
69473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                mChildRelativeOffsets[index] = offset;
69573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            }
69673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen            return offset;
69773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        }
69873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    }
69973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen
70073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    protected int getScaledRelativeChildOffset(int index) {
70173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        final int padding = mPaddingLeft + mPaddingRight;
70273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        final int offset = mPaddingLeft + (getMeasuredWidth() - padding -
70373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen                getScaledMeasuredWidth(getPageAt(index))) / 2;
70473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        return offset;
70573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    }
70673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen
70773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    protected int getScaledMeasuredWidth(View child) {
70873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        // This functions are called enough times that it actually makes a difference in the
70973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        // profiler -- so just inline the max() here
71073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        final int measuredWidth = child.getMeasuredWidth();
71173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        final int minWidth = mMinimumWidth;
71273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth;
71373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen        return (int) (maxWidth * mLayoutScale + 0.5f);
71473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen    }
71573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen
716dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka    protected void getVisiblePages(int[] range) {
7170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int pageCount = getChildCount();
718c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka        if (pageCount > 0) {
71922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            final int pageWidth = getScaledMeasuredWidth(getPageAt(0));
720c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final int screenWidth = getMeasuredWidth();
721557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung            int x = getScaledRelativeChildOffset(0) + pageWidth;
722c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int leftScreen = 0;
723c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int rightScreen = 0;
72422f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            while (x <= mScrollX && leftScreen < pageCount - 1) {
725c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                leftScreen++;
72622f823d340710bcde587b18a1542570cbe0360f5Adam Cohen                x += getScaledMeasuredWidth(getPageAt(leftScreen)) + mPageSpacing;
727c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
728c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = leftScreen;
72922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            while (x < mScrollX + screenWidth && rightScreen < pageCount - 1) {
730c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                rightScreen++;
73122f823d340710bcde587b18a1542570cbe0360f5Adam Cohen                x += getScaledMeasuredWidth(getPageAt(rightScreen)) + mPageSpacing;
732c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
733dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            range[0] = leftScreen;
734dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            range[1] = rightScreen;
735dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka        } else {
736dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            range[0] = -1;
737dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            range[1] = -1;
738dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka        }
739dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka    }
740dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka
741dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka    @Override
742dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka    protected void dispatchDraw(Canvas canvas) {
743dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka        int halfScreenSize = getMeasuredWidth() / 2;
744ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        // mOverScrollX is equal to mScrollX when we're within the normal scroll range. Otherwise
745ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        // it is equal to the scaled overscroll position.
746ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        int screenCenter = mOverScrollX + halfScreenSize;
747dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka
748dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka        if (screenCenter != mLastScreenCenter || mForceScreenScrolled) {
749dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            screenScrolled(screenCenter);
750dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            mLastScreenCenter = screenCenter;
751dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            mForceScreenScrolled = false;
752dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka        }
753dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka
754dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka        // Find out which screens are visible; as an optimization we only call draw on them
755dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka        final int pageCount = getChildCount();
756dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka        if (pageCount > 0) {
757dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            getVisiblePages(mTempVisiblePagesRange);
758dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            final int leftScreen = mTempVisiblePagesRange[0];
759dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka            final int rightScreen = mTempVisiblePagesRange[1];
760c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung            if (leftScreen != -1 && rightScreen != -1) {
761c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                final long drawingTime = getDrawingTime();
762c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                // Clip to the bounds
763c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                canvas.save();
764c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft,
765c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                        mScrollY + mBottom - mTop);
766c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung
767c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                for (int i = rightScreen; i >= leftScreen; i--) {
768c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                    drawChild(canvas, getPageAt(i), drawingTime);
769c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                }
770c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung                canvas.restore();
771c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
7720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
777ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen        int page = indexToPage(indexOfChild(child));
77886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page != mCurrentPage || !mScroller.isFinished()) {
77986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
78786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int focusablePage;
78886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mNextPage != INVALID_PAGE) {
78986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mNextPage;
790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
79186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mCurrentPage;
792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
79386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View v = getPageAt(focusablePage);
794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
79576fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen            return v.requestFocus(direction, previouslyFocusedRect);
796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean dispatchUnhandledMove(View focused, int direction) {
802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
80386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() > 0) {
80486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() - 1);
805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT) {
80886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() < getPageCount() - 1) {
80986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() + 1);
810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return super.dispatchUnhandledMove(focused, direction);
814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
81886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
81986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(mCurrentPage).addFocusables(views, direction);
820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
82286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) {
82386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage - 1).addFocusables(views, direction);
824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT){
82686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getPageCount() - 1) {
82786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage + 1).addFocusables(views, direction);
828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
830321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
831321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
832321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * If one of our descendant views decides that it could be focused now, only
83486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * pass that along if it's on the current page.
835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
83686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This happens when live folders requery, and if they're off page, they
83786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * end up calling requestFocus, which pulls it on page.
838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void focusableViewAvailable(View focused) {
84186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View current = getPageAt(mCurrentPage);
842321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        View v = focused;
843321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        while (true) {
844321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == current) {
845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                super.focusableViewAvailable(focused);
846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == this) {
849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent parent = v.getParent();
852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (parent instanceof View) {
853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                v = (View)v.getParent();
854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else {
855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * {@inheritDoc}
862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
864321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
865321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (disallowIntercept) {
866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // We need to make sure to cancel our long press if
867321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // a scrollable widget takes over touch events
86822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            final View currentPage = getPageAt(mCurrentPage);
86986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage.cancelLongPress();
870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestDisallowInterceptTouchEvent(disallowIntercept);
872321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
873321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
874d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    /**
875d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     * Return true if a tap at (x, y) should trigger a flip to the previous page.
876d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     */
877d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    protected boolean hitsPreviousPage(float x, float y) {
878d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy        return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
879d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    }
880d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy
881d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    /**
882d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     * Return true if a tap at (x, y) should trigger a flip to the next page.
883d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     */
884d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    protected boolean hitsNextPage(float x, float y) {
885d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy        return  (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
886d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    }
887d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy
888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * This method JUST determines whether we want to intercept the motion.
892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * If we return true, onTouchEvent will be called and we do the actual
893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * scrolling there.
894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
8956342bbae1a18f3c1862953e33309481703f541cfAdam Cohen        acquireVelocityTrackerAndAddMovement(ev);
896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
89745e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
89845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
89945e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
901321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Shortcut the most recurring case: the user is in the dragging
902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * state and he is moving his finger.  We want to intercept this
903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * motion.
904321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
906321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if ((action == MotionEvent.ACTION_MOVE) &&
907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                (mTouchState == TOUCH_STATE_SCROLLING)) {
908321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
909321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
910321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
911321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
912321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_MOVE: {
913321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * whether the user has moved far enough from his original down touch.
916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
9171ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                if (mActivePointerId != INVALID_POINTER) {
9181ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    determineScrollingStart(ev);
9191ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    break;
9201ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                }
9211ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
9221ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
9231ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // i.e. fall through to the next case (don't break)
9241ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
9251ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // while it's small- this was causing a crash before we checked for INVALID_POINTER)
926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_DOWN: {
929321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX();
930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float y = ev.getY();
931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Remember location of down touch
932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mDownMotionX = x;
933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionY = y;
935c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                mLastMotionXRemainder = 0;
936aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                mTotalMotionX = 0;
937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = ev.getPointerId(0);
938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = true;
939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * If being flinged and user touches the screen, initiate drag;
942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * otherwise don't.  mScroller.isFinished should be false when
943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * being flinged.
944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
945fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka                final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
9465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
9475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                if (finishedScrolling) {
9485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_REST;
9495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mScroller.abortAnimation();
9505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                } else {
9515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_SCROLLING;
9525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
95486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                // check if this can be the beginning of a tap on the side of the pages
955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // to scroll the current page
956d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (getChildCount() > 0) {
958d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                        if (hitsPreviousPage(x, y)) {
959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_PREV_PAGE;
960d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                        } else if (hitsNextPage(x, y)) {
961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_NEXT_PAGE;
962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        }
963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
966321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
968321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_UP:
9691d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            case MotionEvent.ACTION_CANCEL:
970321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_REST;
971321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
972321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = INVALID_POINTER;
9736342bbae1a18f3c1862953e33309481703f541cfAdam Cohen                releaseVelocityTracker();
974321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
975321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
976321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_POINTER_UP:
977321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                onSecondaryPointerUp(ev);
9786342bbae1a18f3c1862953e33309481703f541cfAdam Cohen                releaseVelocityTracker();
979321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
980321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
981321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
982321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
983321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * The only time we want to intercept motion events is if we are in the
984321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * drag mode.
985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
986321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mTouchState != TOUCH_STATE_REST;
987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
98980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    protected void animateClickFeedback(View v, final Runnable r) {
99080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // animate the view slightly to show click feedback running some logic after it is "pressed"
991228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.
992228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung                loadAnimator(mContext, R.anim.paged_view_click_feedback);
993228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.setTarget(v);
994228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.addListener(new AnimatorListenerAdapter() {
995228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung            public void onAnimationRepeat(Animator animation) {
99680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                r.run();
99780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
99880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        });
999228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.start();
100080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
100180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1002f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void determineScrollingStart(MotionEvent ev) {
1003f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        determineScrollingStart(ev, 1.0f);
1004f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    }
1005f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen
1006321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /*
1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Determines if we should change the touch state to start scrolling after the
1008321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * user moves their touch point too far.
1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1010f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
1011321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
1012321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Locally do absolute value. mLastMotionX is set to the y value
1013321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * of the down event.
1014321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
10162698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka        if (pointerIndex == -1) {
10172698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka            return;
10182698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka        }
1019321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float x = ev.getX(pointerIndex);
1020321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float y = ev.getY(pointerIndex);
1021321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int xDiff = (int) Math.abs(x - mLastMotionX);
1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int yDiff = (int) Math.abs(y - mLastMotionY);
1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1024f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xPaged = xDiff > mPagingTouchSlop;
1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xMoved = xDiff > touchSlop;
1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean yMoved = yDiff > touchSlop;
1028321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1029f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        if (xMoved || xPaged || yMoved) {
10300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mUsePagingTouchSlop ? xPaged : xMoved) {
1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll if the user moved far enough along the X axis
1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_SCROLLING;
10336342bbae1a18f3c1862953e33309481703f541cfAdam Cohen                mTotalMotionX += Math.abs(mLastMotionX - x);
1034321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
1035c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                mLastMotionXRemainder = 0;
10360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mTouchX = mScrollX;
10370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
10380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
1039321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1040321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Either way, cancel any pending longpress
1041f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            cancelCurrentPageLongPress();
1042f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        }
1043f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    }
1044f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen
1045f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void cancelCurrentPageLongPress() {
1046f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        if (mAllowLongPress) {
1047f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            mAllowLongPress = false;
1048f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // Try canceling the long press. It could also have been scheduled
1049f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // by a distant descendant, so use the mAllowLongPress flag to block
1050f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // everything
1051f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            final View currentPage = getPageAt(mCurrentPage);
1052f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            if (currentPage != null) {
1053f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen                currentPage.cancelLongPress();
1054321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1055321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1056321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1058b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    protected float getScrollProgress(int screenCenter, View v, int page) {
1059b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        final int halfScreenSize = getMeasuredWidth() / 2;
1060b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1061b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing;
1062b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        int delta = screenCenter - (getChildOffset(page) -
1063b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen                getRelativeChildOffset(page) + halfScreenSize);
1064b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1065b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        float scrollProgress = delta / (totalDistance * 1.0f);
1066b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        scrollProgress = Math.min(scrollProgress, 1.0f);
1067b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        scrollProgress = Math.max(scrollProgress, -1.0f);
1068b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        return scrollProgress;
1069b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
1070b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1071e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // This curve determines how the effect of scrolling over the limits of the page dimishes
1072e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // as the user pulls further and further from the bounds
1073e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private float overScrollInfluenceCurve(float f) {
1074e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f -= 1.0f;
1075e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        return f * f * f + 1.0f;
1076e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1077e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1078b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    protected void acceleratedOverScroll(float amount) {
1079b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        int screenSize = getMeasuredWidth();
1080b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1081b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        // We want to reach the max over scroll effect when the user has
1082b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        // over scrolled half the size of the screen
1083b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
1084b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1085b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (f == 0) return;
1086b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1087b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        // Clamp this factor, f, to -1 < f < 1
1088b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (Math.abs(f) >= 1) {
1089b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            f /= Math.abs(f);
1090b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        }
1091b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1092b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        int overScrollAmount = (int) Math.round(f * screenSize);
1093b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (amount < 0) {
1094ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mOverScrollX = overScrollAmount;
1095ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mScrollX = 0;
1096b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        } else {
1097ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mOverScrollX = mMaxScrollX + overScrollAmount;
1098ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mScrollX = mMaxScrollX;
1099b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        }
1100b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        invalidate();
1101b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
1102b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1103b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    protected void dampedOverScroll(float amount) {
1104e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int screenSize = getMeasuredWidth();
1105e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1106e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float f = (amount / screenSize);
1107e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1108e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        if (f == 0) return;
1109e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
1110e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
11117bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        // Clamp this factor, f, to -1 < f < 1
11127bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        if (Math.abs(f) >= 1) {
11137bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen            f /= Math.abs(f);
11147bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        }
11157bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen
1116e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
111768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        if (amount < 0) {
1118ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mOverScrollX = overScrollAmount;
1119ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mScrollX = 0;
112068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else {
1121ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mOverScrollX = mMaxScrollX + overScrollAmount;
1122ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            mScrollX = mMaxScrollX;
112368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        }
112468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        invalidate();
112568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    }
112668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
1127b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    protected void overScroll(float amount) {
1128b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        dampedOverScroll(amount);
1129b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
1130b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
1131c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka    protected float maxOverScroll() {
1132c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
1133b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        // exceed). Used to find out how much extra wallpaper we need for the over scroll effect
1134c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        float f = 1.0f;
1135c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
1136c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        return OVERSCROLL_DAMP_FACTOR * f;
1137c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka    }
1138c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka
1139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
1140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onTouchEvent(MotionEvent ev) {
114145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
114245e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onTouchEvent(ev);
114345e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
1144b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        acquireVelocityTrackerAndAddMovement(ev);
1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
1147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_DOWN:
1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            /*
1151321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * If being flinged and user touches, stop the fling. isFinished
1152321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * will be false if being flinged.
1153321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             */
1154321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (!mScroller.isFinished()) {
1155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mScroller.abortAnimation();
1156321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Remember where the motion event started
1159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mDownMotionX = mLastMotionX = ev.getX();
1160c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung            mLastMotionXRemainder = 0;
1161aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen            mTotalMotionX = 0;
1162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(0);
11630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
11640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
11650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
1166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_MOVE:
1169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
1170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll to follow the motion event
1171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
1172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
1173c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                final float deltaX = mLastMotionX + mLastMotionXRemainder - x;
1174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1175aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                mTotalMotionX += Math.abs(deltaX);
1176aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1177c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // Only scroll and update mLastMotionX if we have moved some discrete amount.  We
1178c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // keep the remainder because we are actually testing if we've moved from the last
1179c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // scrolled position (which is discrete).
1180c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                if (Math.abs(deltaX) >= 1.0f) {
118168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    mTouchX += deltaX;
118268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
118368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    if (!mDeferScrollUpdate) {
1184c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                        scrollBy((int) deltaX, 0);
1185785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                        if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX);
118668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    } else {
118768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                        invalidate();
1188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
1189c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                    mLastMotionX = x;
1190c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                    mLastMotionXRemainder = deltaX - (int) deltaX;
1191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    awakenScrollBars();
1193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1194564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen            } else {
1195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                determineScrollingStart(ev);
1196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1197321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1199321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_UP:
1200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
1201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int activePointerId = mActivePointerId;
1202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(activePointerId);
1203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
1204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final VelocityTracker velocityTracker = mVelocityTracker;
1205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
12079cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                final int deltaX = (int) (x - mDownMotionX);
120800481b3ed322b4f36b669201d2287964febda65fAdam Cohen                final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage));
120900481b3ed322b4f36b669201d2287964febda65fAdam Cohen                boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
121000481b3ed322b4f36b669201d2287964febda65fAdam Cohen                        SIGNIFICANT_MOVE_THRESHOLD;
12110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int snapVelocity = mSnapVelocity;
1212aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1213b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
1214b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen
121500481b3ed322b4f36b669201d2287964febda65fAdam Cohen                boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
121600481b3ed322b4f36b669201d2287964febda65fAdam Cohen                        Math.abs(velocityX) > snapVelocity;
121700481b3ed322b4f36b669201d2287964febda65fAdam Cohen
1218aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // In the case that the page is moved far to one direction and then is flung
1219aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // in the opposite direction, we use a threshold to determine whether we should
1220aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // just return to the starting page, or if we should skip one further.
1221aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                boolean returnToOriginalPage = false;
1222b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
122300481b3ed322b4f36b669201d2287964febda65fAdam Cohen                        Math.signum(velocityX) != Math.signum(deltaX) && isFling) {
1224aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    returnToOriginalPage = true;
1225aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                }
1226aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1227aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                int finalPage;
1228aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // We give flings precedence over large moves, which is why we short-circuit our
1229aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // test for a large move if a fling has been registered. That is, a large
1230aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // move to the left and fling to the right will register as a fling to the right.
1231aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                if (((isSignificantMove && deltaX > 0 && !isFling) ||
1232aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        (isFling && velocityX > 0)) && mCurrentPage > 0) {
1233aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
1234aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    snapToPageWithVelocity(finalPage, velocityX);
1235aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
1236aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        (isFling && velocityX < 0)) &&
123786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                        mCurrentPage < getChildCount() - 1) {
1238aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
1239aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    snapToPageWithVelocity(finalPage, velocityX);
1240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1243d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
1244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
1245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
1246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
124786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.max(0, mCurrentPage - 1);
124886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
124986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
1250321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1253d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
1254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
1255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
1256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
125786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
125886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
125986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
12631d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            } else {
1264d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka                onUnhandledTap(ev);
1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
1267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
1268b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
1269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1271321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_CANCEL:
1272b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
1273b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka                snapToDestination();
1274b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            }
1275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
1276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
1277b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
1278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_POINTER_UP:
1281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            onSecondaryPointerUp(ev);
1282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return true;
1286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1288185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung    @Override
1289185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung    public boolean onGenericMotionEvent(MotionEvent event) {
1290185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1291185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung            switch (event.getAction()) {
1292185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                case MotionEvent.ACTION_SCROLL: {
1293185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    // Handle mouse (or ext. device) by shifting the page depending on the scroll
1294185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    final float vscroll;
1295185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    final float hscroll;
1296185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
1297185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        vscroll = 0;
1298185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1299185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    } else {
1300185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1301185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
1302185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    }
1303185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    if (hscroll != 0 || vscroll != 0) {
1304185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        if (hscroll > 0 || vscroll > 0) {
1305185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                            scrollRight();
1306185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        } else {
1307185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                            scrollLeft();
1308185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        }
1309185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        return true;
1310185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    }
1311185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                }
1312185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung            }
1313185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung        }
1314185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung        return super.onGenericMotionEvent(event);
1315185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung    }
1316185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung
1317b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
1318b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker == null) {
1319b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = VelocityTracker.obtain();
1320b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
1321b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        mVelocityTracker.addMovement(ev);
1322b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
1323b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
1324b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void releaseVelocityTracker() {
1325b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker != null) {
1326b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker.recycle();
1327b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = null;
1328b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
1329b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
1330b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
1331321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void onSecondaryPointerUp(MotionEvent ev) {
1332321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1333321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1334321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerId = ev.getPointerId(pointerIndex);
1335321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (pointerId == mActivePointerId) {
1336321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // This was our active pointer going up. Choose a new
1337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // active pointer and adjust accordingly.
1338321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // TODO: Make this decision more intelligent.
1339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
1341321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionY = ev.getY(newPointerIndex);
1342c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung            mLastMotionXRemainder = 0;
1343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(newPointerIndex);
1344321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mVelocityTracker != null) {
1345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mVelocityTracker.clear();
1346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
13481d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown    }
13491d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown
1350d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    protected void onUnhandledTap(MotionEvent ev) {}
1351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1352321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
1353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestChildFocus(View child, View focused) {
1354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestChildFocus(child, focused);
1355ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen        int page = indexToPage(indexOfChild(child));
135697d85d23b013347bead4e2f5fa430a79ce69431eWinson Chung        if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
135786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
1358321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1359321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1361e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getChildIndexForRelativeOffset(int relativeOffset) {
1362e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int childCount = getChildCount();
13639c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int left;
13649c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int right;
1365e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        for (int i = 0; i < childCount; ++i) {
13669c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            left = getRelativeChildOffset(i);
136722f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            right = (left + getScaledMeasuredWidth(getPageAt(i)));
1368e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            if (left <= relativeOffset && relativeOffset <= right) {
1369e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                return i;
1370e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            }
1371e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        }
1372e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return -1;
1373e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1374e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
13751908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung    protected int getChildWidth(int index) {
137663257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        // This functions are called enough times that it actually makes a difference in the
137763257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        // profiler -- so just inline the max() here
137822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen        final int measuredWidth = getPageAt(index).getMeasuredWidth();
137963257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        final int minWidth = mMinimumWidth;
138063257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        return (minWidth > measuredWidth) ? minWidth : measuredWidth;
13811908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung    }
13821908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung
1383d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    int getPageNearestToCenterOfScreen() {
138422f823d340710bcde587b18a1542570cbe0360f5Adam Cohen        int minDistanceFromScreenCenter = Integer.MAX_VALUE;
1385321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenterIndex = -1;
1386321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int screenCenter = mScrollX + (getMeasuredWidth() / 2);
1387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
1388321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; ++i) {
138922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            View layout = (View) getPageAt(i);
1390d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            int childWidth = getScaledMeasuredWidth(layout);
1391321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int halfChildWidth = (childWidth / 2);
1392321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childCenter = getChildOffset(i) + halfChildWidth;
1393321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
1394321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
1395321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenter = distanceFromScreenCenter;
1396321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenterIndex = i;
1397321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1399d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        return minDistanceFromScreenCenterIndex;
1400d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    }
1401d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen
1402d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    protected void snapToDestination() {
1403d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
1404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1405321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1406e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static class ScrollInterpolator implements Interpolator {
1407e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        public ScrollInterpolator() {
1408e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1409e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1410e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        public float getInterpolation(float t) {
1411e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            t -= 1.0f;
1412e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            return t*t*t*t*t + 1;
1413e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1414e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1415e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1416e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // We want the duration of the page snap animation to be influenced by the distance that
1417e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // the screen has to travel, however, we don't want this duration to be effected in a
1418e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
1419e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // of travel has on the overall snap duration.
1420e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    float distanceInfluenceForSnapDuration(float f) {
1421e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f -= 0.5f; // center the values about 0.
1422e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f *= 0.3f * Math.PI / 2.0f;
1423e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        return (float) Math.sin(f);
1424e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1425e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
14260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPageWithVelocity(int whichPage, int velocity) {
1427e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
1428e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int halfScreenSize = getMeasuredWidth() / 2;
1429e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1430785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
1431785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
1432785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                + getMeasuredWidth() + ", " + getChildWidth(whichPage));
1433e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
1434e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int delta = newX - mUnboundedScrollX;
1435e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int duration = 0;
1436e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1437e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        if (Math.abs(velocity) < MIN_FLING_VELOCITY) {
1438e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            // If the velocity is low enough, then treat this more as an automatic page advance
1439e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            // as opposed to an apparent physical response to flinging
1440e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1441e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            return;
1442e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1443e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1444e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // Here we compute a "distance" that will be used in the computation of the overall
1445e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // snap duration. This is a function of the actual distance that needs to be traveled;
1446e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // we keep this value close to half screen size in order to reduce the variance in snap
1447e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // duration as a function of the distance the page needs to travel.
144820b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka        float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize));
1449e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float distance = halfScreenSize + halfScreenSize *
1450e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen                distanceInfluenceForSnapDuration(distanceRatio);
1451e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1452e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        velocity = Math.abs(velocity);
1453e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
1454e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1455e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // we want the page's snap velocity to approximately match the velocity at which the
1456e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // user flings, so we scale the duration by a value near to the derivative of the scroll
145720b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka        // interpolator at zero, ie. 5. We use 4 to make it a little slower.
145820b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka        duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
1459e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1460e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        snapToPage(whichPage, delta, duration);
14610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
14620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
14630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage) {
14645f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
14670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int duration) {
146886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
1469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1470785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
1471785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", "
1472785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                + getChildWidth(whichPage));
147386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
147468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        final int sX = mUnboundedScrollX;
1475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int delta = newX - sX;
14760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage, delta, duration);
14770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
14780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
14790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int delta, int duration) {
14800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mNextPage = whichPage;
14810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
14820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        View focusedChild = getFocusedChild();
14830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (focusedChild != null && whichPage != mCurrentPage &&
148422f823d340710bcde587b18a1542570cbe0360f5Adam Cohen                focusedChild == getPageAt(mCurrentPage)) {
14850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            focusedChild.clearFocus();
14860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
14870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
14880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        pageBeginMoving();
1489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        awakenScrollBars(duration);
1490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (duration == 0) {
1491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            duration = Math.abs(delta);
1492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1494321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
149568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
149680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1497b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        // Load associated pages immediately if someone else is handling the scroll, otherwise defer
1498b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        // loading associated pages until the scroll settles
1499b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        if (mDeferScrollUpdate) {
1500b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung            loadAssociatedPages(mNextPage);
1501b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        } else {
15024e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung            mDeferLoadAssociatedPagesUntilScrollCompletes = true;
1503b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        }
15040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        notifyPageSwitchListener();
1505321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
1506321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1507321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1508321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollLeft() {
1509321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
151086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
1511321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
151286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage > 0) snapToPage(mNextPage - 1);
1513321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1515321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1516321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollRight() {
1517321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
151886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
1519321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
152086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
1521321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1522321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1523321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
152486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public int getPageForView(View v) {
1525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int result = -1;
1526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
1527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent vp = v.getParent();
1528321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int count = getChildCount();
1529321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            for (int i = 0; i < count; i++) {
153022f823d340710bcde587b18a1542570cbe0360f5Adam Cohen                if (vp == getPageAt(i)) {
1531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    return i;
1532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return result;
1536321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1537321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1538321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
1539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @return True is long presses are still allowed for the current touch
1540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean allowLongPress() {
1542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mAllowLongPress;
1543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
15450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
15460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * Set true to allow long-press events to be triggered, usually checked by
15470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * {@link Launcher} to accept or block dpad-initiated long-presses.
15480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     */
15490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setAllowLongPress(boolean allowLongPress) {
15500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mAllowLongPress = allowLongPress;
15510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
15520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public static class SavedState extends BaseSavedState {
155486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int currentPage = -1;
1555321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1556321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState(Parcelable superState) {
1557321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(superState);
1558321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1559321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1560321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        private SavedState(Parcel in) {
1561321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(in);
156286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage = in.readInt();
1563321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        @Override
1566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public void writeToParcel(Parcel out, int flags) {
1567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super.writeToParcel(out, flags);
156886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            out.writeInt(currentPage);
1569321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1570321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public static final Parcelable.Creator<SavedState> CREATOR =
1572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                new Parcelable.Creator<SavedState>() {
1573321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState createFromParcel(Parcel in) {
1574321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState(in);
1575321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1576321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1577321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState[] newArray(int size) {
1578321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState[size];
1579321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1580321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        };
1581321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1582321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1583f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung    protected void loadAssociatedPages(int page) {
1584f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung        loadAssociatedPages(page, false);
1585f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung    }
1586f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung    protected void loadAssociatedPages(int page, boolean immediateAndOnly) {
15870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
15880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
15890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (page < count) {
1590e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int lowerPageBound = getAssociatedLowerPageBound(page);
1591e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int upperPageBound = getAssociatedUpperPageBound(page);
1592785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/"
1593785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                        + upperPageBound);
15940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < count; ++i) {
1595f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung                    if ((i != page) && immediateAndOnly) {
1596f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung                        continue;
1597f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung                    }
159822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen                    Page layout = (Page) getPageAt(i);
15998245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                    final int childCount = layout.getPageChildCount();
16000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (lowerPageBound <= i && i <= upperPageBound) {
16010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (mDirtyPageContent.get(i)) {
1602f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung                            syncPageItems(i, (i == page) && immediateAndOnly);
16030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            mDirtyPageContent.set(i, false);
16040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
16050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
16060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (childCount > 0) {
16078245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                            layout.removeAllViewsOnPage();
16080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
16090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mDirtyPageContent.set(i, true);
161080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                    }
161180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                }
161280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
161380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        }
161480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
161580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1616e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedLowerPageBound(int page) {
1617e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.max(0, page - 1);
1618e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1619e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedUpperPageBound(int page) {
1620e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int count = getChildCount();
1621e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.min(page + 1, count - 1);
1622e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1623e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
16245f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void startChoiceMode(int mode, ActionMode.Callback callback) {
1625430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        if (isChoiceMode(CHOICE_MODE_NONE)) {
1626430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mChoiceMode = mode;
1627430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mActionMode = startActionMode(callback);
1628430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        }
16295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
16305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
16312b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public void endChoiceMode() {
16325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        if (!isChoiceMode(CHOICE_MODE_NONE)) {
16335f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            mChoiceMode = CHOICE_MODE_NONE;
16345f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            resetCheckedGrandchildren();
1635e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka            if (mActionMode != null) mActionMode.finish();
16369f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            mActionMode = null;
16375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
16385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
16395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
16405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected boolean isChoiceMode(int mode) {
16415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return mChoiceMode == mode;
16425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
16435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
16445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected ArrayList<Checkable> getCheckedGrandchildren() {
16455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        ArrayList<Checkable> checked = new ArrayList<Checkable>();
16465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final int childCount = getChildCount();
16475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < childCount; ++i) {
164822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen            Page layout = (Page) getPageAt(i);
16498245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka            final int grandChildCount = layout.getPageChildCount();
16505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            for (int j = 0; j < grandChildCount; ++j) {
16518245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                final View v = layout.getChildOnPageAt(j);
16529f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                if (v instanceof Checkable && ((Checkable) v).isChecked()) {
16535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    checked.add((Checkable) v);
16545f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
16555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            }
16565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
16575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return checked;
16585f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
16595f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
16609f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    /**
16619f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * If in CHOICE_MODE_SINGLE and an item is checked, returns that item.
16629f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * Otherwise, returns null.
16639f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     */
16649f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    protected Checkable getSingleCheckedGrandchild() {
16656f13342ffd3f968de9ff86b988621cc91d94adffPatrick Dubroy        if (mChoiceMode != CHOICE_MODE_MULTIPLE) {
16669f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            final int childCount = getChildCount();
16679f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            for (int i = 0; i < childCount; ++i) {
166822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen                Page layout = (Page) getPageAt(i);
16698245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                final int grandChildCount = layout.getPageChildCount();
16709f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                for (int j = 0; j < grandChildCount; ++j) {
16718245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                    final View v = layout.getChildOnPageAt(j);
16729f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    if (v instanceof Checkable && ((Checkable) v).isChecked()) {
16739f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                        return (Checkable) v;
16749f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    }
16759f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                }
16769f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            }
16779f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        }
16789f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        return null;
16799f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    }
16809f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
16815f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void resetCheckedGrandchildren() {
16825f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        // loop through children, and set all of their children to _not_ be checked
16835f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final ArrayList<Checkable> checked = getCheckedGrandchildren();
16845f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < checked.size(); ++i) {
16855f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final Checkable c = checked.get(i);
16865f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            c.setChecked(false);
16875f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
16885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
16895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
169086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
169186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called ONLY to synchronize the number of pages that the paged view has.
169286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * To actually fill the pages with information, implement syncPageItems() below.  It is
169386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * guaranteed that syncPageItems() will be called for a particular page before it is shown,
169486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * and therefore, individual page items do not need to be updated in this method.
169586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPages();
169786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
169886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
169986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called to synchronize the items that are on a particular page.  If views on
170086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * the page can be reused, then they should be updated within this method.
170186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1702f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung    public abstract void syncPageItems(int page, boolean immediate);
170386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
1704244d74cb353f1260c4d633e719bf84bb3b6e52bcPatrick Dubroy    protected void invalidatePageData() {
1705f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung        invalidatePageData(-1, false);
17065a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung    }
17075a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung    protected void invalidatePageData(int currentPage) {
1708f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung        invalidatePageData(currentPage, false);
1709f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung    }
1710f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung    protected void invalidatePageData(int currentPage, boolean immediateAndOnly) {
1711f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        if (!mIsDataReady) {
1712f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung            return;
1713f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        }
1714f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
17150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
17160cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen            // Force all scrolling-related behavior to end
17170cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen            mScroller.forceFinished(true);
17180cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen            mNextPage = INVALID_PAGE;
17190cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen
17200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Update all the pages
17210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            syncPages();
172286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
17235a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            // We must force a measure after we've loaded the pages to update the content width and
17245a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            // to determine the full scroll width
17255a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
17265a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
17275a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung
17285a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            // Set a new page as the current page if necessary
17295a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            if (currentPage > -1) {
17305afbf7bdd6f6879124c9b8283e005fe57f310d02Winson Chung                setCurrentPage(Math.min(getPageCount() - 1, currentPage));
17315a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            }
17325a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung
17330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Mark each of the pages as dirty
17340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
17350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageContent.clear();
17360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            for (int i = 0; i < count; ++i) {
17370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageContent.add(true);
17380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
173986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
17400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Load any pages that are necessary for the current window of views
1741f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung            loadAssociatedPages(mCurrentPage, immediateAndOnly);
17420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            requestLayout();
17430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
1744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1745007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
174621b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen    protected ImageView getScrollingIndicator() {
1747007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        // We use mHasScrollIndicator to prevent future lookups if there is no sibling indicator
1748007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        // found
1749007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (mHasScrollIndicator && mScrollIndicator == null) {
1750007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            ViewGroup parent = (ViewGroup) getParent();
1751007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            mScrollIndicator = (ImageView) (parent.findViewById(R.id.paged_view_indicator));
1752007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            mHasScrollIndicator = mScrollIndicator != null;
1753007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            if (mHasScrollIndicator) {
1754007c69867d821ea2b271398577a8b3440b3a7046Winson Chung                mScrollIndicator.setVisibility(View.VISIBLE);
1755007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            }
1756007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        }
1757007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        return mScrollIndicator;
1758007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1759007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1760007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    protected boolean isScrollingIndicatorEnabled() {
1761649723cfb3d73af16dd02462725700897ca60e38Winson Chung        return !LauncherApplication.isScreenLarge();
1762007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1763007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
17645a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung    Runnable hideScrollingIndicatorRunnable = new Runnable() {
17655a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        @Override
17665a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        public void run() {
17675a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            hideScrollingIndicator(false);
17685a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        }
17695a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung    };
1770b737ee6ab2dc4938a1b2a574c336af0eab826712Michael Jurka    protected void flashScrollingIndicator(boolean animated) {
17715a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        removeCallbacks(hideScrollingIndicatorRunnable);
1772b737ee6ab2dc4938a1b2a574c336af0eab826712Michael Jurka        showScrollingIndicator(!animated);
17735a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration);
17743ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    }
17753ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung
1776430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka    protected void showScrollingIndicator(boolean immediately) {
1777007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (getChildCount() <= 1) return;
1778007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (!isScrollingIndicatorEnabled()) return;
1779007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1780007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        getScrollingIndicator();
1781007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (mScrollIndicator != null) {
1782007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            // Fade the indicator in
1783007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            updateScrollingIndicatorPosition();
178432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            mScrollIndicator.setVisibility(View.VISIBLE);
178521b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen            cancelScrollingIndicatorAnimations();
1786430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka            if (immediately) {
1787430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka                mScrollIndicator.setAlpha(1f);
1788430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka            } else {
1789430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka                mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f);
1790430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka                mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration);
1791430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka                mScrollIndicatorAnimator.start();
1792430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka            }
1793007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        }
1794007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1795007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
179621b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen    protected void cancelScrollingIndicatorAnimations() {
179721b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen        if (mScrollIndicatorAnimator != null) {
179821b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen            mScrollIndicatorAnimator.cancel();
179921b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen        }
180021b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen    }
180121b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen
1802007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    protected void hideScrollingIndicator(boolean immediately) {
1803007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (getChildCount() <= 1) return;
1804007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (!isScrollingIndicatorEnabled()) return;
1805007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1806007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        getScrollingIndicator();
1807007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (mScrollIndicator != null) {
1808007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            // Fade the indicator out
1809007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            updateScrollingIndicatorPosition();
181021b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen            cancelScrollingIndicatorAnimations();
181132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            if (immediately) {
181281efbad05e0ce69b06d7323749ef2e200b84b98eMichael Jurka                mScrollIndicator.setVisibility(View.INVISIBLE);
181332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.setAlpha(0f);
181432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            } else {
181532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f);
181632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration);
181732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
181832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    private boolean cancelled = false;
181932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    @Override
182032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    public void onAnimationCancel(android.animation.Animator animation) {
182132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                        cancelled = true;
182232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    }
182332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    @Override
182432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    public void onAnimationEnd(Animator animation) {
182532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                        if (!cancelled) {
182681efbad05e0ce69b06d7323749ef2e200b84b98eMichael Jurka                            mScrollIndicator.setVisibility(View.INVISIBLE);
182732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                        }
182832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    }
182932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                });
183032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator.start();
183132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            }
1832007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        }
1833007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1834007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
183532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung    /**
183632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung     * To be overridden by subclasses to determine whether the scroll indicator should stretch to
183732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung     * fill its space on the track or not.
183832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung     */
183932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung    protected boolean hasElasticScrollIndicator() {
1840dea74b7d12b0fcd50bfdb4274f9867ba76d75238Winson Chung        return true;
184132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung    }
184232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung
1843007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private void updateScrollingIndicator() {
1844007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (getChildCount() <= 1) return;
1845007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (!isScrollingIndicatorEnabled()) return;
1846007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1847007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        getScrollingIndicator();
1848007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (mScrollIndicator != null) {
1849007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            updateScrollingIndicatorPosition();
1850007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        }
1851007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1852007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1853007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private void updateScrollingIndicatorPosition() {
1854649723cfb3d73af16dd02462725700897ca60e38Winson Chung        if (!isScrollingIndicatorEnabled()) return;
1855430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka        if (mScrollIndicator == null) return;
185632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int numPages = getChildCount();
185732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int pageWidth = getMeasuredWidth();
1858ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung        int lastChildIndex = Math.max(0, getChildCount() - 1);
1859ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung        int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
1860f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung        int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
186132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
186232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();
186332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung
1864ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung        float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX));
186532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int indicatorSpace = trackWidth / numPages;
1866ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung        int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft;
186732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        if (hasElasticScrollIndicator()) {
186832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) {
186932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.getLayoutParams().width = indicatorSpace;
187032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.requestLayout();
187132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            }
187232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        } else {
187332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2;
187432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            indicatorPos += indicatorCenterOffset;
187532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        }
1876007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        mScrollIndicator.setTranslationX(indicatorPos);
18773ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung        mScrollIndicator.invalidate();
18783ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    }
18793ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung
18803ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    public void showScrollIndicatorTrack() {
18813ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    }
18823ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung
18833ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    public void hideScrollIndicatorTrack() {
1884007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
18856a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
18866a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    /* Accessibility */
18876a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    @Override
18886a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
18896a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        super.onInitializeAccessibilityNodeInfo(info);
18906a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        info.setScrollable(true);
18916a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    }
18926a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
18936a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    @Override
18946a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
18956a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        super.onInitializeAccessibilityEvent(event);
18966a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        event.setScrollable(true);
18976a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
18986a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung            event.setFromIndex(mCurrentPage);
18996a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung            event.setToIndex(mCurrentPage);
19006a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung            event.setItemCount(getChildCount());
19016a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        }
19026a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    }
19036a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
19046a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    protected String getCurrentPageDescription() {
19056a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
19066a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        return String.format(mContext.getString(R.string.default_scroll_format),
19076a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung                page + 1, getChildCount());
19086a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    }
1909d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung
1910d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung    @Override
1911d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung    public boolean onHoverEvent(android.view.MotionEvent event) {
1912d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung        return true;
1913d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung    }
1914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung}
1915