PagedView.java revision a6427b15c18d5b8f3078f553d78f8432de9f46e9
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;
426a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityNodeInfo;
43e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator;
445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable;
45007c69867d821ea2b271398577a8b3440b3a7046Winson Chungimport android.widget.ImageView;
46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller;
47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
480499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R;
4980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
506a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport java.util.ArrayList;
516a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/**
53321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a
540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages"
55321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
56321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup {
57321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final String TAG = "PagedView";
58785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung    private static final boolean DEBUG = false;
590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final int INVALID_PAGE = -1;
60321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
6186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    // the min drag distance for a fling to register, to prevent random page shifts
629cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_FLING = 25;
639cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    // The min drag distance to trigger a page shift (regardless of velocity)
649cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_MOVE = 200;
65321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
66e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int PAGE_SNAP_ANIMATION_DURATION = 550;
670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final float NANOTIME_DIV = 1000000000.0f;
680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
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;
7368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // the velocity at which a fling gesture will cause us to snap to the next page
750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mSnapVelocity = 500;
760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mSmoothingTime;
780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mTouchX;
79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFirstLayout = true;
810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mCurrentPage;
830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mNextPage = INVALID_PAGE;
8468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected int mMaxScrollX;
850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected Scroller mScroller;
86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private VelocityTracker mVelocityTracker;
87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
88321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mDownMotionX;
897426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected float mLastMotionX;
90c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung    protected float mLastMotionXRemainder;
917426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected float mLastMotionY;
92aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen    protected float mTotalMotionX;
93f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    private int mLastScreenCenter = -1;
94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_REST = 0;
960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_SCROLLING = 1;
970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_PREV_PAGE = 2;
980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
99e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen    protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mTouchState = TOUCH_STATE_REST;
102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected OnLongClickListener mLongClickListener;
104321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1057426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected boolean mAllowLongPress = true;
106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1077426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected int mTouchSlop;
108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mPagingTouchSlop;
109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mMaximumVelocity;
1101908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung    private int mMinimumWidth;
1119c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageSpacing;
1129c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingTop;
1139c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingBottom;
1149c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingLeft;
1159c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingRight;
116df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutWidthGap;
117df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutHeightGap;
11887b1490c5a100619648b251cb2be05c457bede08Michael Jurka    protected int mCellCountX = 0;
11987b1490c5a100619648b251cb2be05c457bede08Michael Jurka    protected int mCellCountY = 0;
1207da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung    protected boolean mCenterPagesVertically;
12168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected boolean mAllowOverScroll = true;
12268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected int mUnboundedScrollX;
123321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1248c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // parameter that adjusts the layout to be optimized for pages with that scale factor
125d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    protected float mLayoutScale = 1.0f;
126d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
1275f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected static final int INVALID_POINTER = -1;
128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1295f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected int mActivePointerId = INVALID_POINTER;
130321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
13186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private PageSwitchListener mPageSwitchListener;
132321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
13386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private ArrayList<Boolean> mDirtyPageContent;
13486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private boolean mDirtyPageAlpha;
135321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // choice modes
1375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_NONE = 0;
1385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_SINGLE = 1;
1395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // Multiple selection mode is not supported by all Launcher actions atm
1405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_MULTIPLE = 2;
1419f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
142e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka    protected int mChoiceMode;
1435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private ActionMode mActionMode;
1445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, syncPages and syncPageItems will be called to refresh pages
1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mContentIsRefreshable = true;
1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, modify alpha of neighboring pages as user scrolls left/right
1490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFadeInAdjacentScreens = true;
1500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // to switch to a new page
1530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mUsePagingTouchSlop = true;
1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, the subclass should directly update mScrollX itself in its computeScroll method
1560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // (SmoothPagedView does this)
1570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mDeferScrollUpdate = false;
1580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1591262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected boolean mIsPageMoving = false;
1601262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
161f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    // All syncs and layout passes are deferred until data is ready.
162f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    protected boolean mIsDataReady = false;
163f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
164007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    // Scrolling indicator
165bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chung    private ValueAnimator mScrollIndicatorAnimator;
166007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private ImageView mScrollIndicator;
167f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung    private int mScrollIndicatorPaddingLeft;
168f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung    private int mScrollIndicatorPaddingRight;
169007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private boolean mHasScrollIndicator = true;
170a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung    protected static final int sScrollIndicatorFadeInDuration = 150;
171a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung    protected static final int sScrollIndicatorFadeOutDuration = 650;
172a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung    protected static final int sScrollIndicatorFlashDuration = 650;
173007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
174b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung    // If set, will defer loading associated pages until the scrolling settles
1754e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung    private boolean mDeferLoadAssociatedPagesUntilScrollCompletes;
176b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung
17786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public interface PageSwitchListener {
17886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        void onPageSwitch(View newPage, int newPageIndex);
179321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context) {
182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, null);
183321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
185321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs) {
186321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, attrs, 0);
187321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
189321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs, int defStyle) {
190321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super(context, attrs, defStyle);
1915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        mChoiceMode = CHOICE_MODE_NONE;
192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1939c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
1949c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView, defStyle, 0);
1959c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0);
1969c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingTop = a.getDimensionPixelSize(
1971908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung                R.styleable.PagedView_pageLayoutPaddingTop, 0);
1989c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingBottom = a.getDimensionPixelSize(
1991908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung                R.styleable.PagedView_pageLayoutPaddingBottom, 0);
2009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingLeft = a.getDimensionPixelSize(
2011908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung                R.styleable.PagedView_pageLayoutPaddingLeft, 0);
2029c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingRight = a.getDimensionPixelSize(
2031908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung                R.styleable.PagedView_pageLayoutPaddingRight, 0);
204df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutWidthGap = a.getDimensionPixelSize(
205df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung                R.styleable.PagedView_pageLayoutWidthGap, -1);
206df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutHeightGap = a.getDimensionPixelSize(
207df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung                R.styleable.PagedView_pageLayoutHeightGap, -1);
208f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung        mScrollIndicatorPaddingLeft =
209f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung            a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
210f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung        mScrollIndicatorPaddingRight =
211f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung            a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
2129c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        a.recycle();
2139c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen
214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setHapticFeedbackEnabled(false);
2150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        init();
216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Initializes various states for this workspace.
220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
2210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void init() {
22286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent = new ArrayList<Boolean>();
22386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent.ensureCapacity(32);
224e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        mScroller = new Scroller(getContext(), new ScrollInterpolator());
22586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = 0;
2267da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung        mCenterPagesVertically = true;
227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mTouchSlop = configuration.getScaledTouchSlop();
230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
23486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
23586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mPageSwitchListener = pageSwitchListener;
23686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
23786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
242f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung     * Called by subclasses to mark that data is ready, and that we can begin loading and laying
243f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung     * out pages.
244f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung     */
245f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    protected void setDataIsReady() {
246f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        mIsDataReady = true;
247f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    }
248f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    protected boolean isDataReady() {
249f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        return mIsDataReady;
250f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    }
251f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
252f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    /**
25386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Returns the index of the currently displayed page.
254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
25586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * @return The index of the currently displayed page.
256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
25786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getCurrentPage() {
25886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        return mCurrentPage;
259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
26186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getPageCount() {
262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildCount();
263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
26586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    View getPageAt(int index) {
266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildAt(index);
267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
270bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * Updates the scroll of the current page immediately to its final scroll position.  We use this
271bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
272bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * the previous tab page.
273bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     */
274bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    protected void updateCurrentPageScroll() {
275bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
276bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        scrollTo(newX, 0);
277bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        mScroller.setFinalX(newX);
278bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    }
279bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung
280bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    /**
28186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Sets the current page.
282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
28386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    void setCurrentPage(int currentPage) {
28472e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        if (!mScroller.isFinished()) {
28572e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            mScroller.abortAnimation();
28672e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
287d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
288d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // the default
289d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        if (getChildCount() == 0) {
29072e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            return;
29172e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
29386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
294bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        updateCurrentPageScroll();
2955a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        updateScrollingIndicator();
29686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        notifyPageSwitchListener();
297a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung        invalidate();
298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
3000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void notifyPageSwitchListener() {
30186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
30286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
306ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka    protected void pageBeginMoving() {
307d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka        if (!mIsPageMoving) {
308d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka            mIsPageMoving = true;
309d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka            onPageBeginMoving();
310d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka        }
3111262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
3121262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
313ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka    protected void pageEndMoving() {
314d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka        if (mIsPageMoving) {
315d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka            mIsPageMoving = false;
316d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka            onPageEndMoving();
317d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka        }
3181262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
3191262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
32026976d9b53161f80faf822d482ac771b8621cf31Adam Cohen    protected boolean isPageMoving() {
32126976d9b53161f80faf822d482ac771b8621cf31Adam Cohen        return mIsPageMoving;
32226976d9b53161f80faf822d482ac771b8621cf31Adam Cohen    }
32326976d9b53161f80faf822d482ac771b8621cf31Adam Cohen
3240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
3251262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageBeginMoving() {
326007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        showScrollingIndicator();
3270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
3301262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageEndMoving() {
331007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        hideScrollingIndicator(false);
3320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
334321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
33586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Registers the specified listener on each page contained in this workspace.
336321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @param l The listener used to respond to long clicks.
338321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void setOnLongClickListener(OnLongClickListener l) {
341321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mLongClickListener = l;
34286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        final int count = getPageCount();
343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < count; i++) {
34486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(i).setOnLongClickListener(l);
345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
348321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
34968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    public void scrollBy(int x, int y) {
35068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        scrollTo(mUnboundedScrollX + x, mScrollY + y);
35168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    }
35268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
35368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    @Override
3540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void scrollTo(int x, int y) {
35568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        mUnboundedScrollX = x;
35668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
35768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        if (x < 0) {
35868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(0, y);
35968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            if (mAllowOverScroll) {
36068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                overScroll(x);
36168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            }
36268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else if (x > mMaxScrollX) {
36368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(mMaxScrollX, y);
36468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            if (mAllowOverScroll) {
36568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                overScroll(x - mMaxScrollX);
36668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            }
36768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else {
36868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(x, y);
36968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        }
37068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mTouchX = x;
3720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
3730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // we moved this functionality to a helper function so SmoothPagedView can reuse it
3760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean computeScrollHelper() {
377321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.computeScrollOffset()) {
378557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung            // Don't bother scrolling if the page does not need to be moved
379557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung            if (mScrollX != mScroller.getCurrX() || mScrollY != mScroller.getCurrY()) {
380557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung                mDirtyPageAlpha = true;
381557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
382557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung            }
3830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            invalidate();
3840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
38586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        } else if (mNextPage != INVALID_PAGE) {
3865f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
38786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
38886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mNextPage = INVALID_PAGE;
3890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            notifyPageSwitchListener();
390b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung
391b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung            // Load the associated pages if necessary
3924e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung            if (mDeferLoadAssociatedPagesUntilScrollCompletes) {
393b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung                loadAssociatedPages(mCurrentPage);
3944e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung                mDeferLoadAssociatedPagesUntilScrollCompletes = false;
395b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung            }
396b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung
39773aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            // We don't want to trigger a page end moving unless the page has settled
39873aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            // and the user has stopped scrolling
39973aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            if (mTouchState == TOUCH_STATE_REST) {
40073aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen                pageEndMoving();
40173aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            }
4020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
403321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
4040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        return false;
4050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
4060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
4070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    @Override
4080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void computeScroll() {
4090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        computeScrollHelper();
410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
411321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
412321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
413321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
414f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        if (!mIsDataReady) {
415f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
416f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung            return;
417f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        }
418f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (widthMode != MeasureSpec.EXACTLY) {
422321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
424321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
4256b879f0a5885274a85333531e091283405d490ccAdam Lesinski        /* Allow the height to be set as WRAP_CONTENT. This allows the particular case
4266b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * of the All apps view on XLarge displays to not take up more space then it needs. Width
4276b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
4286b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * each page to have the same width.
4296b879f0a5885274a85333531e091283405d490ccAdam Lesinski         */
430321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
4316b879f0a5885274a85333531e091283405d490ccAdam Lesinski        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
4326b879f0a5885274a85333531e091283405d490ccAdam Lesinski        int maxChildHeight = 0;
4336b879f0a5885274a85333531e091283405d490ccAdam Lesinski
4346b879f0a5885274a85333531e091283405d490ccAdam Lesinski        final int verticalPadding = mPaddingTop + mPaddingBottom;
435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
43636fcb74425bebc1d88c9e3102484ec902b68f202Michael Jurka
437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        // The children are given the same width and height as the workspace
4385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        // unless they were set to WRAP_CONTENT
439785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
440321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
441321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
4425f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            // disallowing padding in paged view (just pass 0)
4435f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final View child = getChildAt(i);
4445f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4455f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4465f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childWidthMode;
4475f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.width == LayoutParams.WRAP_CONTENT) {
4485f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.AT_MOST;
4495f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
4505f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.EXACTLY;
4515f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
4525f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4535f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childHeightMode;
4545f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.height == LayoutParams.WRAP_CONTENT) {
4555f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.AT_MOST;
4565f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
4575f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.EXACTLY;
4585f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
4595f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4605f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childWidthMeasureSpec =
4615f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                MeasureSpec.makeMeasureSpec(widthSize, childWidthMode);
4625f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childHeightMeasureSpec =
4636b879f0a5885274a85333531e091283405d490ccAdam Lesinski                MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
4645f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4655f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4666b879f0a5885274a85333531e091283405d490ccAdam Lesinski            maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
467785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung            if (DEBUG) Log.d(TAG, "\tmeasure-child" + i + ": " + child.getMeasuredWidth() + ", "
468785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                    + child.getMeasuredHeight());
4696b879f0a5885274a85333531e091283405d490ccAdam Lesinski        }
4706b879f0a5885274a85333531e091283405d490ccAdam Lesinski
4716b879f0a5885274a85333531e091283405d490ccAdam Lesinski        if (heightMode == MeasureSpec.AT_MOST) {
4726b879f0a5885274a85333531e091283405d490ccAdam Lesinski            heightSize = maxChildHeight + verticalPadding;
473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
474faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        if (childCount > 0) {
475faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen            mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
476faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        } else {
477faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen            mMaxScrollX = 0;
478faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        }
479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
480321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setMeasuredDimension(widthSize, heightSize);
481cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    }
482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
4838c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) {
484af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
485af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        int delta = newX - mScrollX;
486af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka
4878c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        final int pageCount = getChildCount();
4888c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        for (int i = 0; i < pageCount; i++) {
4898c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            View page = (View) getChildAt(i);
4908c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            page.setX(page.getX() + delta);
491af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        }
492af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        setCurrentPage(newCurrentPage);
493af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka    }
494af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka
4958c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a
4968c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and
497d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    // tightens the layout accordingly
498d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    public void setLayoutScale(float childrenScale) {
499d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        mLayoutScale = childrenScale;
500d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
501d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // Now we need to do a re-layout, but preserving absolute X and Y coordinates
502d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        int childCount = getChildCount();
503d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        float childrenX[] = new float[childCount];
504d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        float childrenY[] = new float[childCount];
505d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        for (int i = 0; i < childCount; i++) {
506d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final View child = getChildAt(i);
507d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            childrenX[i] = child.getX();
508d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            childrenY[i] = child.getY();
509d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
510b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        // Trigger a full re-layout (never just call onLayout directly!)
511b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
512b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY);
513b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        requestLayout();
514b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        measure(widthSpec, heightSpec);
515b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        layout(mLeft, mTop, mRight, mBottom);
516d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        for (int i = 0; i < childCount; i++) {
517d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final View child = getChildAt(i);
518d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            child.setX(childrenX[i]);
519d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            child.setY(childrenY[i]);
520d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
521b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung
522d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // Also, the page offset has changed  (since the pages are now smaller);
523d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // update the page offset, but again preserving absolute X and Y coordinates
5248c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        scrollToNewPageWithoutMovingPages(mCurrentPage);
525d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    }
526d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
527cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    @Override
52828750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
529f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        if (!mIsDataReady) {
530f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung            return;
531f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        }
532f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
533785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
5345f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(false);
536cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
537cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            scrollTo(newX, 0);
538cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            mScroller.setFinalX(newX);
539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(true);
540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mFirstLayout = false;
541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
5436b879f0a5885274a85333531e091283405d490ccAdam Lesinski        final int verticalPadding = mPaddingTop + mPaddingBottom;
544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
545321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int childLeft = 0;
546321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (childCount > 0) {
547785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung            if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", "
548785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                    + getChildWidth(0));
549e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            childLeft = getRelativeChildOffset(0);
550321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final View child = getChildAt(i);
554321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (child.getVisibility() != View.GONE) {
555d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                final int childWidth = getScaledMeasuredWidth(child);
5566b879f0a5885274a85333531e091283405d490ccAdam Lesinski                final int childHeight = child.getMeasuredHeight();
5576b879f0a5885274a85333531e091283405d490ccAdam Lesinski                int childTop = mPaddingTop;
5586b879f0a5885274a85333531e091283405d490ccAdam Lesinski                if (mCenterPagesVertically) {
5596b879f0a5885274a85333531e091283405d490ccAdam Lesinski                    childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
5606b879f0a5885274a85333531e091283405d490ccAdam Lesinski                }
561d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
562785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
5636b879f0a5885274a85333531e091283405d490ccAdam Lesinski                child.layout(childLeft, childTop,
564d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                        childLeft + child.getMeasuredWidth(), childTop + childHeight);
5659c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                childLeft += childWidth + mPageSpacing;
566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
568d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
569d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            mFirstLayout = false;
570d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
57303929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung    protected void forceUpdateAdjacentPagesAlpha() {
57403929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung        mDirtyPageAlpha = true;
57503929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung        updateAdjacentPagesAlpha();
57603929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung    }
57703929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung
578e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void updateAdjacentPagesAlpha() {
5790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mFadeInAdjacentScreens) {
5800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
58163257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung                int screenWidth = getMeasuredWidth();
58263257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung                int halfScreenSize = screenWidth / 2;
583e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int screenCenter = mScrollX + halfScreenSize;
5840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int childCount = getChildCount();
5850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < childCount; ++i) {
5860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    View layout = (View) getChildAt(i);
587d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                    int childWidth = getScaledMeasuredWidth(layout);
5880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int halfChildWidth = (childWidth / 2);
5890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childCenter = getChildOffset(i) + halfChildWidth;
590e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
591b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // On the first layout, we may not have a width nor a proper offset, so for now
592b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // we should just assume full page width (and calculate the offset according to
593b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // that).
594b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    if (childWidth <= 0) {
59563257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung                        childWidth = screenWidth;
596b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                        childCenter = (i * childWidth) + (childWidth / 2);
597b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    }
598b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung
599e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int d = halfChildWidth;
600e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int distanceFromScreenCenter = childCenter - screenCenter;
601e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    if (distanceFromScreenCenter > 0) {
602e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i > 0) {
603d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                            d += getScaledMeasuredWidth(getChildAt(i - 1)) / 2;
604f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung                        } else {
605f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung                            continue;
606e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
6070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
608e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i < childCount - 1) {
609d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                            d += getScaledMeasuredWidth(getChildAt(i + 1)) / 2;
610f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung                        } else {
611f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung                            continue;
612e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
6130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
6149c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                    d += mPageSpacing;
615e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
616b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // Preventing potential divide-by-zero
617b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    d = Math.max(1, d);
618b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung
619e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d;
620e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
621e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float alpha = 1.0f - dimAlpha;
622e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
623f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    if (alpha < ALPHA_QUANTIZE_LEVEL) {
624f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                        alpha = 0.0f;
625f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) {
626f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                        alpha = 1.0f;
627f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    }
628f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
629a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // Due to the way we're setting alpha on our children in PagedViewCellLayout,
630a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // this optimization causes alpha to not be properly updated sometimes (repro
631a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // case: in xlarge mode, swipe to second page in All Apps, then click on "My
632a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // Apps" tab. the page will have alpha 0 until you swipe it). Removing
633a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka                    // optimization fixes the issue, but we should fix this in a better manner
634cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka                    //if (Float.compare(alpha, layout.getAlpha()) != 0) {
6350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        layout.setAlpha(alpha);
636cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka                    //}
637affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung                }
6380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageAlpha = false;
639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
641e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
6420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
643f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    protected void screenScrolled(int screenCenter) {
644007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        updateScrollingIndicator();
645f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    }
646f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
647e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    @Override
648e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void dispatchDraw(Canvas canvas) {
649f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int halfScreenSize = getMeasuredWidth() / 2;
650f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int screenCenter = mScrollX + halfScreenSize;
651f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
652f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        if (screenCenter != mLastScreenCenter) {
653f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            screenScrolled(screenCenter);
654f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            updateAdjacentPagesAlpha();
655f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            mLastScreenCenter = screenCenter;
656f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        }
6570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
658e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        // Find out which screens are visible; as an optimization we only call draw on them
6590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // As an optimization, this code assumes that all pages have the same width as the 0th
6600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // page.
6610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int pageCount = getChildCount();
662c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka        if (pageCount > 0) {
663d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final int pageWidth = getScaledMeasuredWidth(getChildAt(0));
664c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final int screenWidth = getMeasuredWidth();
665557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung            int x = getScaledRelativeChildOffset(0) + pageWidth;
666c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int leftScreen = 0;
667c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int rightScreen = 0;
668c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            while (x <= mScrollX) {
669c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                leftScreen++;
670c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                x += getScaledMeasuredWidth(getChildAt(leftScreen)) + mPageSpacing;
671c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
672c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = leftScreen;
673c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung            while (x < mScrollX + screenWidth && rightScreen < pageCount) {
674c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                rightScreen++;
675c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                if (rightScreen < pageCount) {
676c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                    x += getScaledMeasuredWidth(getChildAt(rightScreen)) + mPageSpacing;
677c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                }
678c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
679c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = Math.min(getChildCount() - 1, rightScreen);
6800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
681c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final long drawingTime = getDrawingTime();
68229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            // Clip to the bounds
68329d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.save();
68429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft,
68529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung                    mScrollY + mBottom - mTop);
68629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung
687c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            for (int i = leftScreen; i <= rightScreen; i++) {
688c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                drawChild(canvas, getChildAt(i), drawingTime);
689c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
69029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.restore();
6910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
69686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
69786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page != mCurrentPage || !mScroller.isFinished()) {
69886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
70686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int focusablePage;
70786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mNextPage != INVALID_PAGE) {
70886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mNextPage;
709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
71086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mCurrentPage;
711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
71286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View v = getPageAt(focusablePage);
713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
71476fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen            return v.requestFocus(direction, previouslyFocusedRect);
715321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
716321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
717321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean dispatchUnhandledMove(View focused, int direction) {
721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
72286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() > 0) {
72386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() - 1);
724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
726321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT) {
72786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() < getPageCount() - 1) {
72886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() + 1);
729321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
730321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
731321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return super.dispatchUnhandledMove(focused, direction);
733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
73786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
73886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(mCurrentPage).addFocusables(views, direction);
739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
740321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
74186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) {
74286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage - 1).addFocusables(views, direction);
743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT){
74586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getPageCount() - 1) {
74686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage + 1).addFocusables(views, direction);
747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * If one of our descendant views decides that it could be focused now, only
75386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * pass that along if it's on the current page.
754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
75586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This happens when live folders requery, and if they're off page, they
75686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * end up calling requestFocus, which pulls it on page.
757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void focusableViewAvailable(View focused) {
76086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View current = getPageAt(mCurrentPage);
761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        View v = focused;
762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        while (true) {
763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == current) {
764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                super.focusableViewAvailable(focused);
765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == this) {
768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent parent = v.getParent();
771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (parent instanceof View) {
772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                v = (View)v.getParent();
773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else {
774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
777321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
778321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
779321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * {@inheritDoc}
781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (disallowIntercept) {
785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // We need to make sure to cancel our long press if
786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // a scrollable widget takes over touch events
78786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            final View currentPage = getChildAt(mCurrentPage);
78886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage.cancelLongPress();
789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestDisallowInterceptTouchEvent(disallowIntercept);
791321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
793d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    /**
794d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     * Return true if a tap at (x, y) should trigger a flip to the previous page.
795d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     */
796d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    protected boolean hitsPreviousPage(float x, float y) {
797d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy        return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
798d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    }
799d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy
800d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    /**
801d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     * Return true if a tap at (x, y) should trigger a flip to the next page.
802d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     */
803d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    protected boolean hitsNextPage(float x, float y) {
804d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy        return  (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
805d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    }
806d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy
807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * This method JUST determines whether we want to intercept the motion.
811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * If we return true, onTouchEvent will be called and we do the actual
812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * scrolling there.
813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
8146342bbae1a18f3c1862953e33309481703f541cfAdam Cohen        acquireVelocityTrackerAndAddMovement(ev);
815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
81645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
81745e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
81845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Shortcut the most recurring case: the user is in the dragging
821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * state and he is moving his finger.  We want to intercept this
822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * motion.
823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if ((action == MotionEvent.ACTION_MOVE) &&
826321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                (mTouchState == TOUCH_STATE_SCROLLING)) {
827321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
830321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
831321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_MOVE: {
832321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * whether the user has moved far enough from his original down touch.
835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
8361ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                if (mActivePointerId != INVALID_POINTER) {
8371ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    determineScrollingStart(ev);
8381ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    break;
8391ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                }
8401ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
8411ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
8421ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // i.e. fall through to the next case (don't break)
8431ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
8441ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // while it's small- this was causing a crash before we checked for INVALID_POINTER)
845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_DOWN: {
848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX();
849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float y = ev.getY();
850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Remember location of down touch
851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mDownMotionX = x;
852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionY = y;
854c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                mLastMotionXRemainder = 0;
855aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                mTotalMotionX = 0;
856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = ev.getPointerId(0);
857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = true;
858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * If being flinged and user touches the screen, initiate drag;
861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * otherwise don't.  mScroller.isFinished should be false when
862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * being flinged.
863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
864fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka                final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
8655f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
8665f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                if (finishedScrolling) {
8675f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_REST;
8685f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mScroller.abortAnimation();
8695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                } else {
8705f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_SCROLLING;
8715f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
872321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
87386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                // check if this can be the beginning of a tap on the side of the pages
874321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // to scroll the current page
875d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (getChildCount() > 0) {
877d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                        if (hitsPreviousPage(x, y)) {
878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_PREV_PAGE;
879d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                        } else if (hitsNextPage(x, y)) {
880321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_NEXT_PAGE;
881321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        }
882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
885321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
886321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
887321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_UP:
8881d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            case MotionEvent.ACTION_CANCEL:
889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_REST;
890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = INVALID_POINTER;
8926342bbae1a18f3c1862953e33309481703f541cfAdam Cohen                releaseVelocityTracker();
893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
895321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_POINTER_UP:
896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                onSecondaryPointerUp(ev);
8976342bbae1a18f3c1862953e33309481703f541cfAdam Cohen                releaseVelocityTracker();
898321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
899321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
901321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * The only time we want to intercept motion events is if we are in the
903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * drag mode.
904321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mTouchState != TOUCH_STATE_REST;
906321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
90880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    protected void animateClickFeedback(View v, final Runnable r) {
90980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // animate the view slightly to show click feedback running some logic after it is "pressed"
910228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.
911228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung                loadAnimator(mContext, R.anim.paged_view_click_feedback);
912228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.setTarget(v);
913228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.addListener(new AnimatorListenerAdapter() {
914228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung            public void onAnimationRepeat(Animator animation) {
91580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                r.run();
91680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
91780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        });
918228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.start();
91980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
92080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
921f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void determineScrollingStart(MotionEvent ev) {
922f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        determineScrollingStart(ev, 1.0f);
923f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    }
924f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen
925321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /*
926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Determines if we should change the touch state to start scrolling after the
927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * user moves their touch point too far.
928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
929f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Locally do absolute value. mLastMotionX is set to the y value
932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * of the down event.
933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
9352698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka        if (pointerIndex == -1) {
9362698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka            return;
9372698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka        }
938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float x = ev.getX(pointerIndex);
939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float y = ev.getY(pointerIndex);
940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int xDiff = (int) Math.abs(x - mLastMotionX);
941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int yDiff = (int) Math.abs(y - mLastMotionY);
942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
943f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xPaged = xDiff > mPagingTouchSlop;
945321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xMoved = xDiff > touchSlop;
946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean yMoved = yDiff > touchSlop;
947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
948f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        if (xMoved || xPaged || yMoved) {
9490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mUsePagingTouchSlop ? xPaged : xMoved) {
950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll if the user moved far enough along the X axis
951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_SCROLLING;
9526342bbae1a18f3c1862953e33309481703f541cfAdam Cohen                mTotalMotionX += Math.abs(mLastMotionX - x);
953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
954c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                mLastMotionXRemainder = 0;
9550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mTouchX = mScrollX;
9560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
9570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Either way, cancel any pending longpress
960f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            cancelCurrentPageLongPress();
961f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        }
962f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    }
963f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen
964f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void cancelCurrentPageLongPress() {
965f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        if (mAllowLongPress) {
966f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            mAllowLongPress = false;
967f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // Try canceling the long press. It could also have been scheduled
968f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // by a distant descendant, so use the mAllowLongPress flag to block
969f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // everything
970f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            final View currentPage = getPageAt(mCurrentPage);
971f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            if (currentPage != null) {
972f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen                currentPage.cancelLongPress();
973321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
974321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
975321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
976321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
977e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // This curve determines how the effect of scrolling over the limits of the page dimishes
978e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // as the user pulls further and further from the bounds
979e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private float overScrollInfluenceCurve(float f) {
980e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f -= 1.0f;
981e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        return f * f * f + 1.0f;
982e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
983e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
98468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected void overScroll(float amount) {
985e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int screenSize = getMeasuredWidth();
986e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
987e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float f = (amount / screenSize);
988e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
989e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        if (f == 0) return;
990e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
991e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
9927bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        // Clamp this factor, f, to -1 < f < 1
9937bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        if (Math.abs(f) >= 1) {
9947bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen            f /= Math.abs(f);
9957bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        }
9967bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen
997e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
99868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        if (amount < 0) {
99968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            mScrollX = overScrollAmount;
100068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else {
100168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            mScrollX = mMaxScrollX + overScrollAmount;
100268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        }
100368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        invalidate();
100468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    }
100568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
1006c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka    protected float maxOverScroll() {
1007c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
1008c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        // exceed). Used to find out how much extra wallpaper we need for the overscroll effect
1009c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        float f = 1.0f;
1010c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
1011c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        return OVERSCROLL_DAMP_FACTOR * f;
1012c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka    }
1013c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka
1014321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onTouchEvent(MotionEvent ev) {
101645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
101745e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onTouchEvent(ev);
101845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
1019b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        acquireVelocityTrackerAndAddMovement(ev);
1020321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1021321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
1024321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_DOWN:
1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            /*
1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * If being flinged and user touches, stop the fling. isFinished
1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * will be false if being flinged.
1028321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             */
1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (!mScroller.isFinished()) {
1030321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mScroller.abortAnimation();
1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1033321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Remember where the motion event started
1034321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mDownMotionX = mLastMotionX = ev.getX();
1035c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung            mLastMotionXRemainder = 0;
1036aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen            mTotalMotionX = 0;
1037321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(0);
10380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
10390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
10400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
1041321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1042321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1043321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_MOVE:
1044321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
1045321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll to follow the motion event
1046321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
1047321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
1048c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                final float deltaX = mLastMotionX + mLastMotionXRemainder - x;
1049321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1050aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                mTotalMotionX += Math.abs(deltaX);
1051aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1052c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // Only scroll and update mLastMotionX if we have moved some discrete amount.  We
1053c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // keep the remainder because we are actually testing if we've moved from the last
1054c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                // scrolled position (which is discrete).
1055c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                if (Math.abs(deltaX) >= 1.0f) {
105668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    mTouchX += deltaX;
105768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
105868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    if (!mDeferScrollUpdate) {
1059c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                        scrollBy((int) deltaX, 0);
1060785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                        if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX);
106168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    } else {
106268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                        invalidate();
1063321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
1064c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                    mLastMotionX = x;
1065c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung                    mLastMotionXRemainder = deltaX - (int) deltaX;
1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1067321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    awakenScrollBars();
1068321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1069564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen            } else {
1070321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                determineScrollingStart(ev);
1071321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1072321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_UP:
1075321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
1076321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int activePointerId = mActivePointerId;
1077321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(activePointerId);
1078321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
1079321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final VelocityTracker velocityTracker = mVelocityTracker;
1080321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1081321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
10829cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                final int deltaX = (int) (x - mDownMotionX);
1083b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE;
10840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int snapVelocity = mSnapVelocity;
1085aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1086b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
1087b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen
1088aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // In the case that the page is moved far to one direction and then is flung
1089aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // in the opposite direction, we use a threshold to determine whether we should
1090aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // just return to the starting page, or if we should skip one further.
1091aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                boolean returnToOriginalPage = false;
1092aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                final int pageWidth = getScaledMeasuredWidth(getChildAt(mCurrentPage));
1093b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
1094aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        Math.signum(velocityX) != Math.signum(deltaX)) {
1095aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    returnToOriginalPage = true;
1096aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                }
1097aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1098b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen                boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
1099aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        Math.abs(velocityX) > snapVelocity;
1100aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen
1101aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                int finalPage;
1102aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // We give flings precedence over large moves, which is why we short-circuit our
1103aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // test for a large move if a fling has been registered. That is, a large
1104aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                // move to the left and fling to the right will register as a fling to the right.
1105aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                if (((isSignificantMove && deltaX > 0 && !isFling) ||
1106aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        (isFling && velocityX > 0)) && mCurrentPage > 0) {
1107aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
1108aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    snapToPageWithVelocity(finalPage, velocityX);
1109aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
1110aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                        (isFling && velocityX < 0)) &&
111186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                        mCurrentPage < getChildCount() - 1) {
1112aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
1113aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen                    snapToPageWithVelocity(finalPage, velocityX);
1114321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1117d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
1118321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
1119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
1120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
112186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.max(0, mCurrentPage - 1);
112286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
112386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1127d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
1128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
1129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
1130321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
113186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
113286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
113386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
1134321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1135321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1136321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
11371d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            } else {
11381d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown                onWallpaperTap(ev);
1139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
1141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
1142b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
1143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1144321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_CANCEL:
1146b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
1147b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka                snapToDestination();
1148b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            }
1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
1151b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
1152321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1153321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1154321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_POINTER_UP:
1155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            onSecondaryPointerUp(ev);
1156321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return true;
1160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1162185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung    @Override
1163185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung    public boolean onGenericMotionEvent(MotionEvent event) {
1164185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1165185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung            switch (event.getAction()) {
1166185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                case MotionEvent.ACTION_SCROLL: {
1167185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    // Handle mouse (or ext. device) by shifting the page depending on the scroll
1168185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    final float vscroll;
1169185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    final float hscroll;
1170185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
1171185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        vscroll = 0;
1172185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1173185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    } else {
1174185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1175185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
1176185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    }
1177185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    if (hscroll != 0 || vscroll != 0) {
1178185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        if (hscroll > 0 || vscroll > 0) {
1179185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                            scrollRight();
1180185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        } else {
1181185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                            scrollLeft();
1182185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        }
1183185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                        return true;
1184185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                    }
1185185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung                }
1186185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung            }
1187185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung        }
1188185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung        return super.onGenericMotionEvent(event);
1189185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung    }
1190185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung
1191b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
1192b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker == null) {
1193b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = VelocityTracker.obtain();
1194b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
1195b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        mVelocityTracker.addMovement(ev);
1196b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
1197b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
1198b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void releaseVelocityTracker() {
1199b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker != null) {
1200b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker.recycle();
1201b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = null;
1202b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
1203b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
1204b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
1205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void onSecondaryPointerUp(MotionEvent ev) {
1206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerId = ev.getPointerId(pointerIndex);
1209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (pointerId == mActivePointerId) {
1210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // This was our active pointer going up. Choose a new
1211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // active pointer and adjust accordingly.
1212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // TODO: Make this decision more intelligent.
1213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
1215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionY = ev.getY(newPointerIndex);
1216c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung            mLastMotionXRemainder = 0;
1217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(newPointerIndex);
1218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mVelocityTracker != null) {
1219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mVelocityTracker.clear();
1220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
12221d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown        if (mTouchState == TOUCH_STATE_REST) {
12231d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            onWallpaperTap(ev);
12241d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown        }
12251d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown    }
12261d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown
1227f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung    protected void onWallpaperTap(MotionEvent ev) {}
1228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
1230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestChildFocus(View child, View focused) {
1231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestChildFocus(child, focused);
123286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
123397d85d23b013347bead4e2f5fa430a79ce69431eWinson Chung        if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
123486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
1235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1238e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getChildIndexForRelativeOffset(int relativeOffset) {
1239e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int childCount = getChildCount();
12409c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int left;
12419c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int right;
1242e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        for (int i = 0; i < childCount; ++i) {
12439c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            left = getRelativeChildOffset(i);
1244d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            right = (left + getScaledMeasuredWidth(getChildAt(i)));
1245e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            if (left <= relativeOffset && relativeOffset <= right) {
1246e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                return i;
1247e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            }
1248e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        }
1249e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return -1;
1250e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1251e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
12521908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung    protected void setMinimumWidthOverride(int minimumWidth) {
12531908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung        mMinimumWidth = minimumWidth;
12541908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung    }
125534b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung    protected void resetMinimumWidthOverride() {
125634b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung        mMinimumWidth = 0;
125734b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung    }
12581908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung
12591908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung    protected int getChildWidth(int index) {
126063257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        // This functions are called enough times that it actually makes a difference in the
126163257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        // profiler -- so just inline the max() here
126263257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        final int measuredWidth = getChildAt(index).getMeasuredWidth();
126363257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        final int minWidth = mMinimumWidth;
126463257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        return (minWidth > measuredWidth) ? minWidth : measuredWidth;
12651908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung    }
12661908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung
1267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getRelativeChildOffset(int index) {
12681908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung        return (getMeasuredWidth() - getChildWidth(index)) / 2;
1269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1271557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung    protected int getScaledRelativeChildOffset(int index) {
1272557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung        return (getMeasuredWidth() - getScaledMeasuredWidth(getChildAt(index))) / 2;
1273557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung    }
1274557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung
1275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getChildOffset(int index) {
1276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (getChildCount() == 0)
1277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return 0;
1278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int offset = getRelativeChildOffset(0);
1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < index; ++i) {
1281d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            offset += getScaledMeasuredWidth(getChildAt(i)) + mPageSpacing;
1282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return offset;
1284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1286d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    protected int getScaledMeasuredWidth(View child) {
128763257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        // This functions are called enough times that it actually makes a difference in the
128863257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        // profiler -- so just inline the max() here
128963257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        final int measuredWidth = child.getMeasuredWidth();
129063257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        final int minWidth = mMinimumWidth;
129163257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth;
129263257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung        return (int) (maxWidth * mLayoutScale + 0.5f);
1293d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    }
1294d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
1295d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    int getPageNearestToCenterOfScreen() {
1296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenter = getMeasuredWidth();
1297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenterIndex = -1;
1298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int screenCenter = mScrollX + (getMeasuredWidth() / 2);
1299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
1300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; ++i) {
13010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            View layout = (View) getChildAt(i);
1302d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            int childWidth = getScaledMeasuredWidth(layout);
1303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int halfChildWidth = (childWidth / 2);
1304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childCenter = getChildOffset(i) + halfChildWidth;
1305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
1306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
1307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenter = distanceFromScreenCenter;
1308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenterIndex = i;
1309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1311d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        return minDistanceFromScreenCenterIndex;
1312d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    }
1313d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen
1314d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    protected void snapToDestination() {
1315d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
1316321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1317321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1318e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static class ScrollInterpolator implements Interpolator {
1319e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        public ScrollInterpolator() {
1320e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1321e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1322e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        public float getInterpolation(float t) {
1323e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            t -= 1.0f;
1324e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            return t*t*t*t*t + 1;
1325e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1326e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1327e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1328e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // We want the duration of the page snap animation to be influenced by the distance that
1329e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // the screen has to travel, however, we don't want this duration to be effected in a
1330e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
1331e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // of travel has on the overall snap duration.
1332e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    float distanceInfluenceForSnapDuration(float f) {
1333e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f -= 0.5f; // center the values about 0.
1334e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f *= 0.3f * Math.PI / 2.0f;
1335e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        return (float) Math.sin(f);
1336e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1337e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
13380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPageWithVelocity(int whichPage, int velocity) {
1339e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
1340e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int halfScreenSize = getMeasuredWidth() / 2;
1341e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1342785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
1343785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
1344785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                + getMeasuredWidth() + ", " + getChildWidth(whichPage));
1345e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
1346e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int delta = newX - mUnboundedScrollX;
1347e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int duration = 0;
1348e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1349e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        if (Math.abs(velocity) < MIN_FLING_VELOCITY) {
1350e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            // If the velocity is low enough, then treat this more as an automatic page advance
1351e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            // as opposed to an apparent physical response to flinging
1352e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1353e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            return;
1354e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1355e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1356e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // Here we compute a "distance" that will be used in the computation of the overall
1357e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // snap duration. This is a function of the actual distance that needs to be traveled;
1358e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // we keep this value close to half screen size in order to reduce the variance in snap
1359e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // duration as a function of the distance the page needs to travel.
136020b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka        float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize));
1361e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float distance = halfScreenSize + halfScreenSize *
1362e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen                distanceInfluenceForSnapDuration(distanceRatio);
1363e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1364e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        velocity = Math.abs(velocity);
1365e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
1366e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1367e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // we want the page's snap velocity to approximately match the velocity at which the
1368e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // user flings, so we scale the duration by a value near to the derivative of the scroll
136920b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka        // interpolator at zero, ie. 5. We use 4 to make it a little slower.
137020b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka        duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
1371e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1372e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        snapToPage(whichPage, delta, duration);
13730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
13740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
13750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage) {
13765f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1377321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1378321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
13790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int duration) {
138086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
1381321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1382785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
1383785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung        if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", "
1384785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                + getChildWidth(whichPage));
138586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
138668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        final int sX = mUnboundedScrollX;
1387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int delta = newX - sX;
13880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage, delta, duration);
13890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
13900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
13910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int delta, int duration) {
13920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mNextPage = whichPage;
13930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
13940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        View focusedChild = getFocusedChild();
13950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (focusedChild != null && whichPage != mCurrentPage &&
13960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                focusedChild == getChildAt(mCurrentPage)) {
13970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            focusedChild.clearFocus();
13980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
13990142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
14000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        pageBeginMoving();
1401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        awakenScrollBars(duration);
1402321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (duration == 0) {
1403321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            duration = Math.abs(delta);
1404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1405321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
140768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
140880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1409b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        // Load associated pages immediately if someone else is handling the scroll, otherwise defer
1410b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        // loading associated pages until the scroll settles
1411b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        if (mDeferScrollUpdate) {
1412b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung            loadAssociatedPages(mNextPage);
1413b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        } else {
14144e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung            mDeferLoadAssociatedPagesUntilScrollCompletes = true;
1415b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung        }
14160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        notifyPageSwitchListener();
1417321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
1418321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollLeft() {
1421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
142286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
1423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
142486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage > 0) snapToPage(mNextPage - 1);
1425321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1428321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollRight() {
1429321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
143086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
1431321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
143286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
1433321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1434321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
143686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public int getPageForView(View v) {
1437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int result = -1;
1438321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
1439321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent vp = v.getParent();
1440321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int count = getChildCount();
1441321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            for (int i = 0; i < count; i++) {
1442321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (vp == getChildAt(i)) {
1443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    return i;
1444321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1445321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1446321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1447321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return result;
1448321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1449321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1450321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
1451321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @return True is long presses are still allowed for the current touch
1452321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean allowLongPress() {
1454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mAllowLongPress;
1455321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1456321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
14570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
14580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * Set true to allow long-press events to be triggered, usually checked by
14590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * {@link Launcher} to accept or block dpad-initiated long-presses.
14600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     */
14610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setAllowLongPress(boolean allowLongPress) {
14620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mAllowLongPress = allowLongPress;
14630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
14640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public static class SavedState extends BaseSavedState {
146686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int currentPage = -1;
1467321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState(Parcelable superState) {
1469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(superState);
1470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1471321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        private SavedState(Parcel in) {
1473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(in);
147486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage = in.readInt();
1475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1476321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1477321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        @Override
1478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public void writeToParcel(Parcel out, int flags) {
1479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super.writeToParcel(out, flags);
148086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            out.writeInt(currentPage);
1481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1483321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public static final Parcelable.Creator<SavedState> CREATOR =
1484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                new Parcelable.Creator<SavedState>() {
1485321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState createFromParcel(Parcel in) {
1486321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState(in);
1487321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1488321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState[] newArray(int size) {
1490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState[size];
1491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        };
1493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1494321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
149586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void loadAssociatedPages(int page) {
14960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
14970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
14980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (page < count) {
1499e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int lowerPageBound = getAssociatedLowerPageBound(page);
1500e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int upperPageBound = getAssociatedUpperPageBound(page);
1501785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/"
1502785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung                        + upperPageBound);
15030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < count; ++i) {
15048245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                    Page layout = (Page) getChildAt(i);
15058245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                    final int childCount = layout.getPageChildCount();
15060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (lowerPageBound <= i && i <= upperPageBound) {
15070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (mDirtyPageContent.get(i)) {
15080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            syncPageItems(i);
15090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            mDirtyPageContent.set(i, false);
15100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
15110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
15120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (childCount > 0) {
15138245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                            layout.removeAllViewsOnPage();
15140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
15150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mDirtyPageContent.set(i, true);
151680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                    }
151780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                }
151880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
151980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        }
152080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
152180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1522e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedLowerPageBound(int page) {
1523e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.max(0, page - 1);
1524e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1525e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedUpperPageBound(int page) {
1526e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int count = getChildCount();
1527e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.min(page + 1, count - 1);
1528e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1529e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
15305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void startChoiceMode(int mode, ActionMode.Callback callback) {
1531430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        if (isChoiceMode(CHOICE_MODE_NONE)) {
1532430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mChoiceMode = mode;
1533430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mActionMode = startActionMode(callback);
1534430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        }
15355f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
15365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
15372b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public void endChoiceMode() {
15385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        if (!isChoiceMode(CHOICE_MODE_NONE)) {
15395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            mChoiceMode = CHOICE_MODE_NONE;
15405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            resetCheckedGrandchildren();
1541e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka            if (mActionMode != null) mActionMode.finish();
15429f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            mActionMode = null;
15435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
15445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
15455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
15465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected boolean isChoiceMode(int mode) {
15475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return mChoiceMode == mode;
15485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
15495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
15505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected ArrayList<Checkable> getCheckedGrandchildren() {
15515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        ArrayList<Checkable> checked = new ArrayList<Checkable>();
15525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final int childCount = getChildCount();
15535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < childCount; ++i) {
15548245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka            Page layout = (Page) getChildAt(i);
15558245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka            final int grandChildCount = layout.getPageChildCount();
15565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            for (int j = 0; j < grandChildCount; ++j) {
15578245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                final View v = layout.getChildOnPageAt(j);
15589f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                if (v instanceof Checkable && ((Checkable) v).isChecked()) {
15595f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    checked.add((Checkable) v);
15605f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
15615f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            }
15625f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
15635f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return checked;
15645f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
15655f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
15669f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    /**
15679f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * If in CHOICE_MODE_SINGLE and an item is checked, returns that item.
15689f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * Otherwise, returns null.
15699f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     */
15709f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    protected Checkable getSingleCheckedGrandchild() {
15716f13342ffd3f968de9ff86b988621cc91d94adffPatrick Dubroy        if (mChoiceMode != CHOICE_MODE_MULTIPLE) {
15729f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            final int childCount = getChildCount();
15739f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            for (int i = 0; i < childCount; ++i) {
15748245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                Page layout = (Page) getChildAt(i);
15758245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                final int grandChildCount = layout.getPageChildCount();
15769f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                for (int j = 0; j < grandChildCount; ++j) {
15778245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka                    final View v = layout.getChildOnPageAt(j);
15789f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    if (v instanceof Checkable && ((Checkable) v).isChecked()) {
15799f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                        return (Checkable) v;
15809f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    }
15819f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                }
15829f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            }
15839f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        }
15849f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        return null;
15859f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    }
15869f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
15875f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void resetCheckedGrandchildren() {
15885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        // loop through children, and set all of their children to _not_ be checked
15895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final ArrayList<Checkable> checked = getCheckedGrandchildren();
15905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < checked.size(); ++i) {
15915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final Checkable c = checked.get(i);
15925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            c.setChecked(false);
15935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
15945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
15955f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
159686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
159786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called ONLY to synchronize the number of pages that the paged view has.
159886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * To actually fill the pages with information, implement syncPageItems() below.  It is
159986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * guaranteed that syncPageItems() will be called for a particular page before it is shown,
160086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * and therefore, individual page items do not need to be updated in this method.
160186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1602321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPages();
160386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
160486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
160586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called to synchronize the items that are on a particular page.  If views on
160686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * the page can be reused, then they should be updated within this method.
160786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1608321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPageItems(int page);
160986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
1610983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka    protected void postInvalidatePageData(final boolean clearViews) {
1611983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka        post(new Runnable() {
1612983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka                // post the call to avoid a call to requestLayout from a layout pass
1613983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka                public void run() {
1614983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka                    if (clearViews) {
1615983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka                        removeAllViews();
1616983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka                    }
1617983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka                    invalidatePageData();
1618983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka                }
1619983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka            });
1620983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka    }
1621983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka
1622244d74cb353f1260c4d633e719bf84bb3b6e52bcPatrick Dubroy    protected void invalidatePageData() {
16235a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        invalidatePageData(-1);
16245a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung    }
16255a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung    protected void invalidatePageData(int currentPage) {
1626f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        if (!mIsDataReady) {
1627f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung            return;
1628f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung        }
1629f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung
16300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
16310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Update all the pages
16320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            syncPages();
163386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
16345a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            // We must force a measure after we've loaded the pages to update the content width and
16355a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            // to determine the full scroll width
16365a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
16375a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
16385a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung
16395a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            // Set a new page as the current page if necessary
16405a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            if (currentPage > -1) {
16415a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung                setCurrentPage(currentPage);
16425a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            }
16435a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung
16440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Mark each of the pages as dirty
16450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
16460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageContent.clear();
16470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            for (int i = 0; i < count; ++i) {
16480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageContent.add(true);
16490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
165086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
16510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Load any pages that are necessary for the current window of views
16520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            loadAssociatedPages(mCurrentPage);
16530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageAlpha = true;
1654b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung            updateAdjacentPagesAlpha();
16550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            requestLayout();
16560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
1657321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1658007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1659007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private ImageView getScrollingIndicator() {
1660007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        // We use mHasScrollIndicator to prevent future lookups if there is no sibling indicator
1661007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        // found
1662007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (mHasScrollIndicator && mScrollIndicator == null) {
1663007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            ViewGroup parent = (ViewGroup) getParent();
1664007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            mScrollIndicator = (ImageView) (parent.findViewById(R.id.paged_view_indicator));
1665007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            mHasScrollIndicator = mScrollIndicator != null;
1666007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            if (mHasScrollIndicator) {
1667007c69867d821ea2b271398577a8b3440b3a7046Winson Chung                mScrollIndicator.setVisibility(View.VISIBLE);
1668007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            }
1669007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        }
1670007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        return mScrollIndicator;
1671007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1672007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1673007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    protected boolean isScrollingIndicatorEnabled() {
1674649723cfb3d73af16dd02462725700897ca60e38Winson Chung        return !LauncherApplication.isScreenLarge();
1675007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1676007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
16775a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung    Runnable hideScrollingIndicatorRunnable = new Runnable() {
16785a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        @Override
16795a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        public void run() {
16805a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung            hideScrollingIndicator(false);
16815a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        }
16825a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung    };
16833ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    protected void flashScrollingIndicator() {
16845a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        removeCallbacks(hideScrollingIndicatorRunnable);
16853ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung        showScrollingIndicator();
16865a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung        postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration);
16873ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    }
16883ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung
1689007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    protected void showScrollingIndicator() {
1690007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (getChildCount() <= 1) return;
1691007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (!isScrollingIndicatorEnabled()) return;
1692007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1693007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        getScrollingIndicator();
1694007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (mScrollIndicator != null) {
1695007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            // Fade the indicator in
1696007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            updateScrollingIndicatorPosition();
169732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            mScrollIndicator.setVisibility(View.VISIBLE);
169832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            if (mScrollIndicatorAnimator != null) {
169932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator.cancel();
170032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            }
170132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f);
170232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration);
170332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            mScrollIndicatorAnimator.start();
1704007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        }
1705007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1706007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1707007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    protected void hideScrollingIndicator(boolean immediately) {
1708007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (getChildCount() <= 1) return;
1709007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (!isScrollingIndicatorEnabled()) return;
1710007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1711007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        getScrollingIndicator();
1712007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (mScrollIndicator != null) {
1713007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            // Fade the indicator out
1714007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            updateScrollingIndicatorPosition();
171532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            if (mScrollIndicatorAnimator != null) {
171632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator.cancel();
171732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            }
171832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            if (immediately) {
171932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.setVisibility(View.GONE);
172032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.setAlpha(0f);
172132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            } else {
172232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f);
172332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration);
172432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
172532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    private boolean cancelled = false;
172632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    @Override
172732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    public void onAnimationCancel(android.animation.Animator animation) {
172832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                        cancelled = true;
172932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    }
173032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    @Override
173132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    public void onAnimationEnd(Animator animation) {
173232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                        if (!cancelled) {
173332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                            mScrollIndicator.setVisibility(View.GONE);
173432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                        }
173532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                    }
173632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                });
173732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicatorAnimator.start();
173832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            }
1739007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        }
1740007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1741007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
174232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung    /**
174332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung     * To be overridden by subclasses to determine whether the scroll indicator should stretch to
174432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung     * fill its space on the track or not.
174532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung     */
174632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung    protected boolean hasElasticScrollIndicator() {
174732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        return false;
174832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung    }
174932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung
1750007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private void updateScrollingIndicator() {
1751007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (getChildCount() <= 1) return;
1752007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (!isScrollingIndicatorEnabled()) return;
1753007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1754007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        getScrollingIndicator();
1755007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        if (mScrollIndicator != null) {
1756007c69867d821ea2b271398577a8b3440b3a7046Winson Chung            updateScrollingIndicatorPosition();
1757007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        }
1758007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
1759007c69867d821ea2b271398577a8b3440b3a7046Winson Chung
1760007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    private void updateScrollingIndicatorPosition() {
1761649723cfb3d73af16dd02462725700897ca60e38Winson Chung        if (!isScrollingIndicatorEnabled()) return;
1762649723cfb3d73af16dd02462725700897ca60e38Winson Chung
176332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int numPages = getChildCount();
176432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int pageWidth = getMeasuredWidth();
176532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int maxPageWidth = (numPages * getMeasuredWidth()) + ((numPages - 1) * mPageSpacing);
1766f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung        int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
176732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
176832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();
176932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung
1770007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        float offset = (float) getScrollX() / maxPageWidth;
177132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        int indicatorSpace = trackWidth / numPages;
1772f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung        int indicatorPos = (int) (offset * trackWidth) + mScrollIndicatorPaddingLeft;
177332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        if (hasElasticScrollIndicator()) {
177432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) {
177532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.getLayoutParams().width = indicatorSpace;
177632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung                mScrollIndicator.requestLayout();
177732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            }
177832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        } else {
177932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2;
178032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung            indicatorPos += indicatorCenterOffset;
178132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung        }
1782007c69867d821ea2b271398577a8b3440b3a7046Winson Chung        mScrollIndicator.setTranslationX(indicatorPos);
17833ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung        mScrollIndicator.invalidate();
17843ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    }
17853ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung
17863ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    public void showScrollIndicatorTrack() {
17873ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    }
17883ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung
17893ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung    public void hideScrollIndicatorTrack() {
1790007c69867d821ea2b271398577a8b3440b3a7046Winson Chung    }
17916a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
17926a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    /* Accessibility */
17936a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    @Override
17946a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
17956a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        super.onInitializeAccessibilityNodeInfo(info);
17966a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        info.setScrollable(true);
17976a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    }
17986a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
17996a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    @Override
18006a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
18016a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        super.onInitializeAccessibilityEvent(event);
18026a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        event.setScrollable(true);
18036a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
18046a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung            event.setFromIndex(mCurrentPage);
18056a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung            event.setToIndex(mCurrentPage);
18066a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung            event.setItemCount(getChildCount());
18076a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        }
18086a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    }
18096a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
18106a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    @Override
18116a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
18126a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        // Do not append text content to scroll events they are fired frequently
18136a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        // and the client has already received another event type with the text.
18146a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) {
18156a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung            super.dispatchPopulateAccessibilityEvent(event);
18166a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        }
18176a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
18186a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        onPopulateAccessibilityEvent(event);
18196a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        return false;
18206a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    }
18216a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
18226a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    @Override
18236a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
18246a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        super.onPopulateAccessibilityEvent(event);
18256a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
18266a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
18276a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung            event.getText().add(getCurrentPageDescription());
18286a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        }
18296a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    }
18306a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung
18316a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    protected String getCurrentPageDescription() {
18326a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
18336a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung        return String.format(mContext.getString(R.string.default_scroll_format),
18346a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung                page + 1, getChildCount());
18356a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung    }
1836321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung}
1837