PagedView.java revision f8d2823d885ba682140aee1ae0504c1c5e67a24b
1321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/*
2321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Copyright (C) 2010 The Android Open Source Project
3321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
4321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * you may not use this file except in compliance with the License.
6321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * You may obtain a copy of the License at
7321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
8321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
10321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Unless required by applicable law or agreed to in writing, software
11321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * See the License for the specific language governing permissions and
14321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * limitations under the License.
15321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
16321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
17321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpackage com.android.launcher2;
18321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
190499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport java.util.ArrayList;
209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
21228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.Animator;
22228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorInflater;
23228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorListenerAdapter;
24228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.ObjectAnimator;
25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context;
269c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray;
27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas;
28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect;
29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel;
30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable;
31321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet;
325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode;
33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent;
34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker;
35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View;
36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration;
37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup;
38321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent;
39e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator;
405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable;
41321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller;
42321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
430499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R;
4480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
45321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/**
46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a
470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages"
48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
49321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup {
50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final String TAG = "PagedView";
510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final int INVALID_PAGE = -1;
52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
5386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    // the min drag distance for a fling to register, to prevent random page shifts
549cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_FLING = 25;
559cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    // The min drag distance to trigger a page shift (regardless of velocity)
569cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_MOVE = 200;
57321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
58e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int PAGE_SNAP_ANIMATION_DURATION = 550;
590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final float NANOTIME_DIV = 1000000000.0f;
600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
61e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final float OVERSCROLL_DAMP_FACTOR = 0.08f;
62e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int MINIMUM_SNAP_VELOCITY = 2200;
63e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static final int MIN_FLING_VELOCITY = 250;
6468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // the velocity at which a fling gesture will cause us to snap to the next page
660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mSnapVelocity = 500;
670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mSmoothingTime;
690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mTouchX;
70321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFirstLayout = true;
720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mCurrentPage;
740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mNextPage = INVALID_PAGE;
75a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung    protected int mRestorePage = -1;
7668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected int mMaxScrollX;
770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected Scroller mScroller;
78321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private VelocityTracker mVelocityTracker;
79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
80321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mDownMotionX;
817426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected float mLastMotionX;
827426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected float mLastMotionY;
83f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    private int mLastScreenCenter = -1;
84321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_REST = 0;
860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_SCROLLING = 1;
870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_PREV_PAGE = 2;
880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
89e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen    protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mTouchState = TOUCH_STATE_REST;
92321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected OnLongClickListener mLongClickListener;
94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
957426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected boolean mAllowLongPress = true;
96321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
977426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka    protected int mTouchSlop;
98321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mPagingTouchSlop;
99321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mMaximumVelocity;
1009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageSpacing;
1019c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingTop;
1029c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingBottom;
1039c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingLeft;
1049c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingRight;
105df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutWidthGap;
106df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutHeightGap;
1079c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mCellCountX;
1089c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mCellCountY;
1097da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung    protected boolean mCenterPagesVertically;
11068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected boolean mAllowOverScroll = true;
11168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected int mUnboundedScrollX;
112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1138c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // parameter that adjusts the layout to be optimized for pages with that scale factor
114d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    protected float mLayoutScale = 1.0f;
115d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
1165f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected static final int INVALID_POINTER = -1;
117321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1185f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected int mActivePointerId = INVALID_POINTER;
119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
12086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private PageSwitchListener mPageSwitchListener;
121321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
12286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private ArrayList<Boolean> mDirtyPageContent;
12386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private boolean mDirtyPageAlpha;
124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1255f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // choice modes
1265f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_NONE = 0;
1275f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_SINGLE = 1;
1285f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // Multiple selection mode is not supported by all Launcher actions atm
1295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_MULTIPLE = 2;
1309f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
131e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka    protected int mChoiceMode;
1325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private ActionMode mActionMode;
1335f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
1340499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung    // NOTE: This is a shared icon cache across all the PagedViews.  Currently it is only used in
1350499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung    // AllApps and Customize, and allows them to share holographic icons for the application view
1360499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung    // (which is in both).
1370499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung    protected static PagedViewIconCache mPageViewIconCache = new PagedViewIconCache();
138241c3b451d7841ba08247beea784953eca4e8582Winson Chung
1390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, syncPages and syncPageItems will be called to refresh pages
1400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mContentIsRefreshable = true;
1410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, modify alpha of neighboring pages as user scrolls left/right
1430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFadeInAdjacentScreens = true;
1440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // to switch to a new page
1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mUsePagingTouchSlop = true;
1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, the subclass should directly update mScrollX itself in its computeScroll method
1500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // (SmoothPagedView does this)
1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mDeferScrollUpdate = false;
1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1531262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected boolean mIsPageMoving = false;
1541262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
15586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public interface PageSwitchListener {
15686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        void onPageSwitch(View newPage, int newPageIndex);
157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context) {
160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, null);
161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs) {
164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, attrs, 0);
165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs, int defStyle) {
168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super(context, attrs, defStyle);
1695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        mChoiceMode = CHOICE_MODE_NONE;
170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1719c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
1729c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView, defStyle, 0);
1739c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0);
1749c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingTop = a.getDimensionPixelSize(
1759c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingTop, 10);
1769c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingBottom = a.getDimensionPixelSize(
1779c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingBottom, 10);
1789c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingLeft = a.getDimensionPixelSize(
1799c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingLeft, 10);
1809c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingRight = a.getDimensionPixelSize(
1819c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingRight, 10);
182df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutWidthGap = a.getDimensionPixelSize(
183df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung                R.styleable.PagedView_pageLayoutWidthGap, -1);
184df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutHeightGap = a.getDimensionPixelSize(
185df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung                R.styleable.PagedView_pageLayoutHeightGap, -1);
1869c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        a.recycle();
1879c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen
188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setHapticFeedbackEnabled(false);
1890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        init();
190321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Initializes various states for this workspace.
194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void init() {
19686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent = new ArrayList<Boolean>();
19786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent.ensureCapacity(32);
198e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        mScroller = new Scroller(getContext(), new ScrollInterpolator());
19986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = 0;
2007da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung        mCenterPagesVertically = true;
201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mTouchSlop = configuration.getScaledTouchSlop();
204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
20886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
20986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mPageSwitchListener = pageSwitchListener;
21086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
21186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
21686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Returns the index of the currently displayed page.
217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
21886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * @return The index of the currently displayed page.
219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
22086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getCurrentPage() {
22186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        return mCurrentPage;
222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
22486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getPageCount() {
225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildCount();
226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
22886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    View getPageAt(int index) {
229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildAt(index);
230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    int getScrollWidth() {
233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getWidth();
234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
236be9093426aec3a6d412811a12d2400e645613242Adam Cohen    public int getTouchState() {
237be9093426aec3a6d412811a12d2400e645613242Adam Cohen        return mTouchState;
238be9093426aec3a6d412811a12d2400e645613242Adam Cohen    }
239be9093426aec3a6d412811a12d2400e645613242Adam Cohen
240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
241bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * Updates the scroll of the current page immediately to its final scroll position.  We use this
242bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
243bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * the previous tab page.
244bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     */
245bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    protected void updateCurrentPageScroll() {
246bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
247bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        scrollTo(newX, 0);
248bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        mScroller.setFinalX(newX);
249bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    }
250bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung
251bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    /**
25286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Sets the current page.
253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
25486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    void setCurrentPage(int currentPage) {
25572e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        if (!mScroller.isFinished()) {
25672e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            mScroller.abortAnimation();
25772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
258d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
259d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // the default
260d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        if (getChildCount() == 0) {
26172e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            return;
26272e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
26486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
265bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        updateCurrentPageScroll();
26686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        notifyPageSwitchListener();
267a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung        invalidate();
268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void notifyPageSwitchListener() {
27186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
27286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2761262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    private void pageBeginMoving() {
2771262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        mIsPageMoving = true;
2781262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        onPageBeginMoving();
2791262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
2801262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
2811262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    private void pageEndMoving() {
2821262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        onPageEndMoving();
2831262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        mIsPageMoving = false;
2841262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
2851262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
2860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2871262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageBeginMoving() {
2880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2911262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageEndMoving() {
2920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
29586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Registers the specified listener on each page contained in this workspace.
296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @param l The listener used to respond to long clicks.
298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void setOnLongClickListener(OnLongClickListener l) {
301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mLongClickListener = l;
30286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        final int count = getPageCount();
303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < count; i++) {
30486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(i).setOnLongClickListener(l);
305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
30968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    public void scrollBy(int x, int y) {
31068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        scrollTo(mUnboundedScrollX + x, mScrollY + y);
31168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    }
31268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
31368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    @Override
3140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void scrollTo(int x, int y) {
31568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        mUnboundedScrollX = x;
31668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
31768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        if (x < 0) {
31868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(0, y);
31968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            if (mAllowOverScroll) {
32068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                overScroll(x);
32168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            }
32268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else if (x > mMaxScrollX) {
32368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(mMaxScrollX, y);
32468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            if (mAllowOverScroll) {
32568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                overScroll(x - mMaxScrollX);
32668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            }
32768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else {
32868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            super.scrollTo(x, y);
32968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        }
33068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
3310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mTouchX = x;
3320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
3330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // we moved this functionality to a helper function so SmoothPagedView can reuse it
3360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean computeScrollHelper() {
337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.computeScrollOffset()) {
3385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
3400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            invalidate();
3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
34286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        } else if (mNextPage != INVALID_PAGE) {
3435f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
34486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
34586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mNextPage = INVALID_PAGE;
3460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            notifyPageSwitchListener();
34773aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            // We don't want to trigger a page end moving unless the page has settled
34873aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            // and the user has stopped scrolling
34973aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            if (mTouchState == TOUCH_STATE_REST) {
35073aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen                pageEndMoving();
35173aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen            }
3520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
3540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        return false;
3550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    @Override
3580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void computeScroll() {
3590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        computeScrollHelper();
360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
361321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
362321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
365321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
366321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (widthMode != MeasureSpec.EXACTLY) {
367321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
368321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
369321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
3706b879f0a5885274a85333531e091283405d490ccAdam Lesinski        /* Allow the height to be set as WRAP_CONTENT. This allows the particular case
3716b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * of the All apps view on XLarge displays to not take up more space then it needs. Width
3726b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
3736b879f0a5885274a85333531e091283405d490ccAdam Lesinski         * each page to have the same width.
3746b879f0a5885274a85333531e091283405d490ccAdam Lesinski         */
375321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
3766b879f0a5885274a85333531e091283405d490ccAdam Lesinski        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
3776b879f0a5885274a85333531e091283405d490ccAdam Lesinski        int maxChildHeight = 0;
3786b879f0a5885274a85333531e091283405d490ccAdam Lesinski
3796b879f0a5885274a85333531e091283405d490ccAdam Lesinski        final int verticalPadding = mPaddingTop + mPaddingBottom;
380321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
381321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        // The children are given the same width and height as the workspace
3825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        // unless they were set to WRAP_CONTENT
383321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
384321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
3855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            // disallowing padding in paged view (just pass 0)
3865f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final View child = getChildAt(i);
3875f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
3885f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3895f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childWidthMode;
3905f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.width == LayoutParams.WRAP_CONTENT) {
3915f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.AT_MOST;
3925f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
3935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.EXACTLY;
3945f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
3955f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3965f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childHeightMode;
3975f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.height == LayoutParams.WRAP_CONTENT) {
3985f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.AT_MOST;
3995f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
4005f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.EXACTLY;
4015f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
4025f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4035f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childWidthMeasureSpec =
4045f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                MeasureSpec.makeMeasureSpec(widthSize, childWidthMode);
4055f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childHeightMeasureSpec =
4066b879f0a5885274a85333531e091283405d490ccAdam Lesinski                MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
4075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
4085f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4096b879f0a5885274a85333531e091283405d490ccAdam Lesinski            maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
4106b879f0a5885274a85333531e091283405d490ccAdam Lesinski        }
4116b879f0a5885274a85333531e091283405d490ccAdam Lesinski
4126b879f0a5885274a85333531e091283405d490ccAdam Lesinski        if (heightMode == MeasureSpec.AT_MOST) {
4136b879f0a5885274a85333531e091283405d490ccAdam Lesinski            heightSize = maxChildHeight + verticalPadding;
414321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
415faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        if (childCount > 0) {
416faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen            mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
417faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        } else {
418faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen            mMaxScrollX = 0;
419faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen        }
420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setMeasuredDimension(widthSize, heightSize);
422cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    }
423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
4248c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) {
425af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
426af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        int delta = newX - mScrollX;
427af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka
4288c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        final int pageCount = getChildCount();
4298c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        for (int i = 0; i < pageCount; i++) {
4308c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            View page = (View) getChildAt(i);
4318c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            page.setX(page.getX() + delta);
432af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        }
433af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka        setCurrentPage(newCurrentPage);
434af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka    }
435af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka
4368c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a
4378c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka    // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and
438d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    // tightens the layout accordingly
439d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    public void setLayoutScale(float childrenScale) {
440d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        mLayoutScale = childrenScale;
441d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
442d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // Now we need to do a re-layout, but preserving absolute X and Y coordinates
443d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        int childCount = getChildCount();
444d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        float childrenX[] = new float[childCount];
445d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        float childrenY[] = new float[childCount];
446d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        for (int i = 0; i < childCount; i++) {
447d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final View child = getChildAt(i);
448d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            childrenX[i] = child.getX();
449d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            childrenY[i] = child.getY();
450d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
451d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        onLayout(false, mLeft, mTop, mRight, mBottom);
452d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        for (int i = 0; i < childCount; i++) {
453d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final View child = getChildAt(i);
454d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            child.setX(childrenX[i]);
455d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            child.setY(childrenY[i]);
456d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
457d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // Also, the page offset has changed  (since the pages are now smaller);
458d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        // update the page offset, but again preserving absolute X and Y coordinates
4598c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        scrollToNewPageWithoutMovingPages(mCurrentPage);
460d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    }
461d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
462cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    @Override
46328750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
4645f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(false);
466cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
467cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            scrollTo(newX, 0);
468cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            mScroller.setFinalX(newX);
469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(true);
470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mFirstLayout = false;
471321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
4736b879f0a5885274a85333531e091283405d490ccAdam Lesinski        final int verticalPadding = mPaddingTop + mPaddingBottom;
474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int childLeft = 0;
476321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (childCount > 0) {
477e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            childLeft = getRelativeChildOffset(0);
478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
480321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final View child = getChildAt(i);
482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (child.getVisibility() != View.GONE) {
483d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                final int childWidth = getScaledMeasuredWidth(child);
4846b879f0a5885274a85333531e091283405d490ccAdam Lesinski                final int childHeight = child.getMeasuredHeight();
4856b879f0a5885274a85333531e091283405d490ccAdam Lesinski                int childTop = mPaddingTop;
4866b879f0a5885274a85333531e091283405d490ccAdam Lesinski                if (mCenterPagesVertically) {
4876b879f0a5885274a85333531e091283405d490ccAdam Lesinski                    childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
4886b879f0a5885274a85333531e091283405d490ccAdam Lesinski                }
489d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
4906b879f0a5885274a85333531e091283405d490ccAdam Lesinski                child.layout(childLeft, childTop,
491d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                        childLeft + child.getMeasuredWidth(), childTop + childHeight);
4929c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                childLeft += childWidth + mPageSpacing;
493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
494321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
495d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
496d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            mFirstLayout = false;
497d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        }
498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
499321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
500e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void updateAdjacentPagesAlpha() {
5010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mFadeInAdjacentScreens) {
5020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
503e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int halfScreenSize = getMeasuredWidth() / 2;
504e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int screenCenter = mScrollX + halfScreenSize;
5050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int childCount = getChildCount();
5060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < childCount; ++i) {
5070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    View layout = (View) getChildAt(i);
508d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                    int childWidth = getScaledMeasuredWidth(layout);
5090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int halfChildWidth = (childWidth / 2);
5100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childCenter = getChildOffset(i) + halfChildWidth;
511e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
512b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // On the first layout, we may not have a width nor a proper offset, so for now
513b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // we should just assume full page width (and calculate the offset according to
514b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // that).
515b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    if (childWidth <= 0) {
516b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                        childWidth = getMeasuredWidth();
517b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                        childCenter = (i * childWidth) + (childWidth / 2);
518b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    }
519b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung
520e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int d = halfChildWidth;
521e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int distanceFromScreenCenter = childCenter - screenCenter;
522e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    if (distanceFromScreenCenter > 0) {
523e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i > 0) {
524d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                            d += getScaledMeasuredWidth(getChildAt(i - 1)) / 2;
525e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
5260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
527e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i < childCount - 1) {
528d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka                            d += getScaledMeasuredWidth(getChildAt(i + 1)) / 2;
529e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
5300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
5319c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                    d += mPageSpacing;
532e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
533b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // Preventing potential divide-by-zero
534b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    d = Math.max(1, d);
535b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung
536e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d;
537e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
538e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float alpha = 1.0f - dimAlpha;
539e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
540f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    if (alpha < ALPHA_QUANTIZE_LEVEL) {
541f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                        alpha = 0.0f;
542f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) {
543f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                        alpha = 1.0f;
544f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    }
545f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
5460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (Float.compare(alpha, layout.getAlpha()) != 0) {
5470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        layout.setAlpha(alpha);
5480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
549affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung                }
5500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageAlpha = false;
551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
553e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
5540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
555f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    protected void screenScrolled(int screenCenter) {
556f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    }
557f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
558e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    @Override
559e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void dispatchDraw(Canvas canvas) {
560f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int halfScreenSize = getMeasuredWidth() / 2;
561f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int screenCenter = mScrollX + halfScreenSize;
562f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
563f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        if (screenCenter != mLastScreenCenter) {
564f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            screenScrolled(screenCenter);
565f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            updateAdjacentPagesAlpha();
566f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            mLastScreenCenter = screenCenter;
567f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        }
5680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
569e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        // Find out which screens are visible; as an optimization we only call draw on them
5700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // As an optimization, this code assumes that all pages have the same width as the 0th
5710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // page.
5720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int pageCount = getChildCount();
573c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka        if (pageCount > 0) {
574d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            final int pageWidth = getScaledMeasuredWidth(getChildAt(0));
575c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final int screenWidth = getMeasuredWidth();
576c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int x = getRelativeChildOffset(0) + pageWidth;
577c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int leftScreen = 0;
578c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int rightScreen = 0;
579c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            while (x <= mScrollX) {
580c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                leftScreen++;
581c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                x += getScaledMeasuredWidth(getChildAt(leftScreen)) + mPageSpacing;
582c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
583c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = leftScreen;
584c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung            while (x < mScrollX + screenWidth && rightScreen < pageCount) {
585c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                rightScreen++;
586c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                if (rightScreen < pageCount) {
587c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                    x += getScaledMeasuredWidth(getChildAt(rightScreen)) + mPageSpacing;
588c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung                }
589c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
590c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = Math.min(getChildCount() - 1, rightScreen);
5910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
592c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final long drawingTime = getDrawingTime();
59329d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            // Clip to the bounds
59429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.save();
59529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft,
59629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung                    mScrollY + mBottom - mTop);
59729d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung
598c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            for (int i = leftScreen; i <= rightScreen; i++) {
599c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                drawChild(canvas, getChildAt(i), drawingTime);
600c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
60129d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung            canvas.restore();
6020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
603321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
604321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
605321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
606321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
60786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
60886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page != mCurrentPage || !mScroller.isFinished()) {
60986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
610321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
611321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
612321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
613321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
614321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
61786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int focusablePage;
61886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mNextPage != INVALID_PAGE) {
61986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mNextPage;
620321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
62186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mCurrentPage;
622321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
62386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View v = getPageAt(focusablePage);
624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            v.requestFocus(direction, previouslyFocusedRect);
626321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
631321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean dispatchUnhandledMove(View focused, int direction) {
632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
63386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() > 0) {
63486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() - 1);
635321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
636321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT) {
63886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() < getPageCount() - 1) {
63986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() + 1);
640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return super.dispatchUnhandledMove(focused, direction);
644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
646321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
647321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
64886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
64986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(mCurrentPage).addFocusables(views, direction);
650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
651321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
65286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) {
65386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage - 1).addFocusables(views, direction);
654321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
655321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT){
65686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getPageCount() - 1) {
65786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage + 1).addFocusables(views, direction);
658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
661321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * If one of our descendant views decides that it could be focused now, only
66486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * pass that along if it's on the current page.
665321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
66686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This happens when live folders requery, and if they're off page, they
66786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * end up calling requestFocus, which pulls it on page.
668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
669321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
670321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void focusableViewAvailable(View focused) {
67186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View current = getPageAt(mCurrentPage);
672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        View v = focused;
673321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        while (true) {
674321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == current) {
675321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                super.focusableViewAvailable(focused);
676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
677321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == this) {
679321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
680321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent parent = v.getParent();
682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (parent instanceof View) {
683321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                v = (View)v.getParent();
684321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else {
685321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
686321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
688321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
690321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * {@inheritDoc}
692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (disallowIntercept) {
696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // We need to make sure to cancel our long press if
697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // a scrollable widget takes over touch events
69886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            final View currentPage = getChildAt(mCurrentPage);
69986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage.cancelLongPress();
700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestDisallowInterceptTouchEvent(disallowIntercept);
702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
704d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    /**
705d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     * Return true if a tap at (x, y) should trigger a flip to the previous page.
706d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     */
707d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    protected boolean hitsPreviousPage(float x, float y) {
708d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy        return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
709d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    }
710d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy
711d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    /**
712d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     * Return true if a tap at (x, y) should trigger a flip to the next page.
713d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy     */
714d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    protected boolean hitsNextPage(float x, float y) {
715d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy        return  (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
716d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy    }
717d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy
718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * This method JUST determines whether we want to intercept the motion.
722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * If we return true, onTouchEvent will be called and we do the actual
723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * scrolling there.
724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
72645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
72745e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
72845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
729321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
730321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Shortcut the most recurring case: the user is in the dragging
731321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * state and he is moving his finger.  We want to intercept this
732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * motion.
733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if ((action == MotionEvent.ACTION_MOVE) &&
736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                (mTouchState == TOUCH_STATE_SCROLLING)) {
737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
740321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
741321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_MOVE: {
742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * whether the user has moved far enough from his original down touch.
745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
7461ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                if (mActivePointerId != INVALID_POINTER) {
7471ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    determineScrollingStart(ev);
7481ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    break;
7491ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                }
7501ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
7511ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
7521ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // i.e. fall through to the next case (don't break)
7531ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
7541ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // while it's small- this was causing a crash before we checked for INVALID_POINTER)
755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_DOWN: {
758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX();
759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float y = ev.getY();
760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Remember location of down touch
761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mDownMotionX = x;
762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionY = y;
764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = ev.getPointerId(0);
765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = true;
766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * If being flinged and user touches the screen, initiate drag;
769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * otherwise don't.  mScroller.isFinished should be false when
770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * being flinged.
771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
772fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka                final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
7735f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
7745f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                if (finishedScrolling) {
7755f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_REST;
7765f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mScroller.abortAnimation();
7775f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                } else {
7785f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_SCROLLING;
7795f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
78186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                // check if this can be the beginning of a tap on the side of the pages
782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // to scroll the current page
783d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (getChildCount() > 0) {
785d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                        if (hitsPreviousPage(x, y)) {
786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_PREV_PAGE;
787d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy                        } else if (hitsNextPage(x, y)) {
788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_NEXT_PAGE;
789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        }
790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
791321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
793321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_UP:
7961d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown                onWallpaperTap(ev);
7971d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            case MotionEvent.ACTION_CANCEL:
798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_REST;
799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = INVALID_POINTER;
801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_POINTER_UP:
804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                onSecondaryPointerUp(ev);
805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * The only time we want to intercept motion events is if we are in the
810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * drag mode.
811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mTouchState != TOUCH_STATE_REST;
813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
81580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    protected void animateClickFeedback(View v, final Runnable r) {
81680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // animate the view slightly to show click feedback running some logic after it is "pressed"
817228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.
818228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung                loadAnimator(mContext, R.anim.paged_view_click_feedback);
819228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.setTarget(v);
820228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.addListener(new AnimatorListenerAdapter() {
821228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung            public void onAnimationRepeat(Animator animation) {
82280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                r.run();
82380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
82480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        });
825228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung        anim.start();
82680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
82780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
828f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void determineScrollingStart(MotionEvent ev) {
829f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        determineScrollingStart(ev, 1.0f);
830f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    }
831f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen
832321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /*
833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Determines if we should change the touch state to start scrolling after the
834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * user moves their touch point too far.
835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
836f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Locally do absolute value. mLastMotionX is set to the y value
839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * of the down event.
840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
841321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
842321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float x = ev.getX(pointerIndex);
843321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float y = ev.getY(pointerIndex);
844321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int xDiff = (int) Math.abs(x - mLastMotionX);
845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int yDiff = (int) Math.abs(y - mLastMotionY);
846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
847f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xPaged = xDiff > mPagingTouchSlop;
849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xMoved = xDiff > touchSlop;
850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean yMoved = yDiff > touchSlop;
851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
852f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        if (xMoved || xPaged || yMoved) {
8530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mUsePagingTouchSlop ? xPaged : xMoved) {
854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll if the user moved far enough along the X axis
855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_SCROLLING;
856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
8570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mTouchX = mScrollX;
8580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
8590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Either way, cancel any pending longpress
862f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            cancelCurrentPageLongPress();
863f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        }
864f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    }
865f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen
866f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen    protected void cancelCurrentPageLongPress() {
867f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen        if (mAllowLongPress) {
868f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            mAllowLongPress = false;
869f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // Try canceling the long press. It could also have been scheduled
870f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // by a distant descendant, so use the mAllowLongPress flag to block
871f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            // everything
872f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            final View currentPage = getPageAt(mCurrentPage);
873f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen            if (currentPage != null) {
874f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen                currentPage.cancelLongPress();
875321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
879e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // This curve determines how the effect of scrolling over the limits of the page dimishes
880e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // as the user pulls further and further from the bounds
881e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private float overScrollInfluenceCurve(float f) {
882e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f -= 1.0f;
883e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        return f * f * f + 1.0f;
884e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
885e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
88668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    protected void overScroll(float amount) {
887e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int screenSize = getMeasuredWidth();
888e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
889e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float f = (amount / screenSize);
890e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
891e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        if (f == 0) return;
892e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
893e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
8947bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        // Clamp this factor, f, to -1 < f < 1
8957bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        if (Math.abs(f) >= 1) {
8967bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen            f /= Math.abs(f);
8977bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen        }
8987bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen
899e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
90068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        if (amount < 0) {
90168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            mScrollX = overScrollAmount;
90268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        } else {
90368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen            mScrollX = mMaxScrollX + overScrollAmount;
90468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        }
90568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        invalidate();
90668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen    }
90768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen
908c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka    protected float maxOverScroll() {
909c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
910c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        // exceed). Used to find out how much extra wallpaper we need for the overscroll effect
911c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        float f = 1.0f;
912c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
913c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka        return OVERSCROLL_DAMP_FACTOR * f;
914c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka    }
915c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka
916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onTouchEvent(MotionEvent ev) {
91845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
91945e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onTouchEvent(ev);
92045e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
921b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        acquireVelocityTrackerAndAddMovement(ev);
922321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
924321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
925321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_DOWN:
927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            /*
928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * If being flinged and user touches, stop the fling. isFinished
929321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * will be false if being flinged.
930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             */
931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (!mScroller.isFinished()) {
932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mScroller.abortAnimation();
933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
935321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Remember where the motion event started
936321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mDownMotionX = mLastMotionX = ev.getX();
937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(0);
9380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
9390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
9400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_MOVE:
944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
945321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll to follow the motion event
946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
948321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int deltaX = (int) (mLastMotionX - x);
949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
95168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                if (deltaX != 0) {
95268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    mTouchX += deltaX;
95368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
95468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    if (!mDeferScrollUpdate) {
95568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                        scrollBy(deltaX, 0);
95668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                    } else {
95768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen                        invalidate();
958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    awakenScrollBars();
961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
962564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen            } else {
963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                determineScrollingStart(ev);
964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
966321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_UP:
968321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
969321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int activePointerId = mActivePointerId;
970321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(activePointerId);
971321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
972321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final VelocityTracker velocityTracker = mVelocityTracker;
973321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
974321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
9759cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                final int deltaX = (int) (x - mDownMotionX);
9769cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                boolean isfling = Math.abs(deltaX) > MIN_LENGTH_FOR_FLING;
9779cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE;
978321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
9790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int snapVelocity = mSnapVelocity;
9809cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                if ((isSignificantMove && deltaX > 0 ||
981e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen                        (isfling && velocityX > snapVelocity)) && mCurrentPage > 0) {
9820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    snapToPageWithVelocity(mCurrentPage - 1, velocityX);
9839cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                } else if ((isSignificantMove && deltaX < 0 ||
9849cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                        (isfling && velocityX < -snapVelocity)) &&
98586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                        mCurrentPage < getChildCount() - 1) {
9860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    snapToPageWithVelocity(mCurrentPage + 1, velocityX);
987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
989321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
990d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
992321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
993321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
99486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.max(0, mCurrentPage - 1);
99586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
99686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
998321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1000d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
1001321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
1002321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
1003321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
100486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
100586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
100686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
1008321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
10101d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            } else {
10111d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown                onWallpaperTap(ev);
1012321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1013321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
1014321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
1015b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1017321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1018321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_CANCEL:
1019b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
1020b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka                snapToDestination();
1021b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            }
1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
1024b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_POINTER_UP:
1028321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            onSecondaryPointerUp(ev);
1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
1030321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return true;
1033321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1034321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1035b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
1036b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker == null) {
1037b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = VelocityTracker.obtain();
1038b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
1039b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        mVelocityTracker.addMovement(ev);
1040b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
1041b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
1042b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void releaseVelocityTracker() {
1043b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker != null) {
1044b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker.recycle();
1045b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = null;
1046b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
1047b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
1048b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
1049321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void onSecondaryPointerUp(MotionEvent ev) {
1050321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1051321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1052321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerId = ev.getPointerId(pointerIndex);
1053321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (pointerId == mActivePointerId) {
1054321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // This was our active pointer going up. Choose a new
1055321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // active pointer and adjust accordingly.
1056321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // TODO: Make this decision more intelligent.
1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1058321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
1059321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionY = ev.getY(newPointerIndex);
1060321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(newPointerIndex);
1061321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mVelocityTracker != null) {
1062321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mVelocityTracker.clear();
1063321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1064321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
10651d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown        if (mTouchState == TOUCH_STATE_REST) {
10661d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown            onWallpaperTap(ev);
10671d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown        }
10681d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown    }
10691d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown
10701d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown    protected void onWallpaperTap(MotionEvent ev) {
1071321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1072321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestChildFocus(View child, View focused) {
1075321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestChildFocus(child, focused);
107686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
107786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page >= 0 && !isInTouchMode()) {
107886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
1079321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1080321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1081321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1082e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getChildIndexForRelativeOffset(int relativeOffset) {
1083e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int childCount = getChildCount();
10849c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int left;
10859c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int right;
1086e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        for (int i = 0; i < childCount; ++i) {
10879c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            left = getRelativeChildOffset(i);
1088d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            right = (left + getScaledMeasuredWidth(getChildAt(i)));
1089e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            if (left <= relativeOffset && relativeOffset <= right) {
1090e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                return i;
1091e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            }
1092e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        }
1093e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return -1;
1094e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1095e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
1096321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getRelativeChildOffset(int index) {
1097321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2;
1098321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1099321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getChildOffset(int index) {
1101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (getChildCount() == 0)
1102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return 0;
1103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1104321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int offset = getRelativeChildOffset(0);
1105321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < index; ++i) {
1106d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            offset += getScaledMeasuredWidth(getChildAt(i)) + mPageSpacing;
1107321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return offset;
1109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1110321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1111d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    protected int getScaledMeasuredWidth(View child) {
1112d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka        return (int) (child.getMeasuredWidth() * mLayoutScale + 0.5f);
1113d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka    }
1114d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka
1115d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    int getPageNearestToCenterOfScreen() {
1116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenter = getMeasuredWidth();
1117321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenterIndex = -1;
1118321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int screenCenter = mScrollX + (getMeasuredWidth() / 2);
1119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
1120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; ++i) {
11210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            View layout = (View) getChildAt(i);
1122d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka            int childWidth = getScaledMeasuredWidth(layout);
1123321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int halfChildWidth = (childWidth / 2);
1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childCenter = getChildOffset(i) + halfChildWidth;
1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
1127321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenter = distanceFromScreenCenter;
1128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenterIndex = i;
1129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1130321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1131d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        return minDistanceFromScreenCenterIndex;
1132d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    }
1133d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen
1134d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    protected void snapToDestination() {
1135d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
1136321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1137321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1138e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    private static class ScrollInterpolator implements Interpolator {
1139e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        public ScrollInterpolator() {
1140e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1141e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1142e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        public float getInterpolation(float t) {
1143e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            t -= 1.0f;
1144e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            return t*t*t*t*t + 1;
1145e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1146e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1147e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1148e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // We want the duration of the page snap animation to be influenced by the distance that
1149e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // the screen has to travel, however, we don't want this duration to be effected in a
1150e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
1151e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    // of travel has on the overall snap duration.
1152e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    float distanceInfluenceForSnapDuration(float f) {
1153e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f -= 0.5f; // center the values about 0.
1154e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        f *= 0.3f * Math.PI / 2.0f;
1155e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        return (float) Math.sin(f);
1156e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen    }
1157e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
11580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPageWithVelocity(int whichPage, int velocity) {
1159e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
1160e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int halfScreenSize = getMeasuredWidth() / 2;
1161e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1162e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
1163e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int delta = newX - mUnboundedScrollX;
1164e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        int duration = 0;
1165e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1166e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        if (Math.abs(velocity) < MIN_FLING_VELOCITY) {
1167e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            // If the velocity is low enough, then treat this more as an automatic page advance
1168e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            // as opposed to an apparent physical response to flinging
1169e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1170e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen            return;
1171e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        }
1172e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1173e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // Here we compute a "distance" that will be used in the computation of the overall
1174e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // snap duration. This is a function of the actual distance that needs to be traveled;
1175e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // we keep this value close to half screen size in order to reduce the variance in snap
1176e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // duration as a function of the distance the page needs to travel.
1177e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float distanceRatio = 1.0f * Math.abs(delta) / 2 * halfScreenSize;
1178e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        float distance = halfScreenSize + halfScreenSize *
1179e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen                distanceInfluenceForSnapDuration(distanceRatio);
1180e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1181e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        velocity = Math.abs(velocity);
1182e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
1183e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1184e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // we want the page's snap velocity to approximately match the velocity at which the
1185e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // user flings, so we scale the duration by a value near to the derivative of the scroll
1186e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        // interpolator at zero, ie. 5. We use 6 to make it a little slower.
1187e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        duration = 6 * Math.round(1000 * Math.abs(distance / velocity));
1188e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen
1189e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen        snapToPage(whichPage, delta, duration);
11900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
11910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
11920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage) {
11935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
11960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int duration) {
119786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
1198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
119986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
120068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        final int sX = mUnboundedScrollX;
1201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int delta = newX - sX;
12020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage, delta, duration);
12030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
12040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
12050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int delta, int duration) {
12060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mNextPage = whichPage;
12070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
12080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        View focusedChild = getFocusedChild();
12090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (focusedChild != null && whichPage != mCurrentPage &&
12100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                focusedChild == getChildAt(mCurrentPage)) {
12110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            focusedChild.clearFocus();
12120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
12130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
12140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        pageBeginMoving();
1215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        awakenScrollBars(duration);
1216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (duration == 0) {
1217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            duration = Math.abs(delta);
1218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
122168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen        mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
122280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
122380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // only load some associated pages
122486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        loadAssociatedPages(mNextPage);
12250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        notifyPageSwitchListener();
1226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
1227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollLeft() {
1230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
123186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
1232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
123386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage > 0) snapToPage(mNextPage - 1);
1234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollRight() {
1238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
123986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
1240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
124186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
1242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
124586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public int getPageForView(View v) {
1246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int result = -1;
1247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
1248321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent vp = v.getParent();
1249321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int count = getChildCount();
1250321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            for (int i = 0; i < count; i++) {
1251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (vp == getChildAt(i)) {
1252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    return i;
1253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return result;
1257321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1258321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @return True is long presses are still allowed for the current touch
1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean allowLongPress() {
1263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mAllowLongPress;
1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
12660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
12670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * Set true to allow long-press events to be triggered, usually checked by
12680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * {@link Launcher} to accept or block dpad-initiated long-presses.
12690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     */
12700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setAllowLongPress(boolean allowLongPress) {
12710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mAllowLongPress = allowLongPress;
12720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
12730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public static class SavedState extends BaseSavedState {
127586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int currentPage = -1;
1276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState(Parcelable superState) {
1278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(superState);
1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        private SavedState(Parcel in) {
1282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(in);
128386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage = in.readInt();
1284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        @Override
1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public void writeToParcel(Parcel out, int flags) {
1288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super.writeToParcel(out, flags);
128986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            out.writeInt(currentPage);
1290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public static final Parcelable.Creator<SavedState> CREATOR =
1293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                new Parcelable.Creator<SavedState>() {
1294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState createFromParcel(Parcel in) {
1295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState(in);
1296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState[] newArray(int size) {
1299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState[size];
1300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        };
1302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
130486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void loadAssociatedPages(int page) {
13050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
13060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
13070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (page < count) {
1308e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int lowerPageBound = getAssociatedLowerPageBound(page);
1309e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int upperPageBound = getAssociatedUpperPageBound(page);
13100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < count; ++i) {
13110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    final ViewGroup layout = (ViewGroup) getChildAt(i);
13120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    final int childCount = layout.getChildCount();
13130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (lowerPageBound <= i && i <= upperPageBound) {
13140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (mDirtyPageContent.get(i)) {
13150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            syncPageItems(i);
13160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            mDirtyPageContent.set(i, false);
13170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
13180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
13190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (childCount > 0) {
13200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            layout.removeAllViews();
13210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
13220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mDirtyPageContent.set(i, true);
132380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                    }
132480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                }
132580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
132680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        }
132780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
132880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1329e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedLowerPageBound(int page) {
1330e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.max(0, page - 1);
1331e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1332e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedUpperPageBound(int page) {
1333e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int count = getChildCount();
1334e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.min(page + 1, count - 1);
1335e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1336e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
13375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void startChoiceMode(int mode, ActionMode.Callback callback) {
1338430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        if (isChoiceMode(CHOICE_MODE_NONE)) {
1339430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mChoiceMode = mode;
1340430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mActionMode = startActionMode(callback);
1341430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        }
13425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
13435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
13442b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public void endChoiceMode() {
13455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        if (!isChoiceMode(CHOICE_MODE_NONE)) {
13465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            mChoiceMode = CHOICE_MODE_NONE;
13475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            resetCheckedGrandchildren();
1348e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka            if (mActionMode != null) mActionMode.finish();
13499f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            mActionMode = null;
13505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
13515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
13525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
13535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected boolean isChoiceMode(int mode) {
13545f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return mChoiceMode == mode;
13555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
13565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
13575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected ArrayList<Checkable> getCheckedGrandchildren() {
13585f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        ArrayList<Checkable> checked = new ArrayList<Checkable>();
13595f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final int childCount = getChildCount();
13605f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < childCount; ++i) {
1361d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung            final ViewGroup layout = (ViewGroup) getChildAt(i);
13625f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final int grandChildCount = layout.getChildCount();
13635f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            for (int j = 0; j < grandChildCount; ++j) {
13645f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final View v = layout.getChildAt(j);
13659f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                if (v instanceof Checkable && ((Checkable) v).isChecked()) {
13665f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    checked.add((Checkable) v);
13675f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
13685f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            }
13695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
13705f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return checked;
13715f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
13725f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
13739f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    /**
13749f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * If in CHOICE_MODE_SINGLE and an item is checked, returns that item.
13759f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * Otherwise, returns null.
13769f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     */
13779f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    protected Checkable getSingleCheckedGrandchild() {
13789f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        if (mChoiceMode == CHOICE_MODE_SINGLE) {
13799f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            final int childCount = getChildCount();
13809f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            for (int i = 0; i < childCount; ++i) {
1381d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung                final ViewGroup layout = (ViewGroup) getChildAt(i);
13829f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                final int grandChildCount = layout.getChildCount();
13839f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                for (int j = 0; j < grandChildCount; ++j) {
13849f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    final View v = layout.getChildAt(j);
13859f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    if (v instanceof Checkable && ((Checkable) v).isChecked()) {
13869f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                        return (Checkable) v;
13879f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    }
13889f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                }
13899f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            }
13909f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        }
13919f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        return null;
13929f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    }
13939f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
13942b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public Object getChosenItem() {
13952b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        View checkedView = (View) getSingleCheckedGrandchild();
13962b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        if (checkedView != null) {
13972b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            return checkedView.getTag();
13982b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        }
13992b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        return null;
14002b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    }
14012b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy
14025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void resetCheckedGrandchildren() {
14035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        // loop through children, and set all of their children to _not_ be checked
14045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final ArrayList<Checkable> checked = getCheckedGrandchildren();
14055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < checked.size(); ++i) {
14065f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final Checkable c = checked.get(i);
14075f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            c.setChecked(false);
14085f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
14095f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
14105f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
1411a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung    public void setRestorePage(int restorePage) {
1412a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung        mRestorePage = restorePage;
1413a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung    }
1414a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung
141586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
141686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called ONLY to synchronize the number of pages that the paged view has.
141786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * To actually fill the pages with information, implement syncPageItems() below.  It is
141886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * guaranteed that syncPageItems() will be called for a particular page before it is shown,
141986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * and therefore, individual page items do not need to be updated in this method.
142086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPages();
142286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
142386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
142486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called to synchronize the items that are on a particular page.  If views on
142586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * the page can be reused, then they should be updated within this method.
142686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPageItems(int page);
142886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
1429321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void invalidatePageData() {
14300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
14310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Update all the pages
14320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            syncPages();
143386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
14340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Mark each of the pages as dirty
14350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
14360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageContent.clear();
14370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            for (int i = 0; i < count; ++i) {
14380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageContent.add(true);
14390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
144086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
1441a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung            // Use the restore page if necessary
1442a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung            if (mRestorePage > -1) {
1443a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung                mCurrentPage = mRestorePage;
1444a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung                mRestorePage = -1;
1445a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung            }
1446a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung
14470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Load any pages that are necessary for the current window of views
14480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            loadAssociatedPages(mCurrentPage);
14490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageAlpha = true;
1450b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung            updateAdjacentPagesAlpha();
14510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            requestLayout();
14520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
1453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung}
1455