PagedView.java revision 0142d49e1378a7155bcca1fb59965d9e73016dbc
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
19321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport java.util.ArrayList;
2086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chungimport java.util.Arrays;
21241c3b451d7841ba08247beea784953eca4e8582Winson Chungimport java.util.HashMap;
22321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
23321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context;
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;
30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent;
31321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker;
32321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View;
33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration;
34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup;
35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent;
3680baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation;
3780baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation.AnimationListener;
3886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chungimport android.view.animation.AnimationUtils;
39321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller;
40321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
4180baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport com.android.launcher.R;
4280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
43321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/**
44321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a
450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages"
46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup {
48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final String TAG = "PagedView";
490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final int INVALID_PAGE = -1;
50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
5186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    // the min drag distance for a fling to register, to prevent random page shifts
52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final int MIN_LENGTH_FOR_FLING = 50;
53321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final float NANOTIME_DIV = 1000000000.0f;
550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // the velocity at which a fling gesture will cause us to snap to the next page
570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mSnapVelocity = 500;
580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mSmoothingTime;
600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mTouchX;
61321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFirstLayout = true;
630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mCurrentPage;
650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mNextPage = INVALID_PAGE;
660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected Scroller mScroller;
67321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private VelocityTracker mVelocityTracker;
68321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
69321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mDownMotionX;
70321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mLastMotionX;
71321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mLastMotionY;
72321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_REST = 0;
740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_SCROLLING = 1;
750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_PREV_PAGE = 2;
760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
77321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mTouchState = TOUCH_STATE_REST;
79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected OnLongClickListener mLongClickListener;
81321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
82321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private boolean mAllowLongPress = true;
83321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
84321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mTouchSlop;
85321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mPagingTouchSlop;
86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mMaximumVelocity;
87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
88321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final int INVALID_POINTER = -1;
89321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mActivePointerId = INVALID_POINTER;
91321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    private enum PageMovingState { PAGE_BEGIN_MOVING, PAGE_END_MOVING };
9386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private PageSwitchListener mPageSwitchListener;
940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    private PageMovingListener mPageMovingListener;
95321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
9686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private ArrayList<Boolean> mDirtyPageContent;
9786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private boolean mDirtyPageAlpha;
98321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
99241c3b451d7841ba08247beea784953eca4e8582Winson Chung    protected PagedViewIconCache mPageViewIconCache;
100241c3b451d7841ba08247beea784953eca4e8582Winson Chung
1010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, syncPages and syncPageItems will be called to refresh pages
1020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mContentIsRefreshable = true;
1030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, modify alpha of neighboring pages as user scrolls left/right
1050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFadeInAdjacentScreens = true;
1060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
1080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // to switch to a new page
1090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mUsePagingTouchSlop = true;
1100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, the subclass should directly update mScrollX itself in its computeScroll method
1120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // (SmoothPagedView does this)
1130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mDeferScrollUpdate = false;
1140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
115241c3b451d7841ba08247beea784953eca4e8582Winson Chung    /**
116241c3b451d7841ba08247beea784953eca4e8582Winson Chung     * Simple cache mechanism for PagedViewIcon outlines.
117241c3b451d7841ba08247beea784953eca4e8582Winson Chung     */
118241c3b451d7841ba08247beea784953eca4e8582Winson Chung    class PagedViewIconCache {
119241c3b451d7841ba08247beea784953eca4e8582Winson Chung        private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>();
120241c3b451d7841ba08247beea784953eca4e8582Winson Chung
121241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void clear() {
122241c3b451d7841ba08247beea784953eca4e8582Winson Chung            iconOutlineCache.clear();
123241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
124241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void addOutline(Object key, Bitmap b) {
125241c3b451d7841ba08247beea784953eca4e8582Winson Chung            iconOutlineCache.put(key, b);
126241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
127241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void removeOutline(Object key) {
128241c3b451d7841ba08247beea784953eca4e8582Winson Chung            if (iconOutlineCache.containsKey(key)) {
129241c3b451d7841ba08247beea784953eca4e8582Winson Chung                iconOutlineCache.remove(key);
130241c3b451d7841ba08247beea784953eca4e8582Winson Chung            }
131241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
132241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public Bitmap getOutline(Object key) {
133241c3b451d7841ba08247beea784953eca4e8582Winson Chung            return iconOutlineCache.get(key);
134241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
135241c3b451d7841ba08247beea784953eca4e8582Winson Chung    }
136241c3b451d7841ba08247beea784953eca4e8582Winson Chung
13786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public interface PageSwitchListener {
13886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        void onPageSwitch(View newPage, int newPageIndex);
139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public interface PageMovingListener {
1420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        void onPageBeginMoving();
1430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        void onPageEndMoving();
1440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context) {
147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, null);
148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs) {
151321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, attrs, 0);
152321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
153321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
154321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs, int defStyle) {
155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super(context, attrs, defStyle);
156321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setHapticFeedbackEnabled(false);
1580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        init();
159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Initializes various states for this workspace.
163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void init() {
16586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent = new ArrayList<Boolean>();
16686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent.ensureCapacity(32);
167241c3b451d7841ba08247beea784953eca4e8582Winson Chung        mPageViewIconCache = new PagedViewIconCache();
168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mScroller = new Scroller(getContext());
16986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = 0;
170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mTouchSlop = configuration.getScaledTouchSlop();
173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
176321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
17786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
17886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mPageSwitchListener = pageSwitchListener;
17986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
18086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
183321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
18586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Returns the index of the currently displayed page.
186321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
18786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * @return The index of the currently displayed page.
188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
18986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getCurrentPage() {
19086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        return mCurrentPage;
191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
19386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getPageCount() {
194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildCount();
195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
19786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    View getPageAt(int index) {
198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildAt(index);
199321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    int getScrollWidth() {
202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getWidth();
203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
20686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Sets the current page.
207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
20886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    void setCurrentPage(int currentPage) {
209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (getChildCount() == 0) return;
211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
21286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
21386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        scrollTo(getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage), 0);
21480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
21686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        notifyPageSwitchListener();
217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void notifyPageSwitchListener() {
22086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
22186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
224321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void pageBeginMoving() {
2270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void pageEndMoving() {
2310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
23486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Registers the specified listener on each page contained in this workspace.
235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @param l The listener used to respond to long clicks.
237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void setOnLongClickListener(OnLongClickListener l) {
240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mLongClickListener = l;
24186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        final int count = getPageCount();
242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < count; i++) {
24386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(i).setOnLongClickListener(l);
244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
2480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void scrollTo(int x, int y) {
2490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        super.scrollTo(x, y);
2500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mTouchX = x;
2510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
2520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // we moved this functionality to a helper function so SmoothPagedView can reuse it
2550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean computeScrollHelper() {
256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.computeScrollOffset()) {
257321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
2580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            invalidate();
2590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
26086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        } else if (mNextPage != INVALID_PAGE) {
26186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
26286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mNextPage = INVALID_PAGE;
2630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            notifyPageSwitchListener();
2640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            pageEndMoving();
2650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
2670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        return false;
2680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    @Override
2710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void computeScroll() {
2720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        computeScrollHelper();
273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (widthMode != MeasureSpec.EXACTLY) {
280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (heightMode != MeasureSpec.EXACTLY) {
286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        // The children are given the same width and height as the workspace
290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setMeasuredDimension(widthSize, heightSize);
296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mFirstLayout) {
298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(false);
29986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            scrollTo(mCurrentPage * widthSize, 0);
3000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mScroller.setFinalX(mCurrentPage * widthSize);
301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(true);
302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mFirstLayout = false;
303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int childLeft = 0;
310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (childCount > 0) {
311321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            childLeft = (getMeasuredWidth() - getChildAt(0).getMeasuredWidth()) / 2;
312321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
313321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
314321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
315321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final View child = getChildAt(i);
316321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (child.getVisibility() != View.GONE) {
317321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int childWidth = child.getMeasuredWidth();
318321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
319321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                childLeft += childWidth;
320321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
321321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
323321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
324321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void dispatchDraw(Canvas canvas) {
3260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mFadeInAdjacentScreens) {
3270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
3280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                int screenCenter = mScrollX + (getMeasuredWidth() / 2);
3290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int childCount = getChildCount();
3300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < childCount; ++i) {
3310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    View layout = (View) getChildAt(i);
3320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childWidth = layout.getMeasuredWidth();
3330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int halfChildWidth = (childWidth / 2);
3340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childCenter = getChildOffset(i) + halfChildWidth;
3350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
3360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    float alpha = 0.0f;
3370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (distanceFromScreenCenter < halfChildWidth) {
3380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        alpha = 1.0f;
3390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else if (distanceFromScreenCenter > childWidth) {
3400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        alpha = 0.0f;
3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
3420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        float dimAlpha = (float) (distanceFromScreenCenter - halfChildWidth) / halfChildWidth;
3430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
3440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        alpha = 1.0f - dimAlpha;
3450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
3460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (Float.compare(alpha, layout.getAlpha()) != 0) {
3470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        layout.setAlpha(alpha);
3480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
349affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung                }
3500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageAlpha = false;
351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
352321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
3530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // Find out which screens are visible; as an optimization we only call draw on them
3550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // As an optimization, this code assumes that all pages have the same width as the 0th
3570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // page.
3580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int pageWidth = getChildAt(0).getMeasuredWidth();
3590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int pageCount = getChildCount();
3600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int screenWidth = getMeasuredWidth();
3610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        int x = getRelativeChildOffset(0) + pageWidth;
3620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        int leftScreen = 0;
3630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        int rightScreen = 0;
3640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        while (x <= mScrollX) {
3650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            leftScreen++;
3660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            x += pageWidth;
3670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // replace above line with this if you don't assume all pages have same width as 0th
3680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // page:
3690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // x += getChildAt(leftScreen).getMeasuredWidth();
3700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        rightScreen = leftScreen;
3720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        while (x < mScrollX + screenWidth) {
3730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            rightScreen++;
3740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            x += pageWidth;
3750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // replace above line with this if you don't assume all pages have same width as 0th
3760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // page:
3770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            //if (rightScreen < pageCount) {
3780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            //    x += getChildAt(rightScreen).getMeasuredWidth();
3790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            //}
3800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
3810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        rightScreen = Math.min(getChildCount() - 1, rightScreen);
3820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final long drawingTime = getDrawingTime();
3840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        for (int i = leftScreen; i <= rightScreen; i++) {
3850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            drawChild(canvas, getChildAt(i), drawingTime);
3860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
388321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
389321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
390321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
39186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
39286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page != mCurrentPage || !mScroller.isFinished()) {
39386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
394321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
395321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
396321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
397321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
40186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int focusablePage;
40286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mNextPage != INVALID_PAGE) {
40386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mNextPage;
404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
40586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mCurrentPage;
406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
40786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View v = getPageAt(focusablePage);
408321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
409321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            v.requestFocus(direction, previouslyFocusedRect);
410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
411321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
412321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
413321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
414321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
415321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean dispatchUnhandledMove(View focused, int direction) {
416321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
41786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() > 0) {
41886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() - 1);
419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT) {
42286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() < getPageCount() - 1) {
42386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() + 1);
424321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
425321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return super.dispatchUnhandledMove(focused, direction);
428321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
429321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
430321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
431321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
43286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
43386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(mCurrentPage).addFocusables(views, direction);
434321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
43686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) {
43786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage - 1).addFocusables(views, direction);
438321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
439321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT){
44086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getPageCount() - 1) {
44186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage + 1).addFocusables(views, direction);
442321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
444321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
445321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
446321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
447321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * If one of our descendant views decides that it could be focused now, only
44886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * pass that along if it's on the current page.
449321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
45086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This happens when live folders requery, and if they're off page, they
45186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * end up calling requestFocus, which pulls it on page.
452321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void focusableViewAvailable(View focused) {
45586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View current = getPageAt(mCurrentPage);
456321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        View v = focused;
457321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        while (true) {
458321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == current) {
459321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                super.focusableViewAvailable(focused);
460321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
461321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
462321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == this) {
463321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
464321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent parent = v.getParent();
466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (parent instanceof View) {
467321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                v = (View)v.getParent();
468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else {
469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
471321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * {@inheritDoc}
476321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
477321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (disallowIntercept) {
480321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // We need to make sure to cancel our long press if
481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // a scrollable widget takes over touch events
48286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            final View currentPage = getChildAt(mCurrentPage);
48386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage.cancelLongPress();
484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
485321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestDisallowInterceptTouchEvent(disallowIntercept);
486321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
487321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
488321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * This method JUST determines whether we want to intercept the motion.
492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * If we return true, onTouchEvent will be called and we do the actual
493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * scrolling there.
494321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
495321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
496321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
497321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Shortcut the most recurring case: the user is in the dragging
498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * state and he is moving his finger.  We want to intercept this
499321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * motion.
500321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
501321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
502321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if ((action == MotionEvent.ACTION_MOVE) &&
503321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                (mTouchState == TOUCH_STATE_SCROLLING)) {
504321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
505321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
506321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
507321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
508321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
509321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_MOVE: {
510321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
511321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
512321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * whether the user has moved far enough from his original down touch.
513321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                determineScrollingStart(ev);
515321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
516321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
517321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
518321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_DOWN: {
519321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX();
520321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float y = ev.getY();
521321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Remember location of down touch
522321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mDownMotionX = x;
523321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
524321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionY = y;
525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = ev.getPointerId(0);
526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = true;
527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
528321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
529321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * If being flinged and user touches the screen, initiate drag;
530321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * otherwise don't.  mScroller.isFinished should be false when
531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * being flinged.
532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
53586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                // check if this can be the beginning of a tap on the side of the pages
536321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // to scroll the current page
537321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if ((mTouchState != TOUCH_STATE_PREV_PAGE) &&
538321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        (mTouchState != TOUCH_STATE_NEXT_PAGE)) {
539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (getChildCount() > 0) {
540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        int relativeChildLeft = getChildOffset(0);
541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        int relativeChildRight = relativeChildLeft + getChildAt(0).getMeasuredWidth();
542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        if (x < relativeChildLeft) {
543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_PREV_PAGE;
544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        } else if (x > relativeChildRight) {
545321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_NEXT_PAGE;
546321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        }
547321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
548321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
549321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
550321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_CANCEL:
553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_UP:
554321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Release the drag
5550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageEndMoving();
556321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_REST;
557321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
558321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = INVALID_POINTER;
559321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
560321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
561321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
562321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_POINTER_UP:
563321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                onSecondaryPointerUp(ev);
564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
568321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * The only time we want to intercept motion events is if we are in the
569321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * drag mode.
570321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mTouchState != TOUCH_STATE_REST;
572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
573321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
57480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    protected void animateClickFeedback(View v, final Runnable r) {
57580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // animate the view slightly to show click feedback running some logic after it is "pressed"
57680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        Animation anim = AnimationUtils.loadAnimation(getContext(),
57780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                R.anim.paged_view_click_feedback);
57880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        anim.setAnimationListener(new AnimationListener() {
57980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
58080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationStart(Animation animation) {}
58180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
58280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationRepeat(Animation animation) {
58380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                r.run();
58480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
58580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
58680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationEnd(Animation animation) {}
58780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        });
58880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        v.startAnimation(anim);
58980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
59080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
591321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /*
592321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Determines if we should change the touch state to start scrolling after the
593321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * user moves their touch point too far.
594321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
595321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void determineScrollingStart(MotionEvent ev) {
596321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
597321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Locally do absolute value. mLastMotionX is set to the y value
598321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * of the down event.
599321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
600321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
601321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float x = ev.getX(pointerIndex);
602321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float y = ev.getY(pointerIndex);
603321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int xDiff = (int) Math.abs(x - mLastMotionX);
604321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int yDiff = (int) Math.abs(y - mLastMotionY);
605321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
606321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int touchSlop = mTouchSlop;
607321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xPaged = xDiff > mPagingTouchSlop;
608321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xMoved = xDiff > touchSlop;
609321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean yMoved = yDiff > touchSlop;
610321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
611321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (xMoved || yMoved) {
6120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mUsePagingTouchSlop ? xPaged : xMoved) {
613321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll if the user moved far enough along the X axis
614321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_SCROLLING;
615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
6160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mTouchX = mScrollX;
6170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
6180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
619321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
620321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Either way, cancel any pending longpress
621321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mAllowLongPress) {
622321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Try canceling the long press. It could also have been scheduled
624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // by a distant descendant, so use the mAllowLongPress flag to block
625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // everything
62686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                final View currentPage = getPageAt(mCurrentPage);
62786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                currentPage.cancelLongPress();
628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
631321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
633321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onTouchEvent(MotionEvent ev) {
634321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mVelocityTracker == null) {
635321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mVelocityTracker = VelocityTracker.obtain();
636321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mVelocityTracker.addMovement(ev);
638321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_DOWN:
643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            /*
644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * If being flinged and user touches, stop the fling. isFinished
645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * will be false if being flinged.
646321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             */
647321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (!mScroller.isFinished()) {
648321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mScroller.abortAnimation();
649321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
651321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Remember where the motion event started
652321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mDownMotionX = mLastMotionX = ev.getX();
653321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(0);
6540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
6550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
6560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
657321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_MOVE:
660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
661321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll to follow the motion event
662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int deltaX = (int) (mLastMotionX - x);
665321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
666321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int sx = getScrollX();
668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (deltaX < 0) {
669321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (sx > 0) {
6700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mTouchX += Math.max(-mTouchX, deltaX);
6710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
6720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (!mDeferScrollUpdate) {
6730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            scrollBy(Math.max(-sx, deltaX), 0);
6740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        } else {
6750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            // This will trigger a call to computeScroll() on next drawChild() call
6760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            invalidate();
6770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
679321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else if (deltaX > 0) {
680321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    final int lastChildIndex = getChildCount() - 1;
681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    final int availableToScroll = getChildOffset(lastChildIndex) -
682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        getRelativeChildOffset(lastChildIndex) - sx;
683321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (availableToScroll > 0) {
6840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mTouchX += Math.min(availableToScroll, deltaX);
6850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
6860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (!mDeferScrollUpdate) {
6870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            scrollBy(Math.min(availableToScroll, deltaX), 0);
6880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        } else {
6890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            // This will trigger a call to computeScroll() on next drawChild() call
6900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            invalidate();
6910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    awakenScrollBars();
695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else if ((mTouchState == TOUCH_STATE_PREV_PAGE) ||
697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    (mTouchState == TOUCH_STATE_NEXT_PAGE)) {
698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                determineScrollingStart(ev);
699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_UP:
703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int activePointerId = mActivePointerId;
705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(activePointerId);
706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final VelocityTracker velocityTracker = mVelocityTracker;
708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
710321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                boolean isfling = Math.abs(mDownMotionX - x) > MIN_LENGTH_FOR_FLING;
711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
7120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int snapVelocity = mSnapVelocity;
7130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                if (isfling && velocityX > snapVelocity && mCurrentPage > 0) {
7140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    snapToPageWithVelocity(mCurrentPage - 1, velocityX);
7150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                } else if (isfling && velocityX < -snapVelocity &&
71686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                        mCurrentPage < getChildCount() - 1) {
7170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    snapToPageWithVelocity(mCurrentPage + 1, velocityX);
718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (mVelocityTracker != null) {
723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    mVelocityTracker.recycle();
724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    mVelocityTracker = null;
725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
726321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
727321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
728321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
729321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
73086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.max(0, mCurrentPage - 1);
73186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
73286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
74086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
74186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
74286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_CANCEL:
752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
753321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_POINTER_UP:
757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            onSecondaryPointerUp(ev);
758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return true;
762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void onSecondaryPointerUp(MotionEvent ev) {
765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerId = ev.getPointerId(pointerIndex);
768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (pointerId == mActivePointerId) {
769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // This was our active pointer going up. Choose a new
770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // active pointer and adjust accordingly.
771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // TODO: Make this decision more intelligent.
772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionY = ev.getY(newPointerIndex);
775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(newPointerIndex);
776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mVelocityTracker != null) {
777321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mVelocityTracker.clear();
778321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
779321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestChildFocus(View child, View focused) {
784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestChildFocus(child, focused);
78586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
78686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page >= 0 && !isInTouchMode()) {
78786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
791321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getRelativeChildOffset(int index) {
792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2;
793321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getChildOffset(int index) {
796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (getChildCount() == 0)
797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return 0;
798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int offset = getRelativeChildOffset(0);
800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < index; ++i) {
801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            offset += getChildAt(i).getMeasuredWidth();
802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return offset;
804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void snapToDestination() {
807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenter = getMeasuredWidth();
808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenterIndex = -1;
809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int screenCenter = mScrollX + (getMeasuredWidth() / 2);
810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; ++i) {
8120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            View layout = (View) getChildAt(i);
813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childWidth = layout.getMeasuredWidth();
814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int halfChildWidth = (childWidth / 2);
815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childCenter = getChildOffset(i) + halfChildWidth;
816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenter = distanceFromScreenCenter;
819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenterIndex = i;
820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
82286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        snapToPage(minDistanceFromScreenCenterIndex, 1000);
823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
8250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPageWithVelocity(int whichPage, int velocity) {
8260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // We ignore velocity in this implementation, but children (e.g. SmoothPagedView)
8270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // can use it
8280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage);
8290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
8300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
8310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage) {
83286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        snapToPage(whichPage, 1000);
833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
8350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int duration) {
83686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
83986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int sX = getScrollX();
841321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int delta = newX - sX;
8420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage, delta, duration);
8430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
8440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
8450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int delta, int duration) {
8460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mNextPage = whichPage;
8470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
8480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        View focusedChild = getFocusedChild();
8490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (focusedChild != null && whichPage != mCurrentPage &&
8500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                focusedChild == getChildAt(mCurrentPage)) {
8510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            focusedChild.clearFocus();
8520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
8530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
8540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        pageBeginMoving();
855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        awakenScrollBars(duration);
856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (duration == 0) {
857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            duration = Math.abs(delta);
858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
8610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
86280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
86380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // only load some associated pages
86486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        loadAssociatedPages(mNextPage);
8650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        notifyPageSwitchListener();
866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
867321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
868321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
869321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected Parcelable onSaveInstanceState() {
871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final SavedState state = new SavedState(super.onSaveInstanceState());
87286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        state.currentPage = mCurrentPage;
873321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return state;
874321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
875321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onRestoreInstanceState(Parcelable state) {
878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState savedState = (SavedState) state;
879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.onRestoreInstanceState(savedState.getSuperState());
88086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (savedState.currentPage != -1) {
88186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = savedState.currentPage;
882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
885321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollLeft() {
886321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
88786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
88986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage > 0) snapToPage(mNextPage - 1);
890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollRight() {
894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
89586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
89786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
898321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
899321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
90186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public int getPageForView(View v) {
902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int result = -1;
903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
904321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent vp = v.getParent();
905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int count = getChildCount();
906321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            for (int i = 0; i < count; i++) {
907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (vp == getChildAt(i)) {
908321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    return i;
909321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
910321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
911321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
912321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return result;
913321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @return True is long presses are still allowed for the current touch
917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
918321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean allowLongPress() {
919321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mAllowLongPress;
920321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
9220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
9230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * Set true to allow long-press events to be triggered, usually checked by
9240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * {@link Launcher} to accept or block dpad-initiated long-presses.
9250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     */
9260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setAllowLongPress(boolean allowLongPress) {
9270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mAllowLongPress = allowLongPress;
9280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
9290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public static class SavedState extends BaseSavedState {
93186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int currentPage = -1;
932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState(Parcelable superState) {
934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(superState);
935321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
936321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        private SavedState(Parcel in) {
938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(in);
93986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage = in.readInt();
940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        @Override
943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public void writeToParcel(Parcel out, int flags) {
944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super.writeToParcel(out, flags);
94586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            out.writeInt(currentPage);
946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
948321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public static final Parcelable.Creator<SavedState> CREATOR =
949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                new Parcelable.Creator<SavedState>() {
950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState createFromParcel(Parcel in) {
951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState(in);
952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
954321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState[] newArray(int size) {
955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState[size];
956321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        };
958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
96086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void loadAssociatedPages(int page) {
9610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
9620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
9630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (page < count) {
9640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                int lowerPageBound = Math.max(0, page - 1);
9650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                int upperPageBound = Math.min(page + 1, count - 1);
9660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < count; ++i) {
9670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    final ViewGroup layout = (ViewGroup) getChildAt(i);
9680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    final int childCount = layout.getChildCount();
9690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (lowerPageBound <= i && i <= upperPageBound) {
9700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (mDirtyPageContent.get(i)) {
9710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            syncPageItems(i);
9720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            mDirtyPageContent.set(i, false);
9730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
9740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
9750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (childCount > 0) {
9760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            layout.removeAllViews();
9770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
9780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mDirtyPageContent.set(i, true);
97980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                    }
98080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                }
98180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
98280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        }
98380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
98480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
98586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
98686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called ONLY to synchronize the number of pages that the paged view has.
98786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * To actually fill the pages with information, implement syncPageItems() below.  It is
98886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * guaranteed that syncPageItems() will be called for a particular page before it is shown,
98986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * and therefore, individual page items do not need to be updated in this method.
99086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPages();
99286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
99386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
99486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called to synchronize the items that are on a particular page.  If views on
99586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * the page can be reused, then they should be updated within this method.
99686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPageItems(int page);
99886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void invalidatePageData() {
10000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
10010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Update all the pages
10020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            syncPages();
100386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
10040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Mark each of the pages as dirty
10050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
10060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageContent.clear();
10070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            for (int i = 0; i < count; ++i) {
10080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageContent.add(true);
10090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
101086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
10110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Load any pages that are necessary for the current window of views
10120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            loadAssociatedPages(mCurrentPage);
10130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageAlpha = true;
10140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            requestLayout();
10150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1017321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung}
1018