PagedView.java revision 0cad1116add5303d7c06cc21182527cebc42aff6
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 19228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.Animator; 20228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorInflater; 21228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorListenerAdapter; 22228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.ObjectAnimator; 23bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chungimport android.animation.ValueAnimator; 24321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context; 259c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray; 26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas; 27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect; 28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel; 29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable; 30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet; 31785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chungimport android.util.Log; 325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode; 33185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.InputDevice; 34185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.KeyEvent; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 38321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 39321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 40321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 416a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityEvent; 42c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chungimport android.view.accessibility.AccessibilityManager; 436a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityNodeInfo; 44e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator; 455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable; 46007c69867d821ea2b271398577a8b3440b3a7046Winson Chungimport android.widget.ImageView; 47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 490499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R; 5080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 516a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport java.util.ArrayList; 526a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 53321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/** 54321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a 550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages" 56321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 57321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup { 58321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final String TAG = "PagedView"; 59785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung private static final boolean DEBUG = false; 600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final int INVALID_PAGE = -1; 61321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 6286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // the min drag distance for a fling to register, to prevent random page shifts 639cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_FLING = 25; 64321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 65e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int PAGE_SNAP_ANIMATION_DURATION = 550; 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 68b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen private static final float OVERSCROLL_ACCELERATE_FACTOR = 2; 69b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung private static final float OVERSCROLL_DAMP_FACTOR = 0.14f; 70e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MINIMUM_SNAP_VELOCITY = 2200; 71e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MIN_FLING_VELOCITY = 250; 72b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; 7300481b3ed322b4f36b669201d2287964febda65fAdam Cohen // The page is moved more than halfway, automatically move to the next page on touch up. 7400481b3ed322b4f36b669201d2287964febda65fAdam Cohen private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f; 7568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 79b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected float mDensity; 800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 82321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 8768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mMaxScrollX; 880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 89321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 91321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 927426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionX; 93c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung protected float mLastMotionXRemainder; 947426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionY; 95aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen protected float mTotalMotionX; 96f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private int mLastScreenCenter = -1; 9773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen private int[] mChildOffsets; 9873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen private int[] mChildRelativeOffsets; 9973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen private int[] mChildOffsetsWithLayoutScale; 100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 1020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 1030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 1040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 105e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; 106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 1082591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen protected boolean mForceScreenScrolled = false; 109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 111321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1127426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected boolean mAllowLongPress = true; 113321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1147426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected int mTouchSlop; 115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 1171908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung private int mMinimumWidth; 1189c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageSpacing; 1199c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingTop; 1209c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingBottom; 1219c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingLeft; 1229c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingRight; 123df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutWidthGap; 124df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutHeightGap; 12587b1490c5a100619648b251cb2be05c457bede08Michael Jurka protected int mCellCountX = 0; 12687b1490c5a100619648b251cb2be05c457bede08Michael Jurka protected int mCellCountY = 0; 1277da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung protected boolean mCenterPagesVertically; 12868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected boolean mAllowOverScroll = true; 12968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mUnboundedScrollX; 130dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka protected int[] mTempVisiblePagesRange = new int[2]; 131321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 132ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen // mOverScrollX is equal to mScrollX when we're within the normal scroll range. Otherwise 133ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen // it is equal to the scaled overscroll position. We use a separate value so as to prevent 134ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen // the screens from continuing to translate beyond the normal bounds. 135ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen protected int mOverScrollX; 136ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen 1378c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // parameter that adjusts the layout to be optimized for pages with that scale factor 138d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected float mLayoutScale = 1.0f; 139d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1405f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected static final int INVALID_POINTER = -1; 141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1425f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected int mActivePointerId = INVALID_POINTER; 143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 14486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 146e326f186af6b00e4ea32849f1527254c669d0600Michael Jurka protected ArrayList<Boolean> mDirtyPageContent; 147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // choice modes 1495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_NONE = 0; 1505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_SINGLE = 1; 1515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // Multiple selection mode is not supported by all Launcher actions atm 1525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_MULTIPLE = 2; 1539f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 154e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka protected int mChoiceMode; 1555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private ActionMode mActionMode; 1565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 1570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1711262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected boolean mIsPageMoving = false; 1721262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 173f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung // All syncs and layout passes are deferred until data is ready. 174f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected boolean mIsDataReady = false; 175f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 176007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Scrolling indicator 177bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chung private ValueAnimator mScrollIndicatorAnimator; 178007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private ImageView mScrollIndicator; 179f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung private int mScrollIndicatorPaddingLeft; 180f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung private int mScrollIndicatorPaddingRight; 181007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private boolean mHasScrollIndicator = true; 182a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFadeInDuration = 150; 183a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFadeOutDuration = 650; 184a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFlashDuration = 650; 185007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 186b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // If set, will defer loading associated pages until the scrolling settles 1874e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung private boolean mDeferLoadAssociatedPagesUntilScrollCompletes; 188b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 18986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 19086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 197321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 199321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 2035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2059c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 2069c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView, defStyle, 0); 20760b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0)); 2089c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingTop = a.getDimensionPixelSize( 2091908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingTop, 0); 2109c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingBottom = a.getDimensionPixelSize( 2111908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingBottom, 0); 2129c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingLeft = a.getDimensionPixelSize( 2131908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingLeft, 0); 2149c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingRight = a.getDimensionPixelSize( 2151908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingRight, 0); 216df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutWidthGap = a.getDimensionPixelSize( 2177d7541e7b48fdc114c24b3b0aa75e70d7228041eWinson Chung R.styleable.PagedView_pageLayoutWidthGap, 0); 218df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutHeightGap = a.getDimensionPixelSize( 2197d7541e7b48fdc114c24b3b0aa75e70d7228041eWinson Chung R.styleable.PagedView_pageLayoutHeightGap, 0); 220f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung mScrollIndicatorPaddingLeft = 221f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0); 222f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung mScrollIndicatorPaddingRight = 223f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0); 2249c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen a.recycle(); 2259c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen 226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 2270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 2330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 23486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 23586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 236e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mScroller = new Scroller(getContext(), new ScrollInterpolator()); 23786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 2387da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung mCenterPagesVertically = true; 239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 244b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen mDensity = getResources().getDisplayMetrics().density; 245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 24786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 24886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 24986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 25086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 255f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung * Called by subclasses to mark that data is ready, and that we can begin loading and laying 256f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung * out pages. 257f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung */ 258f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected void setDataIsReady() { 259f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung mIsDataReady = true; 260f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 261f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected boolean isDataReady() { 262f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return mIsDataReady; 263f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 264f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 265f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung /** 26686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 26886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 27086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 27186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 27486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 27886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 282ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen protected int indexToPage(int index) { 283ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen return index; 284ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen } 285ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen 286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 287bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * Updates the scroll of the current page immediately to its final scroll position. We use this 288bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of 289bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * the previous tab page. 290bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung */ 291bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung protected void updateCurrentPageScroll() { 292bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 293bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung scrollTo(newX, 0); 294bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung mScroller.setFinalX(newX); 295bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung } 296bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung 297bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung /** 29886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 30086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 30172e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (!mScroller.isFinished()) { 30272e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy mScroller.abortAnimation(); 30372e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 304d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // don't introduce any checks like mCurrentPage == currentPage here-- if we change the 305d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // the default 306d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (getChildCount() == 0) { 30772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy return; 30872e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 31086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 311bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung updateCurrentPageScroll(); 3125a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung updateScrollingIndicator(); 31386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 314a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung invalidate(); 315321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 316321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 3170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 31886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 31986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 320321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 321321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 323ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageBeginMoving() { 324d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka if (!mIsPageMoving) { 325d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka mIsPageMoving = true; 326d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka onPageBeginMoving(); 327d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka } 3281262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3291262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 330ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageEndMoving() { 331d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka if (mIsPageMoving) { 332d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka mIsPageMoving = false; 333d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka onPageEndMoving(); 334d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka } 3351262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3361262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 33726976d9b53161f80faf822d482ac771b8621cf31Adam Cohen protected boolean isPageMoving() { 33826976d9b53161f80faf822d482ac771b8621cf31Adam Cohen return mIsPageMoving; 33926976d9b53161f80faf822d482ac771b8621cf31Adam Cohen } 34026976d9b53161f80faf822d482ac771b8621cf31Adam Cohen 3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3421262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageBeginMoving() { 343430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka showScrollingIndicator(false); 3440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3471262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageEndMoving() { 348007c69867d821ea2b271398577a8b3440b3a7046Winson Chung hideScrollingIndicator(false); 3490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 35286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 355321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 357321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 358321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 35986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 36186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 362321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 365321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 36668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen public void scrollBy(int x, int y) { 36768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen scrollTo(mUnboundedScrollX + x, mScrollY + y); 36868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 36968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 37068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen @Override 3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 37268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mUnboundedScrollX = x; 37368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 37468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (x < 0) { 37568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(0, y); 37668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 37768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x); 37868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 37968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else if (x > mMaxScrollX) { 38068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(mMaxScrollX, y); 38168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 38268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x - mMaxScrollX); 38368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 38468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 385ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = x; 38668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(x, y); 38768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 38868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 3890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 3900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 3910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 3940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 395321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 396557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung // Don't bother scrolling if the page does not need to be moved 397557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung if (mScrollX != mScroller.getCurrX() || mScrollY != mScroller.getCurrY()) { 398557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 399557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung } 4000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 4010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 40286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 40386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 40486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 4050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 406b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 407b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // Load the associated pages if necessary 4084e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung if (mDeferLoadAssociatedPagesUntilScrollCompletes) { 409b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung loadAssociatedPages(mCurrentPage); 4104e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung mDeferLoadAssociatedPagesUntilScrollCompletes = false; 411b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 412b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 41373aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // We don't want to trigger a page end moving unless the page has settled 41473aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // and the user has stopped scrolling 41573aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen if (mTouchState == TOUCH_STATE_REST) { 41673aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen pageEndMoving(); 41773aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen } 418c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung 419c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung // Notify the user when the page changes 420c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung if (AccessibilityManager.getInstance(getContext()).isEnabled()) { 421c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung AccessibilityEvent ev = 422c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); 423c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung ev.getText().add(getCurrentPageDescription()); 424c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung sendAccessibilityEventUnchecked(ev); 425c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung } 4260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 4280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 4290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 4300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 4310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 4320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 4330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 434321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 436321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 438f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 439f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung super.onMeasure(widthMeasureSpec, heightMeasureSpec); 440f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 441f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 442f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 444321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 445321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 446321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 447321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 448321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4496b879f0a5885274a85333531e091283405d490ccAdam Lesinski /* Allow the height to be set as WRAP_CONTENT. This allows the particular case 4506b879f0a5885274a85333531e091283405d490ccAdam Lesinski * of the All apps view on XLarge displays to not take up more space then it needs. Width 4516b879f0a5885274a85333531e091283405d490ccAdam Lesinski * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect 4526b879f0a5885274a85333531e091283405d490ccAdam Lesinski * each page to have the same width. 4536b879f0a5885274a85333531e091283405d490ccAdam Lesinski */ 454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 4556b879f0a5885274a85333531e091283405d490ccAdam Lesinski int heightSize = MeasureSpec.getSize(heightMeasureSpec); 4566b879f0a5885274a85333531e091283405d490ccAdam Lesinski int maxChildHeight = 0; 4576b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4586b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 459ea359c6aee44c0fe3bb94f7002c3b49208b32b7fWinson Chung final int horizontalPadding = mPaddingLeft + mPaddingRight; 460321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 46136fcb74425bebc1d88c9e3102484ec902b68f202Michael Jurka 462321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 4635f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // unless they were set to WRAP_CONTENT 464785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize); 465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 4675f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // disallowing padding in paged view (just pass 0) 46822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View child = getPageAt(i); 4695f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 4705f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4715f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childWidthMode; 4725f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.width == LayoutParams.WRAP_CONTENT) { 4735f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.AT_MOST; 4745f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4755f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.EXACTLY; 4765f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4775f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4785f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childHeightMode; 4795f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.height == LayoutParams.WRAP_CONTENT) { 4805f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.AT_MOST; 4815f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.EXACTLY; 4835f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4845f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childWidthMeasureSpec = 486ea359c6aee44c0fe3bb94f7002c3b49208b32b7fWinson Chung MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode); 4875f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childHeightMeasureSpec = 4886b879f0a5885274a85333531e091283405d490ccAdam Lesinski MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode); 4895f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4905f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4916b879f0a5885274a85333531e091283405d490ccAdam Lesinski maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); 492785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "\tmeasure-child" + i + ": " + child.getMeasuredWidth() + ", " 493785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + child.getMeasuredHeight()); 4946b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 4956b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4966b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (heightMode == MeasureSpec.AT_MOST) { 4976b879f0a5885274a85333531e091283405d490ccAdam Lesinski heightSize = maxChildHeight + verticalPadding; 498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 499ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung 500ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung setMeasuredDimension(widthSize, heightSize); 501ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung 50225b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions. 50325b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen // We also wait until we set the measured dimensions before flushing the cache as well, to 50425b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen // ensure that the cache is filled with good values. 50525b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen invalidateCachedOffsets(); 50625b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen updateScrollingIndicatorPosition(); 50725b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen 508faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen if (childCount > 0) { 509faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1); 510faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } else { 511faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = 0; 512faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } 513cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka } 514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 5158c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) { 516af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 517af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int delta = newX - mScrollX; 518af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 5198c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka final int pageCount = getChildCount(); 5208c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka for (int i = 0; i < pageCount; i++) { 52122f823d340710bcde587b18a1542570cbe0360f5Adam Cohen View page = (View) getPageAt(i); 5228c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka page.setX(page.getX() + delta); 523af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 524af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka setCurrentPage(newCurrentPage); 525af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 526af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 5278c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a 5288c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and 529d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // tightens the layout accordingly 530d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka public void setLayoutScale(float childrenScale) { 531d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mLayoutScale = childrenScale; 53273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen invalidateCachedOffsets(); 533d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 534d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Now we need to do a re-layout, but preserving absolute X and Y coordinates 535d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childCount = getChildCount(); 536d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenX[] = new float[childCount]; 537d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenY[] = new float[childCount]; 538d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 53922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View child = getPageAt(i); 540d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenX[i] = child.getX(); 541d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenY[i] = child.getY(); 542d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 543b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung // Trigger a full re-layout (never just call onLayout directly!) 544b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY); 545b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY); 546b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung requestLayout(); 547b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung measure(widthSpec, heightSpec); 548b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung layout(mLeft, mTop, mRight, mBottom); 549d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 55022f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View child = getPageAt(i); 551d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setX(childrenX[i]); 552d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setY(childrenY[i]); 553d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 554b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung 555d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Also, the page offset has changed (since the pages are now smaller); 556d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // update the page offset, but again preserving absolute X and Y coordinates 5578c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka scrollToNewPageWithoutMovingPages(mCurrentPage); 558d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 559d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 56060b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen public void setPageSpacing(int pageSpacing) { 56160b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen mPageSpacing = pageSpacing; 56260b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen invalidateCachedOffsets(); 56360b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen } 56460b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen 565cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka @Override 56628750fba6a2d141eb9a1e566718c17236030b815Michael Jurka protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 567f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 568f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 569f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 570f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 571785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "PagedView.onLayout()"); 5726b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 573321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 574321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 575321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 576785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", " 577785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getChildWidth(0)); 578e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung childLeft = getRelativeChildOffset(0); 579ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung 580ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung // Calculate the variable page spacing if necessary 581ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung if (mPageSpacing < 0) { 58260b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen setPageSpacing(((right - left) - getChildAt(0).getMeasuredWidth()) / 2); 583ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung } 584321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 585321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 586321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 58722f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View child = getPageAt(i); 588321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 589d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int childWidth = getScaledMeasuredWidth(child); 5906b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int childHeight = child.getMeasuredHeight(); 5916b879f0a5885274a85333531e091283405d490ccAdam Lesinski int childTop = mPaddingTop; 5926b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (mCenterPagesVertically) { 5936b879f0a5885274a85333531e091283405d490ccAdam Lesinski childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2; 5946b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 595d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 596785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop); 5976b879f0a5885274a85333531e091283405d490ccAdam Lesinski child.layout(childLeft, childTop, 598d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childLeft + child.getMeasuredWidth(), childTop + childHeight); 5999c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen childLeft += childWidth + mPageSpacing; 600321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 601321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 602c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung 603c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 604c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung setHorizontalScrollBarEnabled(false); 605c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 606c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung scrollTo(newX, 0); 607c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung mScroller.setFinalX(newX); 608c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung setHorizontalScrollBarEnabled(true); 609c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung mFirstLayout = false; 610c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung } 611c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung 612d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 613d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mFirstLayout = false; 614d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 61773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected void screenScrolled(int screenCenter) { 61873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (isScrollingIndicatorEnabled()) { 61973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen updateScrollingIndicator(); 62073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 6210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 62273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen for (int i = 0; i < getChildCount(); i++) { 62373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen View child = getChildAt(i); 62473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (child != null) { 62573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen float scrollProgress = getScrollProgress(screenCenter, child, i); 62673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen float alpha = 1 - Math.abs(scrollProgress); 62773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen child.setFastAlpha(alpha); 62873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen child.fastInvalidate(); 629affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 63173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen invalidate(); 632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 633e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 6340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 635e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung @Override 6362591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen protected void onViewAdded(View child) { 6372591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen super.onViewAdded(child); 6382591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen 6392591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen // This ensures that when children are added, they get the correct transforms / alphas 6402591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen // in accordance with any scroll effects. 6412591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen mForceScreenScrolled = true; 6422591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen invalidate(); 64325b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen invalidateCachedOffsets(); 6442591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen } 6452591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen 64673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected void invalidateCachedOffsets() { 64773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen int count = getChildCount(); 64825b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen if (count == 0) { 64925b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen mChildOffsets = null; 65025b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen mChildRelativeOffsets = null; 65125b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen mChildOffsetsWithLayoutScale = null; 65225b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen return; 65325b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen } 65473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 65573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsets = new int[count]; 65673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildRelativeOffsets = new int[count]; 65773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsetsWithLayoutScale = new int[count]; 65873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen for (int i = 0; i < count; i++) { 65973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsets[i] = -1; 66073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildRelativeOffsets[i] = -1; 66173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsetsWithLayoutScale[i] = -1; 66273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 66373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 66473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 66573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected int getChildOffset(int index) { 66673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ? 66773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsets : mChildOffsetsWithLayoutScale; 66873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 66973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (childOffsets != null && childOffsets[index] != -1) { 67073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return childOffsets[index]; 67173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } else { 67273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (getChildCount() == 0) 67373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return 0; 67473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 67573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen int offset = getRelativeChildOffset(0); 67673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen for (int i = 0; i < index; ++i) { 67773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing; 67873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 67973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (childOffsets != null) { 68073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen childOffsets[index] = offset; 68173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 68273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return offset; 68373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 68473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 68573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 68673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected int getRelativeChildOffset(int index) { 68773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) { 68873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return mChildRelativeOffsets[index]; 68973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } else { 69073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int padding = mPaddingLeft + mPaddingRight; 69173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int offset = mPaddingLeft + 69273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen (getMeasuredWidth() - padding - getChildWidth(index)) / 2; 69373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (mChildRelativeOffsets != null) { 69473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildRelativeOffsets[index] = offset; 69573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 69673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return offset; 69773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 69873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 69973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 70073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected int getScaledRelativeChildOffset(int index) { 70173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int padding = mPaddingLeft + mPaddingRight; 70273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int offset = mPaddingLeft + (getMeasuredWidth() - padding - 70373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen getScaledMeasuredWidth(getPageAt(index))) / 2; 70473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return offset; 70573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 70673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 70773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected int getScaledMeasuredWidth(View child) { 70873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen // This functions are called enough times that it actually makes a difference in the 70973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen // profiler -- so just inline the max() here 71073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int measuredWidth = child.getMeasuredWidth(); 71173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int minWidth = mMinimumWidth; 71273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth; 71373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return (int) (maxWidth * mLayoutScale + 0.5f); 71473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 71573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 716dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka protected void getVisiblePages(int[] range) { 7170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 718c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka if (pageCount > 0) { 71922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final int pageWidth = getScaledMeasuredWidth(getPageAt(0)); 720c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int screenWidth = getMeasuredWidth(); 721557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung int x = getScaledRelativeChildOffset(0) + pageWidth; 722c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int leftScreen = 0; 723c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int rightScreen = 0; 72422f823d340710bcde587b18a1542570cbe0360f5Adam Cohen while (x <= mScrollX && leftScreen < pageCount - 1) { 725c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka leftScreen++; 72622f823d340710bcde587b18a1542570cbe0360f5Adam Cohen x += getScaledMeasuredWidth(getPageAt(leftScreen)) + mPageSpacing; 727c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 728c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = leftScreen; 72922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen while (x < mScrollX + screenWidth && rightScreen < pageCount - 1) { 730c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen++; 73122f823d340710bcde587b18a1542570cbe0360f5Adam Cohen x += getScaledMeasuredWidth(getPageAt(rightScreen)) + mPageSpacing; 732c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 733dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka range[0] = leftScreen; 734dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka range[1] = rightScreen; 735dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka } else { 736dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka range[0] = -1; 737dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka range[1] = -1; 738dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka } 739dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka } 740dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka 741dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka @Override 742dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka protected void dispatchDraw(Canvas canvas) { 743dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka int halfScreenSize = getMeasuredWidth() / 2; 744ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen // mOverScrollX is equal to mScrollX when we're within the normal scroll range. Otherwise 745ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen // it is equal to the scaled overscroll position. 746ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen int screenCenter = mOverScrollX + halfScreenSize; 747dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka 748dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka if (screenCenter != mLastScreenCenter || mForceScreenScrolled) { 749dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka screenScrolled(screenCenter); 750dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka mLastScreenCenter = screenCenter; 751dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka mForceScreenScrolled = false; 752dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka } 753dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka 754dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka // Find out which screens are visible; as an optimization we only call draw on them 755dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka final int pageCount = getChildCount(); 756dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka if (pageCount > 0) { 757dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka getVisiblePages(mTempVisiblePagesRange); 758dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka final int leftScreen = mTempVisiblePagesRange[0]; 759dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka final int rightScreen = mTempVisiblePagesRange[1]; 760c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung if (leftScreen != -1 && rightScreen != -1) { 761c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung final long drawingTime = getDrawingTime(); 762c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung // Clip to the bounds 763c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung canvas.save(); 764c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft, 765c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung mScrollY + mBottom - mTop); 766c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung 767c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung for (int i = rightScreen; i >= leftScreen; i--) { 768c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung drawChild(canvas, getPageAt(i), drawingTime); 769c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung } 770c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung canvas.restore(); 771c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 7720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 777ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen int page = indexToPage(indexOfChild(child)); 77886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 77986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 78786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 78886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 78986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 79186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 79386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 79576fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen return v.requestFocus(direction, previouslyFocusedRect); 796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 80386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 80486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 80886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 80986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 81886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 81986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 82286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 82386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 82686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 82786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 830321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 831321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 832321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 83486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 83686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 83786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 84186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 842321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 843321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 844321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 864321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 865321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 867321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 86822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View currentPage = getPageAt(mCurrentPage); 86986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 872321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 873321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 874d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 875d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the previous page. 876d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 877d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsPreviousPage(float x, float y) { 878d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing); 879d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 880d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 881d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 882d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the next page. 883d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 884d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsNextPage(float x, float y) { 885d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing)); 886d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 887d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 8956342bbae1a18f3c1862953e33309481703f541cfAdam Cohen acquireVelocityTrackerAndAddMovement(ev); 896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 89745e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 89845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 89945e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 901321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 904321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 906321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 908321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 909321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 910321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 911321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 912321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 913321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 9171ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka if (mActivePointerId != INVALID_POINTER) { 9181ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka determineScrollingStart(ev); 9191ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka break; 9201ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka } 9211ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 9221ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 9231ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // i.e. fall through to the next case (don't break) 9241ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 9251ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // while it's small- this was causing a crash before we checked for INVALID_POINTER) 926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 929321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 935c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 936aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 945fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 9465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 9475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 9485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 9495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 9505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 9515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 9525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 95486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 956d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { 957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 958d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (hitsPreviousPage(x, y)) { 959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 960d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (hitsNextPage(x, y)) { 961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 966321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 968321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 9691d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown case MotionEvent.ACTION_CANCEL: 970321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 971321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 972321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 9736342bbae1a18f3c1862953e33309481703f541cfAdam Cohen releaseVelocityTracker(); 974321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 975321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 976321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 977321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 9786342bbae1a18f3c1862953e33309481703f541cfAdam Cohen releaseVelocityTracker(); 979321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 980321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 981321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 982321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 983321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 984321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 986321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 98980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 99080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 991228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung ObjectAnimator anim = (ObjectAnimator) AnimatorInflater. 992228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung loadAnimator(mContext, R.anim.paged_view_click_feedback); 993228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.setTarget(v); 994228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.addListener(new AnimatorListenerAdapter() { 995228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung public void onAnimationRepeat(Animator animation) { 99680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 99780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 99880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 999228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.start(); 100080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 100180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1002f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev) { 1003f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen determineScrollingStart(ev, 1.0f); 1004f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 1005f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 1006321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 1008321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1010f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { 1011321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1012321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 1013321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 1014321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 10162698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka if (pointerIndex == -1) { 10172698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka return; 10182698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka } 1019321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1020321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 1021321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1024f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final int touchSlop = Math.round(touchSlopScale * mTouchSlop); 1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 1028321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1029f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (xMoved || xPaged || yMoved) { 10300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 10336342bbae1a18f3c1862953e33309481703f541cfAdam Cohen mTotalMotionX += Math.abs(mLastMotionX - x); 1034321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 1035c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 10360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 10370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 10380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1039321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1040321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 1041f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen cancelCurrentPageLongPress(); 1042f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 1043f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 1044f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 1045f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void cancelCurrentPageLongPress() { 1046f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (mAllowLongPress) { 1047f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen mAllowLongPress = false; 1048f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // Try canceling the long press. It could also have been scheduled 1049f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // by a distant descendant, so use the mAllowLongPress flag to block 1050f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // everything 1051f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final View currentPage = getPageAt(mCurrentPage); 1052f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (currentPage != null) { 1053f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen currentPage.cancelLongPress(); 1054321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1055321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1056321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1058b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected float getScrollProgress(int screenCenter, View v, int page) { 1059b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen final int halfScreenSize = getMeasuredWidth() / 2; 1060b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1061b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing; 1062b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen int delta = screenCenter - (getChildOffset(page) - 1063b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen getRelativeChildOffset(page) + halfScreenSize); 1064b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1065b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen float scrollProgress = delta / (totalDistance * 1.0f); 1066b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen scrollProgress = Math.min(scrollProgress, 1.0f); 1067b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen scrollProgress = Math.max(scrollProgress, -1.0f); 1068b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen return scrollProgress; 1069b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1070b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1071e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // This curve determines how the effect of scrolling over the limits of the page dimishes 1072e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as the user pulls further and further from the bounds 1073e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private float overScrollInfluenceCurve(float f) { 1074e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 1.0f; 1075e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return f * f * f + 1.0f; 1076e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1077e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1078b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected void acceleratedOverScroll(float amount) { 1079b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen int screenSize = getMeasuredWidth(); 1080b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1081b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen // We want to reach the max over scroll effect when the user has 1082b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen // over scrolled half the size of the screen 1083b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize); 1084b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1085b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen if (f == 0) return; 1086b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1087b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen // Clamp this factor, f, to -1 < f < 1 1088b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen if (Math.abs(f) >= 1) { 1089b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen f /= Math.abs(f); 1090b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1091b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1092b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen int overScrollAmount = (int) Math.round(f * screenSize); 1093b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen if (amount < 0) { 1094ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = overScrollAmount; 1095ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mScrollX = 0; 1096b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } else { 1097ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = mMaxScrollX + overScrollAmount; 1098ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mScrollX = mMaxScrollX; 1099b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1100b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen invalidate(); 1101b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1102b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1103b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected void dampedOverScroll(float amount) { 1104e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int screenSize = getMeasuredWidth(); 1105e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1106e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float f = (amount / screenSize); 1107e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1108e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (f == 0) return; 1109e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 1110e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 11117bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen // Clamp this factor, f, to -1 < f < 1 11127bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen if (Math.abs(f) >= 1) { 11137bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen f /= Math.abs(f); 11147bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen } 11157bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen 1116e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); 111768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (amount < 0) { 1118ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = overScrollAmount; 1119ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mScrollX = 0; 112068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 1121ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = mMaxScrollX + overScrollAmount; 1122ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mScrollX = mMaxScrollX; 112368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 112468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 112568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 112668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 1127b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected void overScroll(float amount) { 1128b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen dampedOverScroll(amount); 1129b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1130b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1131c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka protected float maxOverScroll() { 1132c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not 1133b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen // exceed). Used to find out how much extra wallpaper we need for the over scroll effect 1134c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka float f = 1.0f; 1135c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 1136c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka return OVERSCROLL_DAMP_FACTOR * f; 1137c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka } 1138c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka 1139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 114145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 114245e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onTouchEvent(ev); 114345e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 1144b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka acquireVelocityTrackerAndAddMovement(ev); 1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 1147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1151321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 1152321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 1153321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1154321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 1155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 1156321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 1159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 1160c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1161aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 1162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 11630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 11640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 11650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 1169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 1170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 1171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 1172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1173c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung final float deltaX = mLastMotionX + mLastMotionXRemainder - x; 1174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1175aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX += Math.abs(deltaX); 1176aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1177c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // Only scroll and update mLastMotionX if we have moved some discrete amount. We 1178c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // keep the remainder because we are actually testing if we've moved from the last 1179c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // scrolled position (which is discrete). 1180c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung if (Math.abs(deltaX) >= 1.0f) { 118168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mTouchX += deltaX; 118268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 118368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (!mDeferScrollUpdate) { 1184c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung scrollBy((int) deltaX, 0); 1185785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX); 118668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 118768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 1188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1189c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionX = x; 1190c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = deltaX - (int) deltaX; 1191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 1193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1194564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen } else { 1195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 1196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1197321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1199321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 1200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 1201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 1202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 1203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 1205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 12079cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung final int deltaX = (int) (x - mDownMotionX); 120800481b3ed322b4f36b669201d2287964febda65fAdam Cohen final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage)); 120900481b3ed322b4f36b669201d2287964febda65fAdam Cohen boolean isSignificantMove = Math.abs(deltaX) > pageWidth * 121000481b3ed322b4f36b669201d2287964febda65fAdam Cohen SIGNIFICANT_MOVE_THRESHOLD; 12110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 1212aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1213b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); 1214b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen 121500481b3ed322b4f36b669201d2287964febda65fAdam Cohen boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING && 121600481b3ed322b4f36b669201d2287964febda65fAdam Cohen Math.abs(velocityX) > snapVelocity; 121700481b3ed322b4f36b669201d2287964febda65fAdam Cohen 1218aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // In the case that the page is moved far to one direction and then is flung 1219aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // in the opposite direction, we use a threshold to determine whether we should 1220aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // just return to the starting page, or if we should skip one further. 1221aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen boolean returnToOriginalPage = false; 1222b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && 122300481b3ed322b4f36b669201d2287964febda65fAdam Cohen Math.signum(velocityX) != Math.signum(deltaX) && isFling) { 1224aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen returnToOriginalPage = true; 1225aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } 1226aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1227aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen int finalPage; 1228aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // We give flings precedence over large moves, which is why we short-circuit our 1229aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // test for a large move if a fling has been registered. That is, a large 1230aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // move to the left and fling to the right will register as a fling to the right. 1231aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen if (((isSignificantMove && deltaX > 0 && !isFling) || 1232aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX > 0)) && mCurrentPage > 0) { 1233aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; 1234aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1235aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } else if (((isSignificantMove && deltaX < 0 && !isFling) || 1236aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX < 0)) && 123786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 1238aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1; 1239aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1243d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 1244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 124786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 124886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 124986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1250321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1253d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 1254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 125786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 125886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 125986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 12631d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } else { 1264d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka onUnhandledTap(ev); 1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1268b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1271321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 1272b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 1273b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka snapToDestination(); 1274b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1277b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 1281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 1282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 1286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1288185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung @Override 1289185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung public boolean onGenericMotionEvent(MotionEvent event) { 1290185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 1291185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung switch (event.getAction()) { 1292185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung case MotionEvent.ACTION_SCROLL: { 1293185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung // Handle mouse (or ext. device) by shifting the page depending on the scroll 1294185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung final float vscroll; 1295185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung final float hscroll; 1296185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { 1297185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung vscroll = 0; 1298185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1299185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } else { 1300185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1301185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); 1302185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1303185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if (hscroll != 0 || vscroll != 0) { 1304185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if (hscroll > 0 || vscroll > 0) { 1305185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung scrollRight(); 1306185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } else { 1307185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung scrollLeft(); 1308185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1309185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung return true; 1310185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1311185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1312185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1313185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1314185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung return super.onGenericMotionEvent(event); 1315185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1316185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung 1317b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { 1318b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker == null) { 1319b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = VelocityTracker.obtain(); 1320b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1321b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.addMovement(ev); 1322b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1323b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1324b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void releaseVelocityTracker() { 1325b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker != null) { 1326b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.recycle(); 1327b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = null; 1328b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1329b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1330b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1331321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 1332321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 1333321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 1334321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 1335321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 1336321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 1337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 1338321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 1339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 1341321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 1342c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 1344321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 1345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 1346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 13481d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 13491d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown 1350d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka protected void onUnhandledTap(MotionEvent ev) {} 1351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1352321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 1354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 1355ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen int page = indexToPage(indexOfChild(child)); 135697d85d23b013347bead4e2f5fa430a79ce69431eWinson Chung if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) { 135786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 1358321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1359321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1361e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getChildIndexForRelativeOffset(int relativeOffset) { 1362e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int childCount = getChildCount(); 13639c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int left; 13649c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int right; 1365e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung for (int i = 0; i < childCount; ++i) { 13669c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen left = getRelativeChildOffset(i); 136722f823d340710bcde587b18a1542570cbe0360f5Adam Cohen right = (left + getScaledMeasuredWidth(getPageAt(i))); 1368e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung if (left <= relativeOffset && relativeOffset <= right) { 1369e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return i; 1370e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1371e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1372e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return -1; 1373e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1374e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 13751908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung protected int getChildWidth(int index) { 137663257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // This functions are called enough times that it actually makes a difference in the 137763257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // profiler -- so just inline the max() here 137822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final int measuredWidth = getPageAt(index).getMeasuredWidth(); 137963257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int minWidth = mMinimumWidth; 138063257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung return (minWidth > measuredWidth) ? minWidth : measuredWidth; 13811908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung } 13821908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung 1383d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen int getPageNearestToCenterOfScreen() { 138422f823d340710bcde587b18a1542570cbe0360f5Adam Cohen int minDistanceFromScreenCenter = Integer.MAX_VALUE; 1385321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 1386321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 1387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 1388321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 138922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen View layout = (View) getPageAt(i); 1390d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 1391321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 1392321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 1393321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 1394321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 1395321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 1396321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 1397321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1399d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen return minDistanceFromScreenCenterIndex; 1400d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen } 1401d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen 1402d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen protected void snapToDestination() { 1403d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); 1404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1405321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1406e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static class ScrollInterpolator implements Interpolator { 1407e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public ScrollInterpolator() { 1408e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1409e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1410e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public float getInterpolation(float t) { 1411e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen t -= 1.0f; 1412e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return t*t*t*t*t + 1; 1413e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1414e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1415e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1416e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // We want the duration of the page snap animation to be influenced by the distance that 1417e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // the screen has to travel, however, we don't want this duration to be effected in a 1418e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // purely linear fashion. Instead, we use this method to moderate the effect that the distance 1419e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // of travel has on the overall snap duration. 1420e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceInfluenceForSnapDuration(float f) { 1421e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 0.5f; // center the values about 0. 1422e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f *= 0.3f * Math.PI / 2.0f; 1423e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return (float) Math.sin(f); 1424e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1425e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 14260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 1427e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); 1428e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int halfScreenSize = getMeasuredWidth() / 2; 1429e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1430785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); 1431785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): " 1432785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getMeasuredWidth() + ", " + getChildWidth(whichPage)); 1433e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 1434e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int delta = newX - mUnboundedScrollX; 1435e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int duration = 0; 1436e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1437e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (Math.abs(velocity) < MIN_FLING_VELOCITY) { 1438e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // If the velocity is low enough, then treat this more as an automatic page advance 1439e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as opposed to an apparent physical response to flinging 1440e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1441e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return; 1442e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1443e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1444e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // Here we compute a "distance" that will be used in the computation of the overall 1445e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // snap duration. This is a function of the actual distance that needs to be traveled; 1446e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we keep this value close to half screen size in order to reduce the variance in snap 1447e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // duration as a function of the distance the page needs to travel. 144820b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize)); 1449e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distance = halfScreenSize + halfScreenSize * 1450e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen distanceInfluenceForSnapDuration(distanceRatio); 1451e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1452e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.abs(velocity); 1453e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); 1454e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1455e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we want the page's snap velocity to approximately match the velocity at which the 1456e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // user flings, so we scale the duration by a value near to the derivative of the scroll 145720b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka // interpolator at zero, ie. 5. We use 4 to make it a little slower. 145820b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); 1459e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1460e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, delta, duration); 14610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 14645f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 14670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 146886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 1469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1470785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); 1471785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", " 1472785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getChildWidth(whichPage)); 147386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 147468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen final int sX = mUnboundedScrollX; 1475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 14760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 14770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 14800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 14810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 14830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 148422f823d340710bcde587b18a1542570cbe0360f5Adam Cohen focusedChild == getPageAt(mCurrentPage)) { 14850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 14860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 1490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 1491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 1492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1494321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 149568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); 149680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1497b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // Load associated pages immediately if someone else is handling the scroll, otherwise defer 1498b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // loading associated pages until the scroll settles 1499b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung if (mDeferScrollUpdate) { 1500b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung loadAssociatedPages(mNextPage); 1501b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } else { 15024e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung mDeferLoadAssociatedPagesUntilScrollCompletes = true; 1503b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 15040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 1505321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 1506321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1507321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1508321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 1509321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 151086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 1511321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 151286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 1513321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1515321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1516321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 1517321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 151886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 1519321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 152086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 1521321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1522321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1523321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 152486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 1525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 1526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 1527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 1528321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 1529321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 153022f823d340710bcde587b18a1542570cbe0360f5Adam Cohen if (vp == getPageAt(i)) { 1531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 1532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 1536321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1537321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1538321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 1539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 1540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 1542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 1543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 15450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 15460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 15470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 15480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 15490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 15500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 15510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 15520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 155486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 1555321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1556321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 1557321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 1558321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1559321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1560321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 1561321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 156286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 1563321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 1567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 156886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 1569321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1570321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 1572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 1573321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 1574321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 1575321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1576321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1577321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 1578321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 1579321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1580321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 1581321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1582321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1583f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung protected void loadAssociatedPages(int page) { 1584f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung loadAssociatedPages(page, false); 1585f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung } 1586f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung protected void loadAssociatedPages(int page, boolean immediateAndOnly) { 15870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 15880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 15890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 1590e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int lowerPageBound = getAssociatedLowerPageBound(page); 1591e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int upperPageBound = getAssociatedUpperPageBound(page); 1592785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/" 1593785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + upperPageBound); 15940cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka // First, clear any pages that should no longer be loaded 15950cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka for (int i = 0; i < count; ++i) { 15960cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka Page layout = (Page) getPageAt(i); 15970cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka if ((immediateAndOnly && i != page) || 15980cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka (i < lowerPageBound) || 15990cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka (i > upperPageBound)) { 16000cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka if (layout.getPageChildCount() > 0) { 16010cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka layout.removeAllViewsOnPage(); 16020cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka } 16030cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka mDirtyPageContent.set(i, true); 16040cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka } 16050cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka } 16060cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka // Next, load any new pages 16070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 1608f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung if ((i != page) && immediateAndOnly) { 1609f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung continue; 1610f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung } 161122f823d340710bcde587b18a1542570cbe0360f5Adam Cohen Page layout = (Page) getPageAt(i); 16120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 16130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 1614f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung syncPageItems(i, (i == page) && immediateAndOnly); 16150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 16160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 161780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 161880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 161980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 162080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 162180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 162280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1623e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedLowerPageBound(int page) { 1624e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.max(0, page - 1); 1625e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1626e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedUpperPageBound(int page) { 1627e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int count = getChildCount(); 1628e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.min(page + 1, count - 1); 1629e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1630e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 16315f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void startChoiceMode(int mode, ActionMode.Callback callback) { 1632430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy if (isChoiceMode(CHOICE_MODE_NONE)) { 1633430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mChoiceMode = mode; 1634430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mActionMode = startActionMode(callback); 1635430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy } 16365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 16382b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public void endChoiceMode() { 16395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (!isChoiceMode(CHOICE_MODE_NONE)) { 16405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 16415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung resetCheckedGrandchildren(); 1642e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka if (mActionMode != null) mActionMode.finish(); 16439f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy mActionMode = null; 16445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 16475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected boolean isChoiceMode(int mode) { 16485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return mChoiceMode == mode; 16495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 16515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected ArrayList<Checkable> getCheckedGrandchildren() { 16525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung ArrayList<Checkable> checked = new ArrayList<Checkable>(); 16535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int childCount = getChildCount(); 16545f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < childCount; ++i) { 165522f823d340710bcde587b18a1542570cbe0360f5Adam Cohen Page layout = (Page) getPageAt(i); 16568245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int grandChildCount = layout.getPageChildCount(); 16575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int j = 0; j < grandChildCount; ++j) { 16588245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final View v = layout.getChildOnPageAt(j); 16599f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 16605f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung checked.add((Checkable) v); 16615f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16625f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16635f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16645f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return checked; 16655f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16665f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 16679f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy /** 16689f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * If in CHOICE_MODE_SINGLE and an item is checked, returns that item. 16699f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * Otherwise, returns null. 16709f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy */ 16719f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy protected Checkable getSingleCheckedGrandchild() { 16726f13342ffd3f968de9ff86b988621cc91d94adffPatrick Dubroy if (mChoiceMode != CHOICE_MODE_MULTIPLE) { 16739f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int childCount = getChildCount(); 16749f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int i = 0; i < childCount; ++i) { 167522f823d340710bcde587b18a1542570cbe0360f5Adam Cohen Page layout = (Page) getPageAt(i); 16768245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int grandChildCount = layout.getPageChildCount(); 16779f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int j = 0; j < grandChildCount; ++j) { 16788245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final View v = layout.getChildOnPageAt(j); 16799f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 16809f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return (Checkable) v; 16819f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 16829f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 16839f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 16849f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 16859f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return null; 16869f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 16879f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 16885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void resetCheckedGrandchildren() { 16895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // loop through children, and set all of their children to _not_ be checked 16905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final ArrayList<Checkable> checked = getCheckedGrandchildren(); 16915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < checked.size(); ++i) { 16925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final Checkable c = checked.get(i); 16935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung c.setChecked(false); 16945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16955f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 16965f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 169786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 169886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 169986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 170086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 170186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 170286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 170486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 170586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 170686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 170786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 170886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1709f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung public abstract void syncPageItems(int page, boolean immediate); 171086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1711244d74cb353f1260c4d633e719bf84bb3b6e52bcPatrick Dubroy protected void invalidatePageData() { 1712f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung invalidatePageData(-1, false); 17135a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 17145a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung protected void invalidatePageData(int currentPage) { 1715f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung invalidatePageData(currentPage, false); 1716f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung } 1717f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung protected void invalidatePageData(int currentPage, boolean immediateAndOnly) { 1718f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 1719f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 1720f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 1721f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 17220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 17230cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen // Force all scrolling-related behavior to end 17240cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen mScroller.forceFinished(true); 17250cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen mNextPage = INVALID_PAGE; 17260cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen 17270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 17280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 172986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 17305a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // We must force a measure after we've loaded the pages to update the content width and 17315a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // to determine the full scroll width 17325a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), 17335a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); 17345a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung 17355a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // Set a new page as the current page if necessary 17365a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung if (currentPage > -1) { 17375afbf7bdd6f6879124c9b8283e005fe57f310d02Winson Chung setCurrentPage(Math.min(getPageCount() - 1, currentPage)); 17385a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 17395a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung 17400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 17410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 17420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 17430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 17440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 17450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 174686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 17470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 1748f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung loadAssociatedPages(mCurrentPage, immediateAndOnly); 17490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 17500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1752007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 175321b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen protected ImageView getScrollingIndicator() { 1754007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // We use mHasScrollIndicator to prevent future lookups if there is no sibling indicator 1755007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // found 1756007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mHasScrollIndicator && mScrollIndicator == null) { 1757007c69867d821ea2b271398577a8b3440b3a7046Winson Chung ViewGroup parent = (ViewGroup) getParent(); 1758007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator = (ImageView) (parent.findViewById(R.id.paged_view_indicator)); 1759007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mHasScrollIndicator = mScrollIndicator != null; 1760007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mHasScrollIndicator) { 1761007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.setVisibility(View.VISIBLE); 1762007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1763007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1764007c69867d821ea2b271398577a8b3440b3a7046Winson Chung return mScrollIndicator; 1765007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1766007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1767007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected boolean isScrollingIndicatorEnabled() { 1768649723cfb3d73af16dd02462725700897ca60e38Winson Chung return !LauncherApplication.isScreenLarge(); 1769007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1770007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 17715a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung Runnable hideScrollingIndicatorRunnable = new Runnable() { 17725a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung @Override 17735a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung public void run() { 17745a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung hideScrollingIndicator(false); 17755a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 17765a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung }; 1777b737ee6ab2dc4938a1b2a574c336af0eab826712Michael Jurka protected void flashScrollingIndicator(boolean animated) { 17785a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung removeCallbacks(hideScrollingIndicatorRunnable); 1779b737ee6ab2dc4938a1b2a574c336af0eab826712Michael Jurka showScrollingIndicator(!animated); 17805a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration); 17813ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 17823ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 1783430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka protected void showScrollingIndicator(boolean immediately) { 1784007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1785007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1786007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1787007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1788007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1789007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Fade the indicator in 1790007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 179132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.setVisibility(View.VISIBLE); 179221b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen cancelScrollingIndicatorAnimations(); 1793430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka if (immediately) { 1794430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka mScrollIndicator.setAlpha(1f); 1795430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka } else { 1796430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f); 1797430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration); 1798430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka mScrollIndicatorAnimator.start(); 1799430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka } 1800007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1801007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1802007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 180321b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen protected void cancelScrollingIndicatorAnimations() { 180421b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen if (mScrollIndicatorAnimator != null) { 180521b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen mScrollIndicatorAnimator.cancel(); 180621b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen } 180721b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen } 180821b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen 1809007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected void hideScrollingIndicator(boolean immediately) { 1810007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1811007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1812007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1813007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1814007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1815007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Fade the indicator out 1816007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 181721b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen cancelScrollingIndicatorAnimations(); 181832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (immediately) { 181981efbad05e0ce69b06d7323749ef2e200b84b98eMichael Jurka mScrollIndicator.setVisibility(View.INVISIBLE); 182032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.setAlpha(0f); 182132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } else { 182232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f); 182332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration); 182432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() { 182532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung private boolean cancelled = false; 182632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung @Override 182732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung public void onAnimationCancel(android.animation.Animator animation) { 182832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung cancelled = true; 182932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 183032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung @Override 183132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung public void onAnimationEnd(Animator animation) { 183232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (!cancelled) { 183381efbad05e0ce69b06d7323749ef2e200b84b98eMichael Jurka mScrollIndicator.setVisibility(View.INVISIBLE); 183432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 183532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 183632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung }); 183732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.start(); 183832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 1839007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1840007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1841007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 184232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung /** 184332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung * To be overridden by subclasses to determine whether the scroll indicator should stretch to 184432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung * fill its space on the track or not. 184532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung */ 184632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung protected boolean hasElasticScrollIndicator() { 1847dea74b7d12b0fcd50bfdb4274f9867ba76d75238Winson Chung return true; 184832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 184932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung 1850007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private void updateScrollingIndicator() { 1851007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1852007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1853007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1854007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1855007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1856007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 1857007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1858007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1859007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1860007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private void updateScrollingIndicatorPosition() { 1861649723cfb3d73af16dd02462725700897ca60e38Winson Chung if (!isScrollingIndicatorEnabled()) return; 1862430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka if (mScrollIndicator == null) return; 186332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int numPages = getChildCount(); 186432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int pageWidth = getMeasuredWidth(); 1865ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung int lastChildIndex = Math.max(0, getChildCount() - 1); 1866ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex); 1867f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight; 186832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorWidth = mScrollIndicator.getMeasuredWidth() - 186932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight(); 187032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung 1871ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX)); 187232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorSpace = trackWidth / numPages; 1873ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft; 187432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (hasElasticScrollIndicator()) { 187532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) { 187632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.getLayoutParams().width = indicatorSpace; 187732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.requestLayout(); 187832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 187932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } else { 188032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2; 188132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung indicatorPos += indicatorCenterOffset; 188232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 1883007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.setTranslationX(indicatorPos); 18843ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung mScrollIndicator.invalidate(); 18853ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 18863ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 18873ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung public void showScrollIndicatorTrack() { 18883ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 18893ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 18903ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung public void hideScrollIndicatorTrack() { 1891007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 18926a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18936a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung /* Accessibility */ 18946a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 18956a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 18966a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onInitializeAccessibilityNodeInfo(info); 18976a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung info.setScrollable(true); 18986a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18996a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 19006a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 19016a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 19026a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onInitializeAccessibilityEvent(event); 19036a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setScrollable(true); 19046a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { 19056a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setFromIndex(mCurrentPage); 19066a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setToIndex(mCurrentPage); 19076a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setItemCount(getChildCount()); 19086a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 19096a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 19106a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 19116a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung protected String getCurrentPageDescription() { 19126a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; 19136a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung return String.format(mContext.getString(R.string.default_scroll_format), 19146a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung page + 1, getChildCount()); 19156a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 1916d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung 1917d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung @Override 1918d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung public boolean onHoverEvent(android.view.MotionEvent event) { 1919d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung return true; 1920d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung } 1921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1922