PagedView.java revision c3f9f4fcbd3fba3753335d5c9671c0893393b164
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 19af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurkaimport com.android.launcher.R; 209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 21321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context; 229c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray; 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; 295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode; 30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 31321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 32321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 3680baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation; 37e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.view.animation.AnimationUtils; 38e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator; 3968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohenimport android.view.animation.Animation.AnimationListener; 405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable; 41321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 42321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 43af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurkaimport java.util.ArrayList; 44af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurkaimport java.util.HashMap; 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 559cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_FLING = 25; 569cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung // The min drag distance to trigger a page shift (regardless of velocity) 579cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_MOVE = 200; 58321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 59e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int PAGE_SNAP_ANIMATION_DURATION = 550; 600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 62e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final float OVERSCROLL_DAMP_FACTOR = 0.08f; 63e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MINIMUM_SNAP_VELOCITY = 2200; 64e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MIN_FLING_VELOCITY = 250; 6568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 71321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 7668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mMaxScrollX; 770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 78321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 80321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 817426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionX; 827426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionY; 83f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private int mLastScreenCenter = -1; 84321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 89e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; 90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 92321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 957426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected boolean mAllowLongPress = true; 96321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 977426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected int mTouchSlop; 98321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 99321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 1009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageSpacing; 1019c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingTop; 1029c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingBottom; 1039c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingLeft; 1049c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingRight; 105df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutWidthGap; 106df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutHeightGap; 1079c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mCellCountX; 1089c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mCellCountY; 1097da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung protected boolean mCenterPagesVertically; 11068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected boolean mAllowOverScroll = true; 11168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mUnboundedScrollX; 112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 113d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // parameter that adjusts the layout to be optimized for CellLayouts with that scale factor 114d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected float mLayoutScale = 1.0f; 115d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1165f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected static final int INVALID_POINTER = -1; 117321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1185f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected int mActivePointerId = INVALID_POINTER; 119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 121321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private ArrayList<Boolean> mDirtyPageContent; 12386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private boolean mDirtyPageAlpha; 124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1255f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // choice modes 1265f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_NONE = 0; 1275f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_SINGLE = 1; 1285f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // Multiple selection mode is not supported by all Launcher actions atm 1295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_MULTIPLE = 2; 1309f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 131e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka protected int mChoiceMode; 1325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private ActionMode mActionMode; 1335f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 134241c3b451d7841ba08247beea784953eca4e8582Winson Chung protected PagedViewIconCache mPageViewIconCache; 135241c3b451d7841ba08247beea784953eca4e8582Winson Chung 1360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1501262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected boolean mIsPageMoving = false; 1511262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 152241c3b451d7841ba08247beea784953eca4e8582Winson Chung /** 153241c3b451d7841ba08247beea784953eca4e8582Winson Chung * Simple cache mechanism for PagedViewIcon outlines. 154241c3b451d7841ba08247beea784953eca4e8582Winson Chung */ 155241c3b451d7841ba08247beea784953eca4e8582Winson Chung class PagedViewIconCache { 156241c3b451d7841ba08247beea784953eca4e8582Winson Chung private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>(); 157241c3b451d7841ba08247beea784953eca4e8582Winson Chung 158241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void clear() { 159241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.clear(); 160241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 161241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void addOutline(Object key, Bitmap b) { 162241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.put(key, b); 163241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 164241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void removeOutline(Object key) { 165241c3b451d7841ba08247beea784953eca4e8582Winson Chung if (iconOutlineCache.containsKey(key)) { 166241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.remove(key); 167241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 168241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 169241c3b451d7841ba08247beea784953eca4e8582Winson Chung public Bitmap getOutline(Object key) { 170241c3b451d7841ba08247beea784953eca4e8582Winson Chung return iconOutlineCache.get(key); 171241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 172241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 173241c3b451d7841ba08247beea784953eca4e8582Winson Chung 17486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 17586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 176321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 177321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 178321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 179321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 183321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 185321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 186321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 187321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 1885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 189321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1909c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 1919c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView, defStyle, 0); 1929c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0); 1939c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingTop = a.getDimensionPixelSize( 1949c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingTop, 10); 1959c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingBottom = a.getDimensionPixelSize( 1969c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingBottom, 10); 1979c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingLeft = a.getDimensionPixelSize( 1989c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingLeft, 10); 1999c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingRight = a.getDimensionPixelSize( 2009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingRight, 10); 201df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutWidthGap = a.getDimensionPixelSize( 202df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutWidthGap, -1); 203df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutHeightGap = a.getDimensionPixelSize( 204df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutHeightGap, -1); 2059c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen a.recycle(); 2069c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen 207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 2080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 2140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 21586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 21686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 217241c3b451d7841ba08247beea784953eca4e8582Winson Chung mPageViewIconCache = new PagedViewIconCache(); 218e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mScroller = new Scroller(getContext(), new ScrollInterpolator()); 21986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 2207da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung mCenterPagesVertically = true; 221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 224321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 22886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 22986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 23086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 23186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 23686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 23886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 24086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 24186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 24486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 24886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 249321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 250321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int getScrollWidth() { 253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getWidth(); 254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 257bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * Updates the scroll of the current page immediately to its final scroll position. We use this 258bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of 259bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * the previous tab page. 260bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung */ 261bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung protected void updateCurrentPageScroll() { 262bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 263bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung scrollTo(newX, 0); 264bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung mScroller.setFinalX(newX); 265bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung } 266bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung 267bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung /** 26886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 27086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 27172e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (!mScroller.isFinished()) { 27272e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy mScroller.abortAnimation(); 27372e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 274d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // don't introduce any checks like mCurrentPage == currentPage here-- if we change the 275d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // the default 276d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (getChildCount() == 0) { 27772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy return; 27872e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 28086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 281bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung updateCurrentPageScroll(); 28280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 28486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 28886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 28986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2931262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy private void pageBeginMoving() { 2941262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = true; 2951262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageBeginMoving(); 2961262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 2971262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 2981262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy private void pageEndMoving() { 2991262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageEndMoving(); 3001262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = false; 3011262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3021262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 3030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3041262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageBeginMoving() { 3050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3081262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageEndMoving() { 3090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 311321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 31286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 313321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 314321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 315321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 316321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 317321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 318321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 31986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 320321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 32186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 323321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 324321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 32668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen public void scrollBy(int x, int y) { 32768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen scrollTo(mUnboundedScrollX + x, mScrollY + y); 32868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 32968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 33068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen @Override 3310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 33268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mUnboundedScrollX = x; 33368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 33468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (x < 0) { 33568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(0, y); 33668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 33768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x); 33868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 33968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else if (x > mMaxScrollX) { 34068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(mMaxScrollX, y); 34168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 34268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x - mMaxScrollX); 34368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 34468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 34568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(x, y); 34668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 34768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 3480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 3490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 3500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 3530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 3555f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 3570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 3580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 35986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 3605f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 36186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 36286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 3630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 36473aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // We don't want to trigger a page end moving unless the page has settled 36573aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // and the user has stopped scrolling 36673aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen if (mTouchState == TOUCH_STATE_REST) { 36773aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen pageEndMoving(); 36873aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen } 3690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 370321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 3720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 3750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 3760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 377321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 378321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 379321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 380321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 381321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 382321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 383321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 384321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 385321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 386321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 3876b879f0a5885274a85333531e091283405d490ccAdam Lesinski /* Allow the height to be set as WRAP_CONTENT. This allows the particular case 3886b879f0a5885274a85333531e091283405d490ccAdam Lesinski * of the All apps view on XLarge displays to not take up more space then it needs. Width 3896b879f0a5885274a85333531e091283405d490ccAdam Lesinski * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect 3906b879f0a5885274a85333531e091283405d490ccAdam Lesinski * each page to have the same width. 3916b879f0a5885274a85333531e091283405d490ccAdam Lesinski */ 392321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 3936b879f0a5885274a85333531e091283405d490ccAdam Lesinski int heightSize = MeasureSpec.getSize(heightMeasureSpec); 3946b879f0a5885274a85333531e091283405d490ccAdam Lesinski int maxChildHeight = 0; 3956b879f0a5885274a85333531e091283405d490ccAdam Lesinski 3966b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 397321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 3995f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // unless they were set to WRAP_CONTENT 400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 4025f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // disallowing padding in paged view (just pass 0) 4035f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final View child = getChildAt(i); 4045f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 4055f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4065f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childWidthMode; 4075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.width == LayoutParams.WRAP_CONTENT) { 4085f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.AT_MOST; 4095f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4105f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.EXACTLY; 4115f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4125f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4135f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childHeightMode; 4145f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.height == LayoutParams.WRAP_CONTENT) { 4155f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.AT_MOST; 4165f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4175f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.EXACTLY; 4185f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4195f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4205f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childWidthMeasureSpec = 4215f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka MeasureSpec.makeMeasureSpec(widthSize, childWidthMode); 4225f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childHeightMeasureSpec = 4236b879f0a5885274a85333531e091283405d490ccAdam Lesinski MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode); 4245f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4255f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4266b879f0a5885274a85333531e091283405d490ccAdam Lesinski maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); 4276b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 4286b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4296b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (heightMode == MeasureSpec.AT_MOST) { 4306b879f0a5885274a85333531e091283405d490ccAdam Lesinski heightSize = maxChildHeight + verticalPadding; 431321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 432faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen if (childCount > 0) { 433faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1); 434faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } else { 435faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = 0; 436faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } 437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 438321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setMeasuredDimension(widthSize, heightSize); 439cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka } 440321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 441af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka protected void moveToNewPageWithoutMovingCellLayouts(int newCurrentPage) { 442af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 443af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int delta = newX - mScrollX; 444af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 445af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka final int screenCount = getChildCount(); 446af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka for (int i = 0; i < screenCount; i++) { 447af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka CellLayout cl = (CellLayout) getChildAt(i); 448af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka cl.setX(cl.getX() + delta); 449af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 450af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka setCurrentPage(newCurrentPage); 451af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 452af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 453d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // A layout scale of 1.0f assumes that the CellLayouts, in their unshrunken state, have a 454d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // scale of 1.0f. A layout scale of 0.8f assumes the CellLayouts have a scale of 0.8f, and 455d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // tightens the layout accordingly 456d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka public void setLayoutScale(float childrenScale) { 457d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mLayoutScale = childrenScale; 458d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 459d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Now we need to do a re-layout, but preserving absolute X and Y coordinates 460d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childCount = getChildCount(); 461d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenX[] = new float[childCount]; 462d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenY[] = new float[childCount]; 463d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 464d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 465d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenX[i] = child.getX(); 466d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenY[i] = child.getY(); 467d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 468d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka onLayout(false, mLeft, mTop, mRight, mBottom); 469d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 470d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 471d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setX(childrenX[i]); 472d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setY(childrenY[i]); 473d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 474d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Also, the page offset has changed (since the pages are now smaller); 475d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // update the page offset, but again preserving absolute X and Y coordinates 476d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka moveToNewPageWithoutMovingCellLayouts(mCurrentPage); 477d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 478d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 479cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka @Override 48028750fba6a2d141eb9a1e566718c17236030b815Michael Jurka protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 4815f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(false); 483cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 484cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka scrollTo(newX, 0); 485cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka mScroller.setFinalX(newX); 486321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(true); 487321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mFirstLayout = false; 488321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4906b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 494e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung childLeft = getRelativeChildOffset(0); 495321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 496321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 497321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final View child = getChildAt(i); 499321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 500d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int childWidth = getScaledMeasuredWidth(child); 5016b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int childHeight = child.getMeasuredHeight(); 5026b879f0a5885274a85333531e091283405d490ccAdam Lesinski int childTop = mPaddingTop; 5036b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (mCenterPagesVertically) { 5046b879f0a5885274a85333531e091283405d490ccAdam Lesinski childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2; 5056b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 506d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 5076b879f0a5885274a85333531e091283405d490ccAdam Lesinski child.layout(childLeft, childTop, 508d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childLeft + child.getMeasuredWidth(), childTop + childHeight); 5099c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen childLeft += childWidth + mPageSpacing; 510321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 511321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 512d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 513d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mFirstLayout = false; 514d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 515321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 516321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 517e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void updateAdjacentPagesAlpha() { 5180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 5190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { 520e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int halfScreenSize = getMeasuredWidth() / 2; 521e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int screenCenter = mScrollX + halfScreenSize; 5220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = getChildCount(); 5230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < childCount; ++i) { 5240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 525d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 5260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int halfChildWidth = (childWidth / 2); 5270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childCenter = getChildOffset(i) + halfChildWidth; 528e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 529b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // On the first layout, we may not have a width nor a proper offset, so for now 530b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // we should just assume full page width (and calculate the offset according to 531b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // that). 532b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung if (childWidth <= 0) { 533b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childWidth = getMeasuredWidth(); 534b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childCenter = (i * childWidth) + (childWidth / 2); 535b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung } 536b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 537e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int d = halfChildWidth; 538e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int distanceFromScreenCenter = childCenter - screenCenter; 539e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (distanceFromScreenCenter > 0) { 540e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i > 0) { 541d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i - 1)) / 2; 542e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 5430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 544e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i < childCount - 1) { 545d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i + 1)) / 2; 546e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 5470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 5489c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen d += mPageSpacing; 549e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 550b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // Preventing potential divide-by-zero 551b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung d = Math.max(1, d); 552b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 553e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d; 554e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); 555e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float alpha = 1.0f - dimAlpha; 556e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 557f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (alpha < ALPHA_QUANTIZE_LEVEL) { 558f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 0.0f; 559f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) { 560f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 1.0f; 561f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 562f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 5630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (Float.compare(alpha, layout.getAlpha()) != 0) { 5640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.setAlpha(alpha); 5650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 566affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 5670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = false; 568321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 569321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 570e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 5710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 572f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen protected void screenScrolled(int screenCenter) { 573f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 574f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 575e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung @Override 576e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void dispatchDraw(Canvas canvas) { 577f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int halfScreenSize = getMeasuredWidth() / 2; 578f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int screenCenter = mScrollX + halfScreenSize; 579f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 580f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (screenCenter != mLastScreenCenter) { 581f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen screenScrolled(screenCenter); 582f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen updateAdjacentPagesAlpha(); 583f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mLastScreenCenter = screenCenter; 584f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 5850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 586e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung // Find out which screens are visible; as an optimization we only call draw on them 5870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // As an optimization, this code assumes that all pages have the same width as the 0th 5880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page. 5890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 590c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka if (pageCount > 0) { 591d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int pageWidth = getScaledMeasuredWidth(getChildAt(0)); 592c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int screenWidth = getMeasuredWidth(); 593c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int x = getRelativeChildOffset(0) + pageWidth; 594c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int leftScreen = 0; 595c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int rightScreen = 0; 596c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka while (x <= mScrollX) { 597c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka leftScreen++; 598c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(leftScreen)) + mPageSpacing; 599c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 600c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = leftScreen; 601c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung while (x < mScrollX + screenWidth && rightScreen < pageCount) { 602c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen++; 603c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung if (rightScreen < pageCount) { 604c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(rightScreen)) + mPageSpacing; 605c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung } 606c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 607c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = Math.min(getChildCount() - 1, rightScreen); 6080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 609c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final long drawingTime = getDrawingTime(); 61029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung // Clip to the bounds 61129d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.save(); 61229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft, 61329d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung mScrollY + mBottom - mTop); 61429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 615c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka for (int i = leftScreen; i <= rightScreen; i++) { 616c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka drawChild(canvas, getChildAt(i), drawingTime); 617c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 61829d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.restore(); 6190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 620321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 621321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 622321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 62486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 62586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 62686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 631321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 633321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 63486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 63586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 63686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 63886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 64086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v.requestFocus(direction, previouslyFocusedRect); 643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 646321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 647321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 648321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 649321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 65086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 65186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 652321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 653321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 654321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 65586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 65686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 657321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 661321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 66586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 66686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 66986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 67086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 671321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 67386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 67486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 675321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 677321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 679321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 680321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 68186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 68386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 68486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 685321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 686321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 68886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 690321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 710321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 712321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 714321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 71586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getChildAt(mCurrentPage); 71686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 717321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 726321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 727321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 728321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 72945e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 73045e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 73145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 740321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 741321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 7491ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka if (mActivePointerId != INVALID_POINTER) { 7501ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka determineScrollingStart(ev); 7511ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka break; 7521ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka } 7531ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 7541ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 7551ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // i.e. fall through to the next case (don't break) 7561ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 7571ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // while it's small- this was causing a crash before we checked for INVALID_POINTER) 758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 775fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 7765f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 7775f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 7785f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 7795f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 7805f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 7815f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 7825f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 78486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 78621f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen if ((mTouchState != TOUCH_STATE_PREV_PAGE) && !handlePagingClicks() && 787321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState != TOUCH_STATE_NEXT_PAGE)) { 788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 789e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int width = getMeasuredWidth(); 790e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int offset = getRelativeChildOffset(mCurrentPage); 79121f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen if (x < offset - mPageSpacing) { 792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 79321f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen } else if (x > (width - offset + mPageSpacing)) { 794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 8021d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 8031d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown case MotionEvent.ACTION_CANCEL: 804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 82180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 82280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 82380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung Animation anim = AnimationUtils.loadAnimation(getContext(), 82480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung R.anim.paged_view_click_feedback); 82580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung anim.setAnimationListener(new AnimationListener() { 82680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 82780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationStart(Animation animation) {} 82880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 82980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationRepeat(Animation animation) { 83080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 83180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 83280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 83380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationEnd(Animation animation) {} 83480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 83580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung v.startAnimation(anim); 83680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 83780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 841321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 8421adf5391a3a3d215b226adf3702019c22a99e3b1Michael Jurka protected void determineScrollingStart(MotionEvent ev) { 843321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 844321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int touchSlop = mTouchSlop; 854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (xMoved || yMoved) { 8590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 8630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 8640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 8650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 867321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 868321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mAllowLongPress) { 869321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Try canceling the long press. It could also have been scheduled 871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // by a distant descendant, so use the mAllowLongPress flag to block 872321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // everything 87386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getPageAt(mCurrentPage); 87410fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung if (currentPage != null) { 87510fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung currentPage.cancelLongPress(); 87610fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung } 877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 880321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 88121f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen protected boolean handlePagingClicks() { 88221f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen return false; 88321f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen } 88421f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen 885e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // This curve determines how the effect of scrolling over the limits of the page dimishes 886e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as the user pulls further and further from the bounds 887e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private float overScrollInfluenceCurve(float f) { 888e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 1.0f; 889e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return f * f * f + 1.0f; 890e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 891e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 89268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected void overScroll(float amount) { 893e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int screenSize = getMeasuredWidth(); 894e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 895e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float f = (amount / screenSize); 896e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 897e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (f == 0) return; 898e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 899e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 900e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); 90168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (amount < 0) { 90268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = overScrollAmount; 90368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 90468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = mMaxScrollX + overScrollAmount; 90568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 90668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 90768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 90868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 909321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 910321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 91145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 91245e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onTouchEvent(ev); 91345e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 914b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka acquireVelocityTrackerAndAddMovement(ev); 915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 918321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 919321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 920321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 922321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 924321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 925321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 929321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 9310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 9320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 9330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 935321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 936321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int deltaX = (int) (mLastMotionX - x); 942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 94468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (deltaX != 0) { 94568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mTouchX += deltaX; 94668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 94768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (!mDeferScrollUpdate) { 94868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen scrollBy(deltaX, 0); 94968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 95068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 954321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 955564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen } else { 956321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 966321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 9689cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung final int deltaX = (int) (x - mDownMotionX); 9699cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung boolean isfling = Math.abs(deltaX) > MIN_LENGTH_FOR_FLING; 9709cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE; 971321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 9720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 9739cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung if ((isSignificantMove && deltaX > 0 || 974e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen (isfling && velocityX > snapVelocity)) && mCurrentPage > 0) { 9750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage - 1, velocityX); 9769cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung } else if ((isSignificantMove && deltaX < 0 || 9779cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung (isfling && velocityX < -snapVelocity)) && 97886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 9790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage + 1, velocityX); 980321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 981321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 982321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 98321f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen } else if (mTouchState == TOUCH_STATE_PREV_PAGE && !handlePagingClicks()) { 984321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 986321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 98786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 98886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 98986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 990321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 992321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 99321f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen } else if (mTouchState == TOUCH_STATE_NEXT_PAGE && !handlePagingClicks()) { 994321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 995321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 996321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 99786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 99886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 99986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1000321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1001321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1002321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 10031d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } else { 10041d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 1005321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1006321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1008b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1010321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1011321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 1012b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 1013b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka snapToDestination(); 1014b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1017b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1018321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1019321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1020321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 1021321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1024321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1028b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { 1029b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker == null) { 1030b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = VelocityTracker.obtain(); 1031b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1032b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.addMovement(ev); 1033b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1034b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1035b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void releaseVelocityTracker() { 1036b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker != null) { 1037b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.recycle(); 1038b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = null; 1039b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1040b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1041b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1042321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 1043321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 1044321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 1045321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 1046321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 1047321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 1048321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 1049321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 1050321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1051321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 1052321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 1053321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 1054321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 1055321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 1056321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 10581d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown if (mTouchState == TOUCH_STATE_REST) { 10591d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 10601d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 10611d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 10621d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown 10631d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown protected void onWallpaperTap(MotionEvent ev) { 1064321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1065321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1067321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 1068321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 106986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 107086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page >= 0 && !isInTouchMode()) { 107186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 1072321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1075e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getChildIndexForRelativeOffset(int relativeOffset) { 1076e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int childCount = getChildCount(); 10779c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int left; 10789c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int right; 1079e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung for (int i = 0; i < childCount; ++i) { 10809c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen left = getRelativeChildOffset(i); 1081d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka right = (left + getScaledMeasuredWidth(getChildAt(i))); 1082e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung if (left <= relativeOffset && relativeOffset <= right) { 1083e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return i; 1084e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1085e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1086e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return -1; 1087e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1088e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 1089321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getRelativeChildOffset(int index) { 1090321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2; 1091321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1092321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1093321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getChildOffset(int index) { 1094321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) 1095321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return 0; 1096321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1097321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int offset = getRelativeChildOffset(0); 1098321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < index; ++i) { 1099d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka offset += getScaledMeasuredWidth(getChildAt(i)) + mPageSpacing; 1100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return offset; 1102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1104d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected int getScaledMeasuredWidth(View child) { 1105d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka return (int) (child.getMeasuredWidth() * mLayoutScale + 0.5f); 1106d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 1107d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1108d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen int getPageNearestToCenterOfScreen() { 1109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenter = getMeasuredWidth(); 1110321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 1111321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 1112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 1113321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 11140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 1115d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 1116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 1117321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 1118321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 1119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 1120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 1121321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 1122321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1123321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1124d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen return minDistanceFromScreenCenterIndex; 1125d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen } 1126d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen 1127d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen protected void snapToDestination() { 1128d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); 1129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1130321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1131e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static class ScrollInterpolator implements Interpolator { 1132e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public ScrollInterpolator() { 1133e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1134e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1135e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public float getInterpolation(float t) { 1136e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen t -= 1.0f; 1137e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return t*t*t*t*t + 1; 1138e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1139e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1140e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1141e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // We want the duration of the page snap animation to be influenced by the distance that 1142e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // the screen has to travel, however, we don't want this duration to be effected in a 1143e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // purely linear fashion. Instead, we use this method to moderate the effect that the distance 1144e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // of travel has on the overall snap duration. 1145e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceInfluenceForSnapDuration(float f) { 1146e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 0.5f; // center the values about 0. 1147e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f *= 0.3f * Math.PI / 2.0f; 1148e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return (float) Math.sin(f); 1149e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1150e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 11510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 1152e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); 1153e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int halfScreenSize = getMeasuredWidth() / 2; 1154e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1155e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 1156e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int delta = newX - mUnboundedScrollX; 1157e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int duration = 0; 1158e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1159e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (Math.abs(velocity) < MIN_FLING_VELOCITY) { 1160e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // If the velocity is low enough, then treat this more as an automatic page advance 1161e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as opposed to an apparent physical response to flinging 1162e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1163e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return; 1164e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1165e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1166e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // Here we compute a "distance" that will be used in the computation of the overall 1167e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // snap duration. This is a function of the actual distance that needs to be traveled; 1168e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we keep this value close to half screen size in order to reduce the variance in snap 1169e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // duration as a function of the distance the page needs to travel. 1170e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceRatio = 1.0f * Math.abs(delta) / 2 * halfScreenSize; 1171e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distance = halfScreenSize + halfScreenSize * 1172e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen distanceInfluenceForSnapDuration(distanceRatio); 1173e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1174e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.abs(velocity); 1175e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); 1176e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1177e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we want the page's snap velocity to approximately match the velocity at which the 1178e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // user flings, so we scale the duration by a value near to the derivative of the scroll 1179e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // interpolator at zero, ie. 5. We use 6 to make it a little slower. 1180e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen duration = 6 * Math.round(1000 * Math.abs(distance / velocity)); 1181e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1182e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, delta, duration); 11830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 11840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 11850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 11865f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1187321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 11890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 119086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 1191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 119286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 119368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen final int sX = mUnboundedScrollX; 1194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 11950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 11960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 11970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 11980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 11990142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 12000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 12020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 12030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild == getChildAt(mCurrentPage)) { 12040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 12050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 12060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 1209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 1210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 1211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 121468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); 121580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 121680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // only load some associated pages 121786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung loadAssociatedPages(mNextPage); 12180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 1219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 1220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected Parcelable onSaveInstanceState() { 1224321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final SavedState state = new SavedState(super.onSaveInstanceState()); 122586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung state.currentPage = mCurrentPage; 1226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return state; 1227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onRestoreInstanceState(Parcelable state) { 1231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState savedState = (SavedState) state; 1232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.onRestoreInstanceState(savedState.getSuperState()); 123386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (savedState.currentPage != -1) { 123486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = savedState.currentPage; 1235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 1239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 124086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 1241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 124286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 1243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 1247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 124886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 1249321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 125086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 1251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 125486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 1255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 1256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 1257321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 1258321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 1259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (vp == getChildAt(i)) { 1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 1262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 1266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 1269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 1270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1271321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 1272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 1273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 12760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 12770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 12780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 12790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 12800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 12810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 12820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 128486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 1288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 129286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 1293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 1297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 129886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 1299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 1302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 1303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 1304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 1305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 1308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 1309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 1311321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1312321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 131386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void loadAssociatedPages(int page) { 13140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 13150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 13160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 1317e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int lowerPageBound = getAssociatedLowerPageBound(page); 1318e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int upperPageBound = getAssociatedUpperPageBound(page); 13190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 13200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final ViewGroup layout = (ViewGroup) getChildAt(i); 13210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = layout.getChildCount(); 13220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 13230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 13240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPageItems(i); 13250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 13260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 13280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (childCount > 0) { 13290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.removeAllViews(); 13300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, true); 133280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 133380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 133480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 133580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 133680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 133780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1338e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedLowerPageBound(int page) { 1339e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.max(0, page - 1); 1340e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1341e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedUpperPageBound(int page) { 1342e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int count = getChildCount(); 1343e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.min(page + 1, count - 1); 1344e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1345e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 13465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void startChoiceMode(int mode, ActionMode.Callback callback) { 1347430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy if (isChoiceMode(CHOICE_MODE_NONE)) { 1348430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mChoiceMode = mode; 1349430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mActionMode = startActionMode(callback); 1350430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy } 13515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13532b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public void endChoiceMode() { 13545f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (!isChoiceMode(CHOICE_MODE_NONE)) { 13555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 13565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung resetCheckedGrandchildren(); 1357e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka if (mActionMode != null) mActionMode.finish(); 13589f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy mActionMode = null; 13595f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13605f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13615f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13625f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected boolean isChoiceMode(int mode) { 13635f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return mChoiceMode == mode; 13645f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13655f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13665f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected ArrayList<Checkable> getCheckedGrandchildren() { 13675f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung ArrayList<Checkable> checked = new ArrayList<Checkable>(); 13685f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int childCount = getChildCount(); 13695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < childCount; ++i) { 1370d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung final ViewGroup layout = (ViewGroup) getChildAt(i); 13715f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int grandChildCount = layout.getChildCount(); 13725f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int j = 0; j < grandChildCount; ++j) { 13735f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final View v = layout.getChildAt(j); 13749f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 13755f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung checked.add((Checkable) v); 13765f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13775f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13785f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13795f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return checked; 13805f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13815f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13829f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy /** 13839f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * If in CHOICE_MODE_SINGLE and an item is checked, returns that item. 13849f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * Otherwise, returns null. 13859f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy */ 13869f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy protected Checkable getSingleCheckedGrandchild() { 13879f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (mChoiceMode == CHOICE_MODE_SINGLE) { 13889f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int childCount = getChildCount(); 13899f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int i = 0; i < childCount; ++i) { 1390d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung final ViewGroup layout = (ViewGroup) getChildAt(i); 13919f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int grandChildCount = layout.getChildCount(); 13929f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int j = 0; j < grandChildCount; ++j) { 13939f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final View v = layout.getChildAt(j); 13949f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 13959f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return (Checkable) v; 13969f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 13979f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 13989f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 13999f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 14009f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return null; 14019f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 14029f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 14032b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public Object getChosenItem() { 14042b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy View checkedView = (View) getSingleCheckedGrandchild(); 14052b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy if (checkedView != null) { 14062b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy return checkedView.getTag(); 14072b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy } 14082b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy return null; 14092b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy } 14102b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy 14115f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void resetCheckedGrandchildren() { 14125f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // loop through children, and set all of their children to _not_ be checked 14135f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final ArrayList<Checkable> checked = getCheckedGrandchildren(); 14145f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < checked.size(); ++i) { 14155f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final Checkable c = checked.get(i); 14165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung c.setChecked(false); 14175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14185f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14195f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 142086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 142186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 142286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 142386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 142486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 142586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 142786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 142886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 142986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 143086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 143186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1432321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPageItems(int page); 143386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1434321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void invalidatePageData() { 14350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 14360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 14370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 143886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 14390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 14400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 14410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 14420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 14430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 14440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 144586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 14460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 14470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka loadAssociatedPages(mCurrentPage); 14480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = true; 1449b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung updateAdjacentPagesAlpha(); 14500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 14510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1452321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1454