PagedView.java revision bbc60d8e79416e37cbede55c159bf6aaa6c171d5
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
19e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport java.util.ArrayList;
20e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport java.util.HashMap;
219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
22321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context;
239c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray;
24241c3b451d7841ba08247beea784953eca4e8582Winson Chungimport android.graphics.Bitmap;
25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas;
26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect;
27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel;
28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable;
29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet;
30e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.util.Log;
315f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode;
32321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent;
33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker;
34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View;
35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration;
36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup;
37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent;
3880baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation;
395f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.view.animation.Animation.AnimationListener;
40e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.view.animation.AnimationUtils;
415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable;
42e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.widget.LinearLayout;
43321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller;
44321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
45e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport com.android.launcher.R;
4680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/**
48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a
490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages"
50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
51321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup {
52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final String TAG = "PagedView";
530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final int INVALID_PAGE = -1;
54321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
5586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    // the min drag distance for a fling to register, to prevent random page shifts
569cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_FLING = 25;
579cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    // The min drag distance to trigger a page shift (regardless of velocity)
589cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung    private static final int MIN_LENGTH_FOR_MOVE = 200;
59321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
605f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private static final int PAGE_SNAP_ANIMATION_DURATION = 1000;
610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final float NANOTIME_DIV = 1000000000.0f;
620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // the velocity at which a fling gesture will cause us to snap to the next page
640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mSnapVelocity = 500;
650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mSmoothingTime;
670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mTouchX;
68321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFirstLayout = true;
700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mCurrentPage;
720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mNextPage = INVALID_PAGE;
730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected Scroller mScroller;
74321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private VelocityTracker mVelocityTracker;
75321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
76321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mDownMotionX;
77321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mLastMotionX;
78321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mLastMotionY;
79f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    private int mLastScreenCenter = -1;
80321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_REST = 0;
820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_SCROLLING = 1;
830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_PREV_PAGE = 2;
840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
85e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen    protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mTouchState = TOUCH_STATE_REST;
88321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected OnLongClickListener mLongClickListener;
90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
91321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private boolean mAllowLongPress = true;
92321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
93321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mTouchSlop;
94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mPagingTouchSlop;
95321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mMaximumVelocity;
969c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageSpacing;
979c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingTop;
989c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingBottom;
999c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingLeft;
1009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mPageLayoutPaddingRight;
101df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutWidthGap;
102df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung    protected int mPageLayoutHeightGap;
1039c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mCellCountX;
1049c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen    protected int mCellCountY;
1057da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung    protected boolean mCenterPagesVertically;
106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected static final int INVALID_POINTER = -1;
108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1095f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected int mActivePointerId = INVALID_POINTER;
110321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
11186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private PageSwitchListener mPageSwitchListener;
112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
11386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private ArrayList<Boolean> mDirtyPageContent;
11486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private boolean mDirtyPageAlpha;
115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // choice modes
1175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_NONE = 0;
1185f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_SINGLE = 1;
1195f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // Multiple selection mode is not supported by all Launcher actions atm
1205f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_MULTIPLE = 2;
1219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
122e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka    protected int mChoiceMode;
1235f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private ActionMode mActionMode;
1245f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
125241c3b451d7841ba08247beea784953eca4e8582Winson Chung    protected PagedViewIconCache mPageViewIconCache;
126241c3b451d7841ba08247beea784953eca4e8582Winson Chung
1270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, syncPages and syncPageItems will be called to refresh pages
1280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mContentIsRefreshable = true;
1290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, modify alpha of neighboring pages as user scrolls left/right
1310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFadeInAdjacentScreens = true;
1320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
1340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // to switch to a new page
1350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mUsePagingTouchSlop = true;
1360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, the subclass should directly update mScrollX itself in its computeScroll method
1380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // (SmoothPagedView does this)
1390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mDeferScrollUpdate = false;
1400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1411262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected boolean mIsPageMoving = false;
1421262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
143241c3b451d7841ba08247beea784953eca4e8582Winson Chung    /**
144241c3b451d7841ba08247beea784953eca4e8582Winson Chung     * Simple cache mechanism for PagedViewIcon outlines.
145241c3b451d7841ba08247beea784953eca4e8582Winson Chung     */
146241c3b451d7841ba08247beea784953eca4e8582Winson Chung    class PagedViewIconCache {
147241c3b451d7841ba08247beea784953eca4e8582Winson Chung        private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>();
148241c3b451d7841ba08247beea784953eca4e8582Winson Chung
149241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void clear() {
150241c3b451d7841ba08247beea784953eca4e8582Winson Chung            iconOutlineCache.clear();
151241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
152241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void addOutline(Object key, Bitmap b) {
153241c3b451d7841ba08247beea784953eca4e8582Winson Chung            iconOutlineCache.put(key, b);
154241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
155241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void removeOutline(Object key) {
156241c3b451d7841ba08247beea784953eca4e8582Winson Chung            if (iconOutlineCache.containsKey(key)) {
157241c3b451d7841ba08247beea784953eca4e8582Winson Chung                iconOutlineCache.remove(key);
158241c3b451d7841ba08247beea784953eca4e8582Winson Chung            }
159241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
160241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public Bitmap getOutline(Object key) {
161241c3b451d7841ba08247beea784953eca4e8582Winson Chung            return iconOutlineCache.get(key);
162241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
163241c3b451d7841ba08247beea784953eca4e8582Winson Chung    }
164241c3b451d7841ba08247beea784953eca4e8582Winson Chung
16586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public interface PageSwitchListener {
16686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        void onPageSwitch(View newPage, int newPageIndex);
167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context) {
170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, null);
171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs) {
174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, attrs, 0);
175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
176321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
177321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs, int defStyle) {
178321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super(context, attrs, defStyle);
1795f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        mChoiceMode = CHOICE_MODE_NONE;
180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1819c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
1829c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView, defStyle, 0);
1839c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0);
1849c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingTop = a.getDimensionPixelSize(
1859c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingTop, 10);
1869c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingBottom = a.getDimensionPixelSize(
1879c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingBottom, 10);
1889c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingLeft = a.getDimensionPixelSize(
1899c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingLeft, 10);
1909c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        mPageLayoutPaddingRight = a.getDimensionPixelSize(
1919c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                R.styleable.PagedView_pageLayoutPaddingRight, 10);
192df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutWidthGap = a.getDimensionPixelSize(
193df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung                R.styleable.PagedView_pageLayoutWidthGap, -1);
194df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        mPageLayoutHeightGap = a.getDimensionPixelSize(
195df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung                R.styleable.PagedView_pageLayoutHeightGap, -1);
1969c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        a.recycle();
1979c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen
198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setHapticFeedbackEnabled(false);
1990142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        init();
200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Initializes various states for this workspace.
204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
2050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void init() {
20686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent = new ArrayList<Boolean>();
20786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent.ensureCapacity(32);
208241c3b451d7841ba08247beea784953eca4e8582Winson Chung        mPageViewIconCache = new PagedViewIconCache();
209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mScroller = new Scroller(getContext());
21086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = 0;
2117da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung        mCenterPagesVertically = true;
212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mTouchSlop = configuration.getScaledTouchSlop();
215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
21986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
22086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mPageSwitchListener = pageSwitchListener;
22186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
22286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
224321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
22786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Returns the index of the currently displayed page.
228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
22986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * @return The index of the currently displayed page.
230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
23186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getCurrentPage() {
23286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        return mCurrentPage;
233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
23586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getPageCount() {
236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildCount();
237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
23986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    View getPageAt(int index) {
240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildAt(index);
241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    int getScrollWidth() {
244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getWidth();
245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
248bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * Updates the scroll of the current page immediately to its final scroll position.  We use this
249bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
250bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     * the previous tab page.
251bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung     */
252bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    protected void updateCurrentPageScroll() {
253bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
254bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        scrollTo(newX, 0);
255bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        mScroller.setFinalX(newX);
256bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    }
257bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung
258bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    /**
25986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Sets the current page.
260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
26186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    void setCurrentPage(int currentPage) {
26272e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        if (!mScroller.isFinished()) {
26372e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            mScroller.abortAnimation();
26472e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
26572e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        if (getChildCount() == 0 || currentPage == mCurrentPage) {
26672e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy            return;
26772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy        }
268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
26986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
270bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        updateCurrentPageScroll();
27180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
27386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        notifyPageSwitchListener();
274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void notifyPageSwitchListener() {
27786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
27886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2821262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    private void pageBeginMoving() {
2831262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        mIsPageMoving = true;
2841262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        onPageBeginMoving();
2851262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
2861262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
2871262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    private void pageEndMoving() {
2881262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        onPageEndMoving();
2891262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        mIsPageMoving = false;
2901262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
2911262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
2920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2931262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageBeginMoving() {
2940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2971262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onPageEndMoving() {
2980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2990142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
30186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Registers the specified listener on each page contained in this workspace.
302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @param l The listener used to respond to long clicks.
304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void setOnLongClickListener(OnLongClickListener l) {
307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mLongClickListener = l;
30886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        final int count = getPageCount();
309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < count; i++) {
31086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(i).setOnLongClickListener(l);
311321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
312321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
313321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
314321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
3150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void scrollTo(int x, int y) {
3160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        super.scrollTo(x, y);
3170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mTouchX = x;
3180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
3190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // we moved this functionality to a helper function so SmoothPagedView can reuse it
3220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean computeScrollHelper() {
323321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.computeScrollOffset()) {
3245f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
3260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            invalidate();
3270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
32886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        } else if (mNextPage != INVALID_PAGE) {
3295f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
33086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
33186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mNextPage = INVALID_PAGE;
3320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            notifyPageSwitchListener();
3330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            pageEndMoving();
3340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
335321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
3360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        return false;
3370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    @Override
3400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void computeScroll() {
3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        computeScrollHelper();
342321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
344321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
348321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (widthMode != MeasureSpec.EXACTLY) {
349321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
350321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
352321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (heightMode != MeasureSpec.EXACTLY) {
355321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
357321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
358321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        // The children are given the same width and height as the workspace
3595f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        // unless they were set to WRAP_CONTENT
360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
361321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
3625f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            // disallowing padding in paged view (just pass 0)
3635f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final View child = getChildAt(i);
3645f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
3655f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3665f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childWidthMode;
3675f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.width == LayoutParams.WRAP_CONTENT) {
3685f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.AT_MOST;
3695f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
3705f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.EXACTLY;
3715f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
3725f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3735f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childHeightMode;
3745f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.height == LayoutParams.WRAP_CONTENT) {
3755f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.AT_MOST;
3765f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
3775f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.EXACTLY;
3785f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
3795f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3805f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childWidthMeasureSpec =
3815f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                MeasureSpec.makeMeasureSpec(widthSize, childWidthMode);
3825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childHeightMeasureSpec =
3835f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                MeasureSpec.makeMeasureSpec(heightSize, childHeightMode);
3845f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
386321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
388321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setMeasuredDimension(widthSize, heightSize);
389cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    }
390321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
391cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    @Override
39228750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
3935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
394321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(false);
395cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
396cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            scrollTo(newX, 0);
397cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            mScroller.setFinalX(newX);
398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(true);
399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mFirstLayout = false;
400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
402321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
403321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int childLeft = 0;
404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (childCount > 0) {
405e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            childLeft = getRelativeChildOffset(0);
406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
407321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
408321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
409321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final View child = getChildAt(i);
410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (child.getVisibility() != View.GONE) {
411321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int childWidth = child.getMeasuredWidth();
4127da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung                final int childHeight = (mCenterPagesVertically ?
4137da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung                        (getMeasuredHeight() - child.getMeasuredHeight()) / 2 : 0);
4145f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                child.layout(childLeft, childHeight,
4155f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                        childLeft + childWidth, childHeight + child.getMeasuredHeight());
4169c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                childLeft += childWidth + mPageSpacing;
417321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
418321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
421e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void updateAdjacentPagesAlpha() {
4220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mFadeInAdjacentScreens) {
4230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
424e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int halfScreenSize = getMeasuredWidth() / 2;
425e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int screenCenter = mScrollX + halfScreenSize;
4260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int childCount = getChildCount();
4270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < childCount; ++i) {
4280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    View layout = (View) getChildAt(i);
4290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childWidth = layout.getMeasuredWidth();
4300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int halfChildWidth = (childWidth / 2);
4310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childCenter = getChildOffset(i) + halfChildWidth;
432e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
433b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // On the first layout, we may not have a width nor a proper offset, so for now
434b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // we should just assume full page width (and calculate the offset according to
435b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // that).
436b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    if (childWidth <= 0) {
437b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                        childWidth = getMeasuredWidth();
438b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                        childCenter = (i * childWidth) + (childWidth / 2);
439b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    }
440b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung
441e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int d = halfChildWidth;
442e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int distanceFromScreenCenter = childCenter - screenCenter;
443e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    if (distanceFromScreenCenter > 0) {
444e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i > 0) {
445e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                            d += getChildAt(i - 1).getMeasuredWidth() / 2;
446e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
4470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
448e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i < childCount - 1) {
449e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                            d += getChildAt(i + 1).getMeasuredWidth() / 2;
450e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
4510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
4529c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                    d += mPageSpacing;
453e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
454b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    // Preventing potential divide-by-zero
455b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                    d = Math.max(1, d);
456b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung
457e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d;
458e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
459e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float alpha = 1.0f - dimAlpha;
460e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
461f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    if (alpha < ALPHA_QUANTIZE_LEVEL) {
462f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                        alpha = 0.0f;
463f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) {
464f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                        alpha = 1.0f;
465f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    }
466f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
4670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (Float.compare(alpha, layout.getAlpha()) != 0) {
4680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        layout.setAlpha(alpha);
4690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
470affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung                }
4710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageAlpha = false;
472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
474e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
4750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
476f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    protected void screenScrolled(int screenCenter) {
477f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    }
478f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
479e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    @Override
480e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void dispatchDraw(Canvas canvas) {
481f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int halfScreenSize = getMeasuredWidth() / 2;
482f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int screenCenter = mScrollX + halfScreenSize;
483f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
484f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        if (screenCenter != mLastScreenCenter) {
485f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            screenScrolled(screenCenter);
486f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            updateAdjacentPagesAlpha();
487f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            mLastScreenCenter = screenCenter;
488f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        }
4890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
490e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        // Find out which screens are visible; as an optimization we only call draw on them
4910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // As an optimization, this code assumes that all pages have the same width as the 0th
4920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // page.
4930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int pageCount = getChildCount();
494c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka        if (pageCount > 0) {
495c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final int pageWidth = getChildAt(0).getMeasuredWidth();
496c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final int screenWidth = getMeasuredWidth();
497c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int x = getRelativeChildOffset(0) + pageWidth;
498c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int leftScreen = 0;
499c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int rightScreen = 0;
500c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            while (x <= mScrollX) {
501c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                leftScreen++;
5029c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                x += pageWidth + mPageSpacing;
503c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // replace above line with this if you don't assume all pages have same width as 0th
504c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // page:
505c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // x += getChildAt(leftScreen).getMeasuredWidth();
506c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
507c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = leftScreen;
508c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            while (x < mScrollX + screenWidth) {
509c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                rightScreen++;
5109c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                x += pageWidth + mPageSpacing;
511c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // replace above line with this if you don't assume all pages have same width as 0th
512c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // page:
513c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                //if (rightScreen < pageCount) {
514c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                //    x += getChildAt(rightScreen).getMeasuredWidth();
515c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                //}
516c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
517c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = Math.min(getChildCount() - 1, rightScreen);
5180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
519c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final long drawingTime = getDrawingTime();
520c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            for (int i = leftScreen; i <= rightScreen; i++) {
521c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                drawChild(canvas, getChildAt(i), drawingTime);
522c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
5230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
524321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
52886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
52986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page != mCurrentPage || !mScroller.isFinished()) {
53086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
536321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
537321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
53886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int focusablePage;
53986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mNextPage != INVALID_PAGE) {
54086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mNextPage;
541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
54286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mCurrentPage;
543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
54486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View v = getPageAt(focusablePage);
545321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
546321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            v.requestFocus(direction, previouslyFocusedRect);
547321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
548321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
549321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
550321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean dispatchUnhandledMove(View focused, int direction) {
553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
55486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() > 0) {
55586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() - 1);
556321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
557321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
558321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT) {
55986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() < getPageCount() - 1) {
56086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() + 1);
561321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
562321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
563321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return super.dispatchUnhandledMove(focused, direction);
565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
568321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
56986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
57086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(mCurrentPage).addFocusables(views, direction);
571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
57386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) {
57486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage - 1).addFocusables(views, direction);
575321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
576321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT){
57786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getPageCount() - 1) {
57886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage + 1).addFocusables(views, direction);
579321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
580321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
581321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
582321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
583321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
584321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * If one of our descendant views decides that it could be focused now, only
58586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * pass that along if it's on the current page.
586321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
58786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This happens when live folders requery, and if they're off page, they
58886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * end up calling requestFocus, which pulls it on page.
589321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
590321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
591321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void focusableViewAvailable(View focused) {
59286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View current = getPageAt(mCurrentPage);
593321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        View v = focused;
594321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        while (true) {
595321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == current) {
596321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                super.focusableViewAvailable(focused);
597321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
598321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
599321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == this) {
600321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
601321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
602321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent parent = v.getParent();
603321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (parent instanceof View) {
604321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                v = (View)v.getParent();
605321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else {
606321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
607321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
608321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
609321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
610321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
611321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
612321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * {@inheritDoc}
613321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
614321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (disallowIntercept) {
617321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // We need to make sure to cancel our long press if
618321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // a scrollable widget takes over touch events
61986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            final View currentPage = getChildAt(mCurrentPage);
62086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage.cancelLongPress();
621321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
622321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestDisallowInterceptTouchEvent(disallowIntercept);
623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
626321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * This method JUST determines whether we want to intercept the motion.
629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * If we return true, onTouchEvent will be called and we do the actual
630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * scrolling there.
631321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
63345e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
63445e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
63545e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
636321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Shortcut the most recurring case: the user is in the dragging
638321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * state and he is moving his finger.  We want to intercept this
639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * motion.
640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if ((action == MotionEvent.ACTION_MOVE) &&
643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                (mTouchState == TOUCH_STATE_SCROLLING)) {
644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
646321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
647321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
648321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_MOVE: {
649321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
651321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * whether the user has moved far enough from his original down touch.
652321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
6531ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                if (mActivePointerId != INVALID_POINTER) {
6541ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    determineScrollingStart(ev);
6551ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    break;
6561ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                }
6571ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
6581ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
6591ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // i.e. fall through to the next case (don't break)
6601ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
6611ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // while it's small- this was causing a crash before we checked for INVALID_POINTER)
662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_DOWN: {
665321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX();
666321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float y = ev.getY();
667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Remember location of down touch
668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mDownMotionX = x;
669321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
670321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionY = y;
671321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = ev.getPointerId(0);
672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = true;
673321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
674321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
675321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * If being flinged and user touches the screen, initiate drag;
676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * otherwise don't.  mScroller.isFinished should be false when
677321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * being flinged.
678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
679fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka                final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
6805f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
6815f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                if (finishedScrolling) {
6825f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_REST;
6835f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mScroller.abortAnimation();
6845f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                } else {
6855f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_SCROLLING;
6865f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
68886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                // check if this can be the beginning of a tap on the side of the pages
689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // to scroll the current page
69021f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen                if ((mTouchState != TOUCH_STATE_PREV_PAGE) && !handlePagingClicks() &&
691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        (mTouchState != TOUCH_STATE_NEXT_PAGE)) {
692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (getChildCount() > 0) {
693e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                        int width = getMeasuredWidth();
694e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                        int offset = getRelativeChildOffset(mCurrentPage);
69521f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen                        if (x < offset - mPageSpacing) {
696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_PREV_PAGE;
69721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen                        } else if (x > (width - offset + mPageSpacing)) {
698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_NEXT_PAGE;
699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        }
700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_CANCEL:
706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_UP:
707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_REST;
708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = INVALID_POINTER;
710321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
712321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_POINTER_UP:
713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                onSecondaryPointerUp(ev);
714321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
715321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
716321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
717321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * The only time we want to intercept motion events is if we are in the
719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * drag mode.
720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mTouchState != TOUCH_STATE_REST;
722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
72480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    protected void animateClickFeedback(View v, final Runnable r) {
72580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // animate the view slightly to show click feedback running some logic after it is "pressed"
72680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        Animation anim = AnimationUtils.loadAnimation(getContext(),
72780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                R.anim.paged_view_click_feedback);
72880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        anim.setAnimationListener(new AnimationListener() {
72980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
73080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationStart(Animation animation) {}
73180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
73280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationRepeat(Animation animation) {
73380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                r.run();
73480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
73580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
73680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationEnd(Animation animation) {}
73780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        });
73880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        v.startAnimation(anim);
73980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
74080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
741321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /*
742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Determines if we should change the touch state to start scrolling after the
743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * user moves their touch point too far.
744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
7451adf5391a3a3d215b226adf3702019c22a99e3b1Michael Jurka    protected void determineScrollingStart(MotionEvent ev) {
746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Locally do absolute value. mLastMotionX is set to the y value
748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * of the down event.
749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float x = ev.getX(pointerIndex);
752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float y = ev.getY(pointerIndex);
753321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int xDiff = (int) Math.abs(x - mLastMotionX);
754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int yDiff = (int) Math.abs(y - mLastMotionY);
755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int touchSlop = mTouchSlop;
757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xPaged = xDiff > mPagingTouchSlop;
758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xMoved = xDiff > touchSlop;
759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean yMoved = yDiff > touchSlop;
760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (xMoved || yMoved) {
7620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mUsePagingTouchSlop ? xPaged : xMoved) {
763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll if the user moved far enough along the X axis
764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_SCROLLING;
765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
7660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mTouchX = mScrollX;
7670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
7680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Either way, cancel any pending longpress
771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mAllowLongPress) {
772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Try canceling the long press. It could also have been scheduled
774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // by a distant descendant, so use the mAllowLongPress flag to block
775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // everything
77686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                final View currentPage = getPageAt(mCurrentPage);
77710fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung                if (currentPage != null) {
77810fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung                    currentPage.cancelLongPress();
77910fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung                }
780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
78421f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen    protected boolean handlePagingClicks() {
78521f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen        return false;
78621f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen    }
78721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen
788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onTouchEvent(MotionEvent ev) {
79045e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        // Skip touch handling if there are no pages to swipe
79145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung        if (getChildCount() <= 0) return super.onTouchEvent(ev);
79245e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung
793b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        acquireVelocityTrackerAndAddMovement(ev);
794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_DOWN:
799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            /*
800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * If being flinged and user touches, stop the fling. isFinished
801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * will be false if being flinged.
802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             */
803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (!mScroller.isFinished()) {
804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mScroller.abortAnimation();
805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Remember where the motion event started
808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mDownMotionX = mLastMotionX = ev.getX();
809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(0);
8100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
8110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
8120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_MOVE:
816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll to follow the motion event
818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int deltaX = (int) (mLastMotionX - x);
821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int sx = getScrollX();
824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (deltaX < 0) {
825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (sx > 0) {
8260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mTouchX += Math.max(-mTouchX, deltaX);
8270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
8280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (!mDeferScrollUpdate) {
8290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            scrollBy(Math.max(-sx, deltaX), 0);
8300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        } else {
8310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            // This will trigger a call to computeScroll() on next drawChild() call
8320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            invalidate();
8330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else if (deltaX > 0) {
836321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    final int lastChildIndex = getChildCount() - 1;
837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    final int availableToScroll = getChildOffset(lastChildIndex) -
838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        getRelativeChildOffset(lastChildIndex) - sx;
839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (availableToScroll > 0) {
8400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mTouchX += Math.min(availableToScroll, deltaX);
8410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
8420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (!mDeferScrollUpdate) {
8430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            scrollBy(Math.min(availableToScroll, deltaX), 0);
8440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        } else {
8450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            // This will trigger a call to computeScroll() on next drawChild() call
8460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            invalidate();
8470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    awakenScrollBars();
851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
852564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen            } else {
853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                determineScrollingStart(ev);
854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_UP:
858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int activePointerId = mActivePointerId;
860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(activePointerId);
861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final VelocityTracker velocityTracker = mVelocityTracker;
863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
864321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
8659cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                final int deltaX = (int) (x - mDownMotionX);
8669cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                boolean isfling = Math.abs(deltaX) > MIN_LENGTH_FOR_FLING;
8679cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE;
868321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
8690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int snapVelocity = mSnapVelocity;
8709cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                if ((isSignificantMove && deltaX > 0 ||
8719cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                        (isfling && velocityX > snapVelocity)) &&
8729cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                        mCurrentPage > 0) {
8730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    snapToPageWithVelocity(mCurrentPage - 1, velocityX);
8749cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                } else if ((isSignificantMove && deltaX < 0 ||
8759cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung                        (isfling && velocityX < -snapVelocity)) &&
87686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                        mCurrentPage < getChildCount() - 1) {
8770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    snapToPageWithVelocity(mCurrentPage + 1, velocityX);
878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
880321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
88121f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            } else if (mTouchState == TOUCH_STATE_PREV_PAGE && !handlePagingClicks()) {
882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
88586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.max(0, mCurrentPage - 1);
88686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
88786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
89121f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE && !handlePagingClicks()) {
892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
89586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
89686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
89786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
898321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
899321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
901321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
904b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
906321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_CANCEL:
908b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
909b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka                snapToDestination();
910b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            }
911321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
912321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
913b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            releaseVelocityTracker();
914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_POINTER_UP:
917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            onSecondaryPointerUp(ev);
918321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
919321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
920321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return true;
922321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
924b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
925b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker == null) {
926b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = VelocityTracker.obtain();
927b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
928b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        mVelocityTracker.addMovement(ev);
929b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
930b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
931b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    private void releaseVelocityTracker() {
932b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        if (mVelocityTracker != null) {
933b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker.recycle();
934b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka            mVelocityTracker = null;
935b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka        }
936b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka    }
937b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka
938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void onSecondaryPointerUp(MotionEvent ev) {
939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerId = ev.getPointerId(pointerIndex);
942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (pointerId == mActivePointerId) {
943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // This was our active pointer going up. Choose a new
944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // active pointer and adjust accordingly.
945321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // TODO: Make this decision more intelligent.
946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
948321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionY = ev.getY(newPointerIndex);
949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(newPointerIndex);
950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mVelocityTracker != null) {
951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mVelocityTracker.clear();
952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
954321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
956321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestChildFocus(View child, View focused) {
958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestChildFocus(child, focused);
95986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
96086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page >= 0 && !isInTouchMode()) {
96186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
965e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getChildIndexForRelativeOffset(int relativeOffset) {
966e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int childCount = getChildCount();
9679c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int left;
9689c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen        int right;
969e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        for (int i = 0; i < childCount; ++i) {
9709c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            left = getRelativeChildOffset(i);
9719c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            right = (left + getChildAt(i).getMeasuredWidth());
972e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            if (left <= relativeOffset && relativeOffset <= right) {
973e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                return i;
974e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            }
975e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        }
976e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return -1;
977e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
978e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
979321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getRelativeChildOffset(int index) {
980321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2;
981321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
982321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
983321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getChildOffset(int index) {
984321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (getChildCount() == 0)
985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return 0;
986321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int offset = getRelativeChildOffset(0);
988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < index; ++i) {
9899c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            offset += getChildAt(i).getMeasuredWidth() + mPageSpacing;
990321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return offset;
992321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
993321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
994d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    int getPageNearestToCenterOfScreen() {
995321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenter = getMeasuredWidth();
996321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenterIndex = -1;
997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int screenCenter = mScrollX + (getMeasuredWidth() / 2);
998321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; ++i) {
10000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            View layout = (View) getChildAt(i);
1001321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childWidth = layout.getMeasuredWidth();
1002321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int halfChildWidth = (childWidth / 2);
1003321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childCenter = getChildOffset(i) + halfChildWidth;
1004321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
1005321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
1006321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenter = distanceFromScreenCenter;
1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenterIndex = i;
1008321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1010d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        return minDistanceFromScreenCenterIndex;
1011d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    }
1012d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen
1013d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    protected void snapToDestination() {
1014d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
10170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPageWithVelocity(int whichPage, int velocity) {
10180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // We ignore velocity in this implementation, but children (e.g. SmoothPagedView)
10190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // can use it
10200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage);
10210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
10220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
10230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage) {
10245f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
10270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int duration) {
102886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
103086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int sX = getScrollX();
1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int delta = newX - sX;
10330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage, delta, duration);
10340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
10350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
10360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int delta, int duration) {
10370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mNextPage = whichPage;
10380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
10390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        View focusedChild = getFocusedChild();
10400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (focusedChild != null && whichPage != mCurrentPage &&
10410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                focusedChild == getChildAt(mCurrentPage)) {
10420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            focusedChild.clearFocus();
10430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
10440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
10450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        pageBeginMoving();
1046321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        awakenScrollBars(duration);
1047321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (duration == 0) {
1048321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            duration = Math.abs(delta);
1049321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1050321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1051321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
10520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
105380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
105480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // only load some associated pages
105586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        loadAssociatedPages(mNextPage);
10560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        notifyPageSwitchListener();
1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
1058321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1059321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1060321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
1061321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected Parcelable onSaveInstanceState() {
1062321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final SavedState state = new SavedState(super.onSaveInstanceState());
106386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        state.currentPage = mCurrentPage;
1064321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return state;
1065321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1067321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
1068321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onRestoreInstanceState(Parcelable state) {
1069321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState savedState = (SavedState) state;
1070321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.onRestoreInstanceState(savedState.getSuperState());
107186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (savedState.currentPage != -1) {
107286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = savedState.currentPage;
1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1075321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1076321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollLeft() {
1077321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
107886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
1079321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
108086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage > 0) snapToPage(mNextPage - 1);
1081321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1082321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1083321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1084321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollRight() {
1085321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
108686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
1087321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
108886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
1089321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1090321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1091321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
109286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public int getPageForView(View v) {
1093321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int result = -1;
1094321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
1095321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent vp = v.getParent();
1096321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int count = getChildCount();
1097321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            for (int i = 0; i < count; i++) {
1098321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (vp == getChildAt(i)) {
1099321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    return i;
1100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
1101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return result;
1104321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1105321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
1107321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @return True is long presses are still allowed for the current touch
1108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean allowLongPress() {
1110321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mAllowLongPress;
1111321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
11130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
11140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * Set true to allow long-press events to be triggered, usually checked by
11150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * {@link Launcher} to accept or block dpad-initiated long-presses.
11160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     */
11170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setAllowLongPress(boolean allowLongPress) {
11180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mAllowLongPress = allowLongPress;
11190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
11200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1121321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public static class SavedState extends BaseSavedState {
112286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int currentPage = -1;
1123321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState(Parcelable superState) {
1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(superState);
1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1127321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        private SavedState(Parcel in) {
1129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(in);
113086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage = in.readInt();
1131321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1132321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1133321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        @Override
1134321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public void writeToParcel(Parcel out, int flags) {
1135321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super.writeToParcel(out, flags);
113686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            out.writeInt(currentPage);
1137321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1138321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public static final Parcelable.Creator<SavedState> CREATOR =
1140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                new Parcelable.Creator<SavedState>() {
1141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState createFromParcel(Parcel in) {
1142321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState(in);
1143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1144321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState[] newArray(int size) {
1146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState[size];
1147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        };
1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
115186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void loadAssociatedPages(int page) {
11520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
11530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
11540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (page < count) {
1155e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int lowerPageBound = getAssociatedLowerPageBound(page);
1156e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int upperPageBound = getAssociatedUpperPageBound(page);
11570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < count; ++i) {
11580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    final ViewGroup layout = (ViewGroup) getChildAt(i);
11590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    final int childCount = layout.getChildCount();
11600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (lowerPageBound <= i && i <= upperPageBound) {
11610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (mDirtyPageContent.get(i)) {
11620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            syncPageItems(i);
11630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            mDirtyPageContent.set(i, false);
11640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
11650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
11660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (childCount > 0) {
11670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            layout.removeAllViews();
11680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
11690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mDirtyPageContent.set(i, true);
117080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                    }
117180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                }
117280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
117380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        }
117480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
117580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1176e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedLowerPageBound(int page) {
1177e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.max(0, page - 1);
1178e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1179e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedUpperPageBound(int page) {
1180e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int count = getChildCount();
1181e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.min(page + 1, count - 1);
1182e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1183e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
11845f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void startChoiceMode(int mode, ActionMode.Callback callback) {
1185430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        if (isChoiceMode(CHOICE_MODE_NONE)) {
1186430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mChoiceMode = mode;
1187430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mActionMode = startActionMode(callback);
1188430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        }
11895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
11905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
11912b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public void endChoiceMode() {
11925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        if (!isChoiceMode(CHOICE_MODE_NONE)) {
11935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            mChoiceMode = CHOICE_MODE_NONE;
11945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            resetCheckedGrandchildren();
1195e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka            if (mActionMode != null) mActionMode.finish();
11969f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            mActionMode = null;
11975f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
11985f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
11995f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
12005f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected boolean isChoiceMode(int mode) {
12015f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return mChoiceMode == mode;
12025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
12035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
12045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected ArrayList<Checkable> getCheckedGrandchildren() {
12055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        ArrayList<Checkable> checked = new ArrayList<Checkable>();
12065f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final int childCount = getChildCount();
12075f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < childCount; ++i) {
1208d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung            final ViewGroup layout = (ViewGroup) getChildAt(i);
12095f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final int grandChildCount = layout.getChildCount();
12105f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            for (int j = 0; j < grandChildCount; ++j) {
12115f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final View v = layout.getChildAt(j);
12129f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                if (v instanceof Checkable && ((Checkable) v).isChecked()) {
12135f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    checked.add((Checkable) v);
12145f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
12155f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            }
12165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
12175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return checked;
12185f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
12195f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
12209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    /**
12219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * If in CHOICE_MODE_SINGLE and an item is checked, returns that item.
12229f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * Otherwise, returns null.
12239f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     */
12249f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    protected Checkable getSingleCheckedGrandchild() {
12259f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        if (mChoiceMode == CHOICE_MODE_SINGLE) {
12269f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            final int childCount = getChildCount();
12279f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            for (int i = 0; i < childCount; ++i) {
1228d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung                final ViewGroup layout = (ViewGroup) getChildAt(i);
12299f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                final int grandChildCount = layout.getChildCount();
12309f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                for (int j = 0; j < grandChildCount; ++j) {
12319f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    final View v = layout.getChildAt(j);
12329f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    if (v instanceof Checkable && ((Checkable) v).isChecked()) {
12339f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                        return (Checkable) v;
12349f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    }
12359f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                }
12369f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            }
12379f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        }
12389f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        return null;
12399f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    }
12409f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
12412b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public Object getChosenItem() {
12422b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        View checkedView = (View) getSingleCheckedGrandchild();
12432b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        if (checkedView != null) {
12442b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            return checkedView.getTag();
12452b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        }
12462b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        return null;
12472b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    }
12482b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy
12495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void resetCheckedGrandchildren() {
12505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        // loop through children, and set all of their children to _not_ be checked
12515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final ArrayList<Checkable> checked = getCheckedGrandchildren();
12525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < checked.size(); ++i) {
12535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final Checkable c = checked.get(i);
12545f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            c.setChecked(false);
12555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
12565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
12575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
125886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
125986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called ONLY to synchronize the number of pages that the paged view has.
126086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * To actually fill the pages with information, implement syncPageItems() below.  It is
126186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * guaranteed that syncPageItems() will be called for a particular page before it is shown,
126286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * and therefore, individual page items do not need to be updated in this method.
126386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPages();
126586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
126686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
126786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called to synchronize the items that are on a particular page.  If views on
126886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * the page can be reused, then they should be updated within this method.
126986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPageItems(int page);
127186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
1272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void invalidatePageData() {
12730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
12740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Update all the pages
12750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            syncPages();
127686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
12770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Mark each of the pages as dirty
12780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
12790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageContent.clear();
12800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            for (int i = 0; i < count; ++i) {
12810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageContent.add(true);
12820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
128386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
12840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Load any pages that are necessary for the current window of views
12850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            loadAssociatedPages(mCurrentPage);
12860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageAlpha = true;
1287b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung            updateAdjacentPagesAlpha();
12880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            requestLayout();
12890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
1290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung}
1292