PagedView.java revision 28750fba6a2d141eb9a1e566718c17236030b815
1321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/*
2321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Copyright (C) 2010 The Android Open Source Project
3321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
4321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * you may not use this file except in compliance with the License.
6321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * You may obtain a copy of the License at
7321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
8321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung *
10321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Unless required by applicable law or agreed to in writing, software
11321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * See the License for the specific language governing permissions and
14321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * limitations under the License.
15321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
16321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
17321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpackage com.android.launcher2;
18321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
19e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport java.util.ArrayList;
20e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport java.util.HashMap;
219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
22321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context;
23241c3b451d7841ba08247beea784953eca4e8582Winson Chungimport android.graphics.Bitmap;
24321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas;
25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect;
26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel;
27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable;
28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet;
29e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.util.Log;
305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode;
31321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent;
32321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker;
33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View;
34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration;
35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup;
36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent;
3780baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation;
385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.view.animation.Animation.AnimationListener;
39e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.view.animation.AnimationUtils;
405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable;
41e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.widget.LinearLayout;
42321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller;
43321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
44e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport com.android.launcher.R;
4580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/**
47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a
480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages"
49321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */
50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup {
51321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final String TAG = "PagedView";
520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final int INVALID_PAGE = -1;
53321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
5486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    // the min drag distance for a fling to register, to prevent random page shifts
55321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private static final int MIN_LENGTH_FOR_FLING = 50;
56321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private static final int PAGE_SNAP_ANIMATION_DURATION = 1000;
580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected static final float NANOTIME_DIV = 1000000000.0f;
590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // the velocity at which a fling gesture will cause us to snap to the next page
610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mSnapVelocity = 500;
620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mSmoothingTime;
640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected float mTouchX;
65321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFirstLayout = true;
670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mCurrentPage;
690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mNextPage = INVALID_PAGE;
700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected Scroller mScroller;
71321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private VelocityTracker mVelocityTracker;
72321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
73321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mDownMotionX;
74321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mLastMotionX;
75321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private float mLastMotionY;
76321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_REST = 0;
780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_SCROLLING = 1;
790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_PREV_PAGE = 2;
800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
81321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected int mTouchState = TOUCH_STATE_REST;
83321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected OnLongClickListener mLongClickListener;
85321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private boolean mAllowLongPress = true;
87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
88321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mTouchSlop;
89321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mPagingTouchSlop;
90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private int mMaximumVelocity;
91321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
925f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected static final int INVALID_POINTER = -1;
93321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
945f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected int mActivePointerId = INVALID_POINTER;
95321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
9686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private PageSwitchListener mPageSwitchListener;
97321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
9886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private ArrayList<Boolean> mDirtyPageContent;
9986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    private boolean mDirtyPageAlpha;
100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1015f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // choice modes
1025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_NONE = 0;
1035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_SINGLE = 1;
1045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    // Multiple selection mode is not supported by all Launcher actions atm
1055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected static final int CHOICE_MODE_MULTIPLE = 2;
1069f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
1075f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private int mChoiceMode;
1085f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    private ActionMode mActionMode;
1095f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
110241c3b451d7841ba08247beea784953eca4e8582Winson Chung    protected PagedViewIconCache mPageViewIconCache;
111241c3b451d7841ba08247beea784953eca4e8582Winson Chung
1120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, syncPages and syncPageItems will be called to refresh pages
1130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mContentIsRefreshable = true;
1140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, modify alpha of neighboring pages as user scrolls left/right
1160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mFadeInAdjacentScreens = true;
1170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
1190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // to switch to a new page
1200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mUsePagingTouchSlop = true;
1210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // If true, the subclass should directly update mScrollX itself in its computeScroll method
1230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // (SmoothPagedView does this)
1240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean mDeferScrollUpdate = false;
1250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
126241c3b451d7841ba08247beea784953eca4e8582Winson Chung    /**
127241c3b451d7841ba08247beea784953eca4e8582Winson Chung     * Simple cache mechanism for PagedViewIcon outlines.
128241c3b451d7841ba08247beea784953eca4e8582Winson Chung     */
129241c3b451d7841ba08247beea784953eca4e8582Winson Chung    class PagedViewIconCache {
130241c3b451d7841ba08247beea784953eca4e8582Winson Chung        private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>();
131241c3b451d7841ba08247beea784953eca4e8582Winson Chung
132241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void clear() {
133241c3b451d7841ba08247beea784953eca4e8582Winson Chung            iconOutlineCache.clear();
134241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
135241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void addOutline(Object key, Bitmap b) {
136241c3b451d7841ba08247beea784953eca4e8582Winson Chung            iconOutlineCache.put(key, b);
137241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
138241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public void removeOutline(Object key) {
139241c3b451d7841ba08247beea784953eca4e8582Winson Chung            if (iconOutlineCache.containsKey(key)) {
140241c3b451d7841ba08247beea784953eca4e8582Winson Chung                iconOutlineCache.remove(key);
141241c3b451d7841ba08247beea784953eca4e8582Winson Chung            }
142241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
143241c3b451d7841ba08247beea784953eca4e8582Winson Chung        public Bitmap getOutline(Object key) {
144241c3b451d7841ba08247beea784953eca4e8582Winson Chung            return iconOutlineCache.get(key);
145241c3b451d7841ba08247beea784953eca4e8582Winson Chung        }
146241c3b451d7841ba08247beea784953eca4e8582Winson Chung    }
147241c3b451d7841ba08247beea784953eca4e8582Winson Chung
14886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public interface PageSwitchListener {
14986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        void onPageSwitch(View newPage, int newPageIndex);
150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
151321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public interface PageMovingListener {
1530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        void onPageBeginMoving();
1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        void onPageEndMoving();
1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
1560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context) {
158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, null);
159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs) {
162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        this(context, attrs, 0);
163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public PagedView(Context context, AttributeSet attrs, int defStyle) {
166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super(context, attrs, defStyle);
1675f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        mChoiceMode = CHOICE_MODE_NONE;
168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setHapticFeedbackEnabled(false);
1700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        init();
171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Initializes various states for this workspace.
175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void init() {
17786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent = new ArrayList<Boolean>();
17886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mDirtyPageContent.ensureCapacity(32);
179241c3b451d7841ba08247beea784953eca4e8582Winson Chung        mPageViewIconCache = new PagedViewIconCache();
180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mScroller = new Scroller(getContext());
18186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = 0;
182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
183321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mTouchSlop = configuration.getScaledTouchSlop();
185321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
186321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
187321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
18986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
19086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mPageSwitchListener = pageSwitchListener;
19186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
19286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
19786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Returns the index of the currently displayed page.
198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
19986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * @return The index of the currently displayed page.
200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
20186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getCurrentPage() {
20286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        return mCurrentPage;
203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
20586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    int getPageCount() {
206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildCount();
207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
20986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    View getPageAt(int index) {
210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getChildAt(index);
211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    int getScrollWidth() {
214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return getWidth();
215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
21886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Sets the current page.
219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
22086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    void setCurrentPage(int currentPage) {
221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (getChildCount() == 0) return;
223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
22486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
22586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        scrollTo(getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage), 0);
22680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
22886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        notifyPageSwitchListener();
229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void notifyPageSwitchListener() {
23286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mPageSwitchListener != null) {
23386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
2370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void pageBeginMoving() {
2390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // a method that subclasses can override to add behavior
2420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void pageEndMoving() {
2430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
24686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * Registers the specified listener on each page contained in this workspace.
247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
248321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @param l The listener used to respond to long clicks.
249321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
250321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void setOnLongClickListener(OnLongClickListener l) {
252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mLongClickListener = l;
25386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        final int count = getPageCount();
254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < count; i++) {
25586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(i).setOnLongClickListener(l);
256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
257321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
258321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
2600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void scrollTo(int x, int y) {
2610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        super.scrollTo(x, y);
2620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mTouchX = x;
2630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
2640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // we moved this functionality to a helper function so SmoothPagedView can reuse it
2670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected boolean computeScrollHelper() {
268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.computeScrollOffset()) {
2695f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
2710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            invalidate();
2720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
27386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        } else if (mNextPage != INVALID_PAGE) {
2745f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            mDirtyPageAlpha = true;
27586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
27686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mNextPage = INVALID_PAGE;
2770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            notifyPageSwitchListener();
2780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            pageEndMoving();
2790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return true;
280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
2810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        return false;
2820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
2830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    @Override
2850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void computeScroll() {
2860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        computeScrollHelper();
287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (widthMode != MeasureSpec.EXACTLY) {
294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (heightMode != MeasureSpec.EXACTLY) {
300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        // The children are given the same width and height as the workspace
3045f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        // unless they were set to WRAP_CONTENT
305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
3075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            // disallowing padding in paged view (just pass 0)
3085f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final View child = getChildAt(i);
3095f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
3105f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3115f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childWidthMode;
3125f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.width == LayoutParams.WRAP_CONTENT) {
3135f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.AT_MOST;
3145f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
3155f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childWidthMode = MeasureSpec.EXACTLY;
3165f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
3175f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3185f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int childHeightMode;
3195f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            if (lp.height == LayoutParams.WRAP_CONTENT) {
3205f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.AT_MOST;
3215f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            } else {
3225f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                childHeightMode = MeasureSpec.EXACTLY;
3235f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            }
3245f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3255f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childWidthMeasureSpec =
3265f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                MeasureSpec.makeMeasureSpec(widthSize, childWidthMode);
3275f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            final int childHeightMeasureSpec =
3285f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                MeasureSpec.makeMeasureSpec(heightSize, childHeightMode);
3295f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
3305f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
331321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
332321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
333321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        setMeasuredDimension(widthSize, heightSize);
334cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    }
335321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
336cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka    @Override
33728750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
3385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(false);
340cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
341cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            scrollTo(newX, 0);
342cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka            mScroller.setFinalX(newX);
343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            setHorizontalScrollBarEnabled(true);
344321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mFirstLayout = false;
345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
348321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int childLeft = 0;
349321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (childCount > 0) {
350e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            childLeft = getRelativeChildOffset(0);
351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
352321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; i++) {
354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final View child = getChildAt(i);
355321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (child.getVisibility() != View.GONE) {
356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int childWidth = child.getMeasuredWidth();
3575f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                final int childHeight = (getMeasuredHeight() - child.getMeasuredHeight()) / 2;
3585f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                child.layout(childLeft, childHeight,
3595f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                        childLeft + childWidth, childHeight + child.getMeasuredHeight());
360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                childLeft += childWidth;
361321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
362321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
365e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void updateAdjacentPagesAlpha() {
3660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mFadeInAdjacentScreens) {
3670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
368e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int halfScreenSize = getMeasuredWidth() / 2;
369e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int screenCenter = mScrollX + halfScreenSize;
370e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int childCount = getChildCount();
3720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < childCount; ++i) {
3730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    View layout = (View) getChildAt(i);
3740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childWidth = layout.getMeasuredWidth();
3750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int halfChildWidth = (childWidth / 2);
3760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    int childCenter = getChildOffset(i) + halfChildWidth;
377e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
378e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int d = halfChildWidth;
379e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    int distanceFromScreenCenter = childCenter - screenCenter;
380e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    if (distanceFromScreenCenter > 0) {
381e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i > 0) {
382e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                            d += getChildAt(i - 1).getMeasuredWidth() / 2;
383e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
3840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
385e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        if (i < childCount - 1) {
386e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                            d += getChildAt(i + 1).getMeasuredWidth() / 2;
387e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                        }
3880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
389e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
390e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d;
391e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
392e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung                    float alpha = 1.0f - dimAlpha;
393e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung
3940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (Float.compare(alpha, layout.getAlpha()) != 0) {
3950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        layout.setAlpha(alpha);
3960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
397affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung                }
3980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageAlpha = false;
399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
401e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
4020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
403e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    @Override
404e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected void dispatchDraw(Canvas canvas) {
405e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        updateAdjacentPagesAlpha();
4060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
407e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        // Find out which screens are visible; as an optimization we only call draw on them
4080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // As an optimization, this code assumes that all pages have the same width as the 0th
4090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // page.
4100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int pageCount = getChildCount();
411c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka        if (pageCount > 0) {
412c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final int pageWidth = getChildAt(0).getMeasuredWidth();
413c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final int screenWidth = getMeasuredWidth();
414c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int x = getRelativeChildOffset(0) + pageWidth;
415c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int leftScreen = 0;
416c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            int rightScreen = 0;
417c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            while (x <= mScrollX) {
418c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                leftScreen++;
419c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                x += pageWidth;
420c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // replace above line with this if you don't assume all pages have same width as 0th
421c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // page:
422c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // x += getChildAt(leftScreen).getMeasuredWidth();
423c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
424c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = leftScreen;
425c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            while (x < mScrollX + screenWidth) {
426c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                rightScreen++;
427c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                x += pageWidth;
428c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // replace above line with this if you don't assume all pages have same width as 0th
429c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                // page:
430c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                //if (rightScreen < pageCount) {
431c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                //    x += getChildAt(rightScreen).getMeasuredWidth();
432c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                //}
433c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
434c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            rightScreen = Math.min(getChildCount() - 1, rightScreen);
4350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
436c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            final long drawingTime = getDrawingTime();
437c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            for (int i = leftScreen; i <= rightScreen; i++) {
438c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka                drawChild(canvas, getChildAt(i), drawingTime);
439c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka            }
4400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
441321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
442321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
444321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
44586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
44686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page != mCurrentPage || !mScroller.isFinished()) {
44786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
448321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
449321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
450321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
451321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
452321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
45586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int focusablePage;
45686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mNextPage != INVALID_PAGE) {
45786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mNextPage;
458321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
45986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            focusablePage = mCurrentPage;
460321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
46186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View v = getPageAt(focusablePage);
462321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
463321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            v.requestFocus(direction, previouslyFocusedRect);
464321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return false;
466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
467321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean dispatchUnhandledMove(View focused, int direction) {
470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
47186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() > 0) {
47286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() - 1);
473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT) {
47686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (getCurrentPage() < getPageCount() - 1) {
47786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                snapToPage(getCurrentPage() + 1);
478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return true;
479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
480321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return super.dispatchUnhandledMove(focused, direction);
482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
483321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
485321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
48686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
48786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            getPageAt(mCurrentPage).addFocusables(views, direction);
488321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (direction == View.FOCUS_LEFT) {
49086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) {
49186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage - 1).addFocusables(views, direction);
492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else if (direction == View.FOCUS_RIGHT){
49486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getPageCount() - 1) {
49586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                getPageAt(mCurrentPage + 1).addFocusables(views, direction);
496321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
497321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
499321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
500321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
501321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * If one of our descendant views decides that it could be focused now, only
50286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * pass that along if it's on the current page.
503321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     *
50486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This happens when live folders requery, and if they're off page, they
50586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * end up calling requestFocus, which pulls it on page.
506321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
507321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
508321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void focusableViewAvailable(View focused) {
50986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        View current = getPageAt(mCurrentPage);
510321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        View v = focused;
511321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        while (true) {
512321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == current) {
513321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                super.focusableViewAvailable(focused);
514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
515321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
516321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (v == this) {
517321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
518321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
519321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent parent = v.getParent();
520321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (parent instanceof View) {
521321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                v = (View)v.getParent();
522321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else {
523321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return;
524321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
528321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
529321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * {@inheritDoc}
530321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (disallowIntercept) {
534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // We need to make sure to cancel our long press if
535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // a scrollable widget takes over touch events
53686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            final View currentPage = getChildAt(mCurrentPage);
53786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage.cancelLongPress();
538321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestDisallowInterceptTouchEvent(disallowIntercept);
540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
545321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * This method JUST determines whether we want to intercept the motion.
546321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * If we return true, onTouchEvent will be called and we do the actual
547321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * scrolling there.
548321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
549321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
550321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Shortcut the most recurring case: the user is in the dragging
552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * state and he is moving his finger.  We want to intercept this
553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * motion.
554321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
555321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
556321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if ((action == MotionEvent.ACTION_MOVE) &&
557321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                (mTouchState == TOUCH_STATE_SCROLLING)) {
558321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return true;
559321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
560321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
561321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
562321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_MOVE: {
563321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * whether the user has moved far enough from his original down touch.
566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
5671ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                if (mActivePointerId != INVALID_POINTER) {
5681ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    determineScrollingStart(ev);
5691ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                    break;
5701ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                }
5711ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
5721ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
5731ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // i.e. fall through to the next case (don't break)
5741ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
5751ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka                // while it's small- this was causing a crash before we checked for INVALID_POINTER)
576321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
577321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
578321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_DOWN: {
579321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX();
580321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float y = ev.getY();
581321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Remember location of down touch
582321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mDownMotionX = x;
583321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
584321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionY = y;
585321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = ev.getPointerId(0);
586321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = true;
587321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
588321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                /*
589321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * If being flinged and user touches the screen, initiate drag;
590321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * otherwise don't.  mScroller.isFinished should be false when
591321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 * being flinged.
592321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                 */
5935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final int xDist = (mScroller.getFinalX() - mScroller.getCurrX());
5945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
5955f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                if (finishedScrolling) {
5965f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_REST;
5975f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mScroller.abortAnimation();
5985f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                } else {
5995f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    mTouchState = TOUCH_STATE_SCROLLING;
6005f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
601321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
60286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                // check if this can be the beginning of a tap on the side of the pages
603321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // to scroll the current page
604321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if ((mTouchState != TOUCH_STATE_PREV_PAGE) &&
605321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        (mTouchState != TOUCH_STATE_NEXT_PAGE)) {
606321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (getChildCount() > 0) {
607e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                        int width = getMeasuredWidth();
608e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                        int offset = getRelativeChildOffset(mCurrentPage);
609e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                        if (x < offset) {
610321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_PREV_PAGE;
611e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                        } else if (x > (width - offset)) {
612321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                            mTouchState = TOUCH_STATE_NEXT_PAGE;
613321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        }
614321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
617321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
618321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
619321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_CANCEL:
620321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_UP:
621321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Release the drag
6220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageEndMoving();
623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_REST;
624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mActivePointerId = INVALID_POINTER;
626321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            case MotionEvent.ACTION_POINTER_UP:
630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                onSecondaryPointerUp(ev);
631321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                break;
632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
633321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
634321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
635321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * The only time we want to intercept motion events is if we are in the
636321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * drag mode.
637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
638321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mTouchState != TOUCH_STATE_REST;
639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
64180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    protected void animateClickFeedback(View v, final Runnable r) {
64280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // animate the view slightly to show click feedback running some logic after it is "pressed"
64380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        Animation anim = AnimationUtils.loadAnimation(getContext(),
64480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                R.anim.paged_view_click_feedback);
64580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        anim.setAnimationListener(new AnimationListener() {
64680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
64780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationStart(Animation animation) {}
64880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
64980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationRepeat(Animation animation) {
65080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                r.run();
65180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
65280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            @Override
65380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            public void onAnimationEnd(Animation animation) {}
65480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        });
65580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        v.startAnimation(anim);
65680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
65780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /*
659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * Determines if we should change the touch state to start scrolling after the
660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * user moves their touch point too far.
661321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void determineScrollingStart(MotionEvent ev) {
663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        /*
664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * Locally do absolute value. mLastMotionX is set to the y value
665321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         * of the down event.
666321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung         */
667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float x = ev.getX(pointerIndex);
669321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final float y = ev.getY(pointerIndex);
670321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int xDiff = (int) Math.abs(x - mLastMotionX);
671321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int yDiff = (int) Math.abs(y - mLastMotionY);
672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
673321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int touchSlop = mTouchSlop;
674321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xPaged = xDiff > mPagingTouchSlop;
675321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean xMoved = xDiff > touchSlop;
676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        boolean yMoved = yDiff > touchSlop;
677321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (xMoved || yMoved) {
6790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mUsePagingTouchSlop ? xPaged : xMoved) {
680321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll if the user moved far enough along the X axis
681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mTouchState = TOUCH_STATE_SCROLLING;
682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
6830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mTouchX = mScrollX;
6840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
6850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
686321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Either way, cancel any pending longpress
688321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mAllowLongPress) {
689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mAllowLongPress = false;
690321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Try canceling the long press. It could also have been scheduled
691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // by a distant descendant, so use the mAllowLongPress flag to block
692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // everything
69386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                final View currentPage = getPageAt(mCurrentPage);
69486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                currentPage.cancelLongPress();
695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean onTouchEvent(MotionEvent ev) {
701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mVelocityTracker == null) {
702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mVelocityTracker = VelocityTracker.obtain();
703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        mVelocityTracker.addMovement(ev);
705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int action = ev.getAction();
707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        switch (action & MotionEvent.ACTION_MASK) {
709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_DOWN:
710321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            /*
711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * If being flinged and user touches, stop the fling. isFinished
712321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             * will be false if being flinged.
713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung             */
714321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (!mScroller.isFinished()) {
715321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mScroller.abortAnimation();
716321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
717321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // Remember where the motion event started
719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mDownMotionX = mLastMotionX = ev.getX();
720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(0);
7210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (mTouchState == TOUCH_STATE_SCROLLING) {
7220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                pageBeginMoving();
7230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
726321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_MOVE:
727321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
728321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // Scroll to follow the motion event
729321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
730321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
731321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int deltaX = (int) (mLastMotionX - x);
732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mLastMotionX = x;
733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int sx = getScrollX();
735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (deltaX < 0) {
736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (sx > 0) {
7370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mTouchX += Math.max(-mTouchX, deltaX);
7380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
7390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (!mDeferScrollUpdate) {
7400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            scrollBy(Math.max(-sx, deltaX), 0);
7410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        } else {
7420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            // This will trigger a call to computeScroll() on next drawChild() call
7430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            invalidate();
7440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else if (deltaX > 0) {
747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    final int lastChildIndex = getChildCount() - 1;
748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    final int availableToScroll = getChildOffset(lastChildIndex) -
749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                        getRelativeChildOffset(lastChildIndex) - sx;
750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    if (availableToScroll > 0) {
7510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mTouchX += Math.min(availableToScroll, deltaX);
7520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
7530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (!mDeferScrollUpdate) {
7540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            scrollBy(Math.min(availableToScroll, deltaX), 0);
7550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        } else {
7560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            // This will trigger a call to computeScroll() on next drawChild() call
7570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            invalidate();
7580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    }
760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    awakenScrollBars();
762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else if ((mTouchState == TOUCH_STATE_PREV_PAGE) ||
764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    (mTouchState == TOUCH_STATE_NEXT_PAGE)) {
765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                determineScrollingStart(ev);
766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_UP:
770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mTouchState == TOUCH_STATE_SCROLLING) {
771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int activePointerId = mActivePointerId;
772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final int pointerIndex = ev.findPointerIndex(activePointerId);
773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final float x = ev.getX(pointerIndex);
774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                final VelocityTracker velocityTracker = mVelocityTracker;
775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
777321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                boolean isfling = Math.abs(mDownMotionX - x) > MIN_LENGTH_FOR_FLING;
778321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
7790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                final int snapVelocity = mSnapVelocity;
7800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                if (isfling && velocityX > snapVelocity && mCurrentPage > 0) {
7810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    snapToPageWithVelocity(mCurrentPage - 1, velocityX);
7820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                } else if (isfling && velocityX < -snapVelocity &&
78386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                        mCurrentPage < getChildCount() - 1) {
7840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    snapToPageWithVelocity(mCurrentPage + 1, velocityX);
785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
787321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (mVelocityTracker != null) {
790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    mVelocityTracker.recycle();
791321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    mVelocityTracker = null;
792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
793321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
79786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.max(0, mCurrentPage - 1);
79886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
79986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // at this point we have not moved beyond the touch slop
805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                // we can just page
80786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
80886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                if (nextPage != mCurrentPage) {
80986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung                    snapToPage(nextPage);
810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                } else {
811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    snapToDestination();
812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_CANCEL:
819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mTouchState = TOUCH_STATE_REST;
820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = INVALID_POINTER;
821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        case MotionEvent.ACTION_POINTER_UP:
824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            onSecondaryPointerUp(ev);
825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            break;
826321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
827321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return true;
829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
830321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
831321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    private void onSecondaryPointerUp(MotionEvent ev) {
832321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int pointerId = ev.getPointerId(pointerIndex);
835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (pointerId == mActivePointerId) {
836321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // This was our active pointer going up. Choose a new
837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // active pointer and adjust accordingly.
838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            // TODO: Make this decision more intelligent.
839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
841321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mLastMotionY = ev.getY(newPointerIndex);
842321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            mActivePointerId = ev.getPointerId(newPointerIndex);
843321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (mVelocityTracker != null) {
844321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                mVelocityTracker.clear();
845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void requestChildFocus(View child, View focused) {
851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.requestChildFocus(child, focused);
85286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int page = indexOfChild(child);
85386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (page >= 0 && !isInTouchMode()) {
85486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            snapToPage(page);
855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
858e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getChildIndexForRelativeOffset(int relativeOffset) {
859e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int childCount = getChildCount();
860e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        int left = getRelativeChildOffset(0);
861e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        for (int i = 0; i < childCount; ++i) {
862e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            final int right = (left + getChildAt(i).getMeasuredWidth());
863e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            if (left <= relativeOffset && relativeOffset <= right) {
864e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                return i;
865e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            }
866e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung            left = right;
867e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        }
868e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return -1;
869e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
870e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getRelativeChildOffset(int index) {
872321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2;
873321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
874321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
875321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected int getChildOffset(int index) {
876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (getChildCount() == 0)
877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            return 0;
878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int offset = getRelativeChildOffset(0);
880321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < index; ++i) {
881321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            offset += getChildAt(i).getMeasuredWidth();
882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return offset;
884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
885321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
886d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    int getPageNearestToCenterOfScreen() {
887321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenter = getMeasuredWidth();
888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int minDistanceFromScreenCenterIndex = -1;
889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int screenCenter = mScrollX + (getMeasuredWidth() / 2);
890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int childCount = getChildCount();
891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        for (int i = 0; i < childCount; ++i) {
8920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            View layout = (View) getChildAt(i);
893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childWidth = layout.getMeasuredWidth();
894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int halfChildWidth = (childWidth / 2);
895321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int childCenter = getChildOffset(i) + halfChildWidth;
896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
897321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
898321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenter = distanceFromScreenCenter;
899321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                minDistanceFromScreenCenterIndex = i;
900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
901321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
902d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        return minDistanceFromScreenCenterIndex;
903d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    }
904d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen
905d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen    protected void snapToDestination() {
906d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
908321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
9090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPageWithVelocity(int whichPage, int velocity) {
9100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // We ignore velocity in this implementation, but children (e.g. SmoothPagedView)
9110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // can use it
9120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage);
9130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
9140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
9150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage) {
9165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
918321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
9190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int duration) {
92086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
92286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int sX = getScrollX();
924321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final int delta = newX - sX;
9250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        snapToPage(whichPage, delta, duration);
9260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
9270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
9280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void snapToPage(int whichPage, int delta, int duration) {
9290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mNextPage = whichPage;
9300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
9310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        View focusedChild = getFocusedChild();
9320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (focusedChild != null && whichPage != mCurrentPage &&
9330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                focusedChild == getChildAt(mCurrentPage)) {
9340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            focusedChild.clearFocus();
9350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
9360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
9370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        pageBeginMoving();
938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        awakenScrollBars(duration);
939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (duration == 0) {
940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            duration = Math.abs(delta);
941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (!mScroller.isFinished()) mScroller.abortAnimation();
9440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
94580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
94680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        // only load some associated pages
94786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        loadAssociatedPages(mNextPage);
9480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        notifyPageSwitchListener();
949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        invalidate();
950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected Parcelable onSaveInstanceState() {
954321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        final SavedState state = new SavedState(super.onSaveInstanceState());
95586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        state.currentPage = mCurrentPage;
956321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return state;
957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    @Override
960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    protected void onRestoreInstanceState(Parcelable state) {
961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState savedState = (SavedState) state;
962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        super.onRestoreInstanceState(savedState.getSuperState());
96386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (savedState.currentPage != -1) {
96486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            mCurrentPage = savedState.currentPage;
965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
966321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
968321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollLeft() {
969321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
97086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
971321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
97286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage > 0) snapToPage(mNextPage - 1);
973321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
974321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
975321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
976321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void scrollRight() {
977321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (mScroller.isFinished()) {
97886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
979321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        } else {
98086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
981321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
982321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
983321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
98486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public int getPageForView(View v) {
985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        int result = -1;
986321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        if (v != null) {
987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            ViewParent vp = v.getParent();
988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            int count = getChildCount();
989321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            for (int i = 0; i < count; i++) {
990321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                if (vp == getChildAt(i)) {
991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                    return i;
992321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                }
993321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
994321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
995321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return result;
996321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
998321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    /**
999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     * @return True is long presses are still allowed for the current touch
1000321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung     */
1001321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public boolean allowLongPress() {
1002321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        return mAllowLongPress;
1003321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1004321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
10050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
10060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * Set true to allow long-press events to be triggered, usually checked by
10070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * {@link Launcher} to accept or block dpad-initiated long-presses.
10080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     */
10090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setAllowLongPress(boolean allowLongPress) {
10100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mAllowLongPress = allowLongPress;
10110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
10120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
1013321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public static class SavedState extends BaseSavedState {
101486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int currentPage = -1;
1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        SavedState(Parcelable superState) {
1017321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(superState);
1018321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1019321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1020321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        private SavedState(Parcel in) {
1021321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super(in);
102286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            currentPage = in.readInt();
1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1024321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        @Override
1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public void writeToParcel(Parcel out, int flags) {
1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            super.writeToParcel(out, flags);
102886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            out.writeInt(currentPage);
1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        }
1030321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        public static final Parcelable.Creator<SavedState> CREATOR =
1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                new Parcelable.Creator<SavedState>() {
1033321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState createFromParcel(Parcel in) {
1034321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState(in);
1035321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1036321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
1037321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            public SavedState[] newArray(int size) {
1038321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung                return new SavedState[size];
1039321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung            }
1040321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung        };
1041321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1042321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung
104386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    public void loadAssociatedPages(int page) {
10440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
10450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
10460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (page < count) {
1047e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int lowerPageBound = getAssociatedLowerPageBound(page);
1048e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung                int upperPageBound = getAssociatedUpperPageBound(page);
10490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                for (int i = 0; i < count; ++i) {
10500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    final ViewGroup layout = (ViewGroup) getChildAt(i);
10510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    final int childCount = layout.getChildCount();
10520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    if (lowerPageBound <= i && i <= upperPageBound) {
10530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (mDirtyPageContent.get(i)) {
10540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            syncPageItems(i);
10550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            mDirtyPageContent.set(i, false);
10560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
10570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    } else {
10580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        if (childCount > 0) {
10590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                            layout.removeAllViews();
10600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        }
10610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        mDirtyPageContent.set(i, true);
106280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                    }
106380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung                }
106480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung            }
106580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung        }
106680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung    }
106780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung
1068e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedLowerPageBound(int page) {
1069e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.max(0, page - 1);
1070e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1071e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    protected int getAssociatedUpperPageBound(int page) {
1072e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        final int count = getChildCount();
1073e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung        return Math.min(page + 1, count - 1);
1074e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung    }
1075e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung
10765f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void startChoiceMode(int mode, ActionMode.Callback callback) {
1077430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        if (isChoiceMode(CHOICE_MODE_NONE)) {
1078430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mChoiceMode = mode;
1079430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy            mActionMode = startActionMode(callback);
1080430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy        }
10815f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
10825f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
10832b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public void endChoiceMode() {
10845f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        if (!isChoiceMode(CHOICE_MODE_NONE)) {
10855f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            mChoiceMode = CHOICE_MODE_NONE;
10865f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            resetCheckedGrandchildren();
10879f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            mActionMode.finish();
10889f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            mActionMode = null;
10895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
10905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
10915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
10925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected boolean isChoiceMode(int mode) {
10935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return mChoiceMode == mode;
10945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
10955f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
10965f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected ArrayList<Checkable> getCheckedGrandchildren() {
10975f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        ArrayList<Checkable> checked = new ArrayList<Checkable>();
10985f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final int childCount = getChildCount();
10995f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < childCount; ++i) {
11005f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
11015f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final int grandChildCount = layout.getChildCount();
11025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            for (int j = 0; j < grandChildCount; ++j) {
11035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                final View v = layout.getChildAt(j);
11049f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                if (v instanceof Checkable && ((Checkable) v).isChecked()) {
11055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                    checked.add((Checkable) v);
11065f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung                }
11075f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            }
11085f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
11095f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        return checked;
11105f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
11115f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
11129f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    /**
11139f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * If in CHOICE_MODE_SINGLE and an item is checked, returns that item.
11149f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     * Otherwise, returns null.
11159f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy     */
11169f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    protected Checkable getSingleCheckedGrandchild() {
11179f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        if (mChoiceMode == CHOICE_MODE_SINGLE) {
11189f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            final int childCount = getChildCount();
11199f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            for (int i = 0; i < childCount; ++i) {
11209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                final PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
11219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                final int grandChildCount = layout.getChildCount();
11229f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                for (int j = 0; j < grandChildCount; ++j) {
11239f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    final View v = layout.getChildAt(j);
11249f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    if (v instanceof Checkable && ((Checkable) v).isChecked()) {
11259f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                        return (Checkable) v;
11269f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                    }
11279f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy                }
11289f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy            }
11299f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        }
11309f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy        return null;
11319f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy    }
11329f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy
11332b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    public Object getChosenItem() {
11342b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        View checkedView = (View) getSingleCheckedGrandchild();
11352b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        if (checkedView != null) {
11362b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            return checkedView.getTag();
11372b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        }
11382b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        return null;
11392b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy    }
11402b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy
11415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    protected void resetCheckedGrandchildren() {
11425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        // loop through children, and set all of their children to _not_ be checked
11435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        final ArrayList<Checkable> checked = getCheckedGrandchildren();
11445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        for (int i = 0; i < checked.size(); ++i) {
11455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            final Checkable c = checked.get(i);
11465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung            c.setChecked(false);
11475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung        }
11485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung    }
11495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung
115086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
115186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called ONLY to synchronize the number of pages that the paged view has.
115286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * To actually fill the pages with information, implement syncPageItems() below.  It is
115386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * guaranteed that syncPageItems() will be called for a particular page before it is shown,
115486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * and therefore, individual page items do not need to be updated in this method.
115586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1156321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPages();
115786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
115886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung    /**
115986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * This method is called to synchronize the items that are on a particular page.  If views on
116086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     * the page can be reused, then they should be updated within this method.
116186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung     */
1162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public abstract void syncPageItems(int page);
116386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
1164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    public void invalidatePageData() {
11650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (mContentIsRefreshable) {
11660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Update all the pages
11670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            syncPages();
116886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
11690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Mark each of the pages as dirty
11700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final int count = getChildCount();
11710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageContent.clear();
11720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            for (int i = 0; i < count; ++i) {
11730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mDirtyPageContent.add(true);
11740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
117586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
11760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Load any pages that are necessary for the current window of views
11770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            loadAssociatedPages(mCurrentPage);
11780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mDirtyPageAlpha = true;
11790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            requestLayout();
11800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
1181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung    }
1182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung}
1183