PagedView.java revision 869390b6abd6e005a5ce85a8ee3c11daeecce919
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.AnimatorListenerAdapter; 21228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.ObjectAnimator; 22bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chungimport android.animation.ValueAnimator; 23321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context; 249c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray; 25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas; 26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect; 27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel; 28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable; 29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet; 30785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chungimport android.util.Log; 31185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.InputDevice; 32185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.KeyEvent; 33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 38321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 396a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityEvent; 40c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chungimport android.view.accessibility.AccessibilityManager; 416a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityNodeInfo; 42e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator; 43321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 44321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 450499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R; 4680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 476a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport java.util.ArrayList; 486a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 49321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/** 50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a 510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages" 52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 538b805b17158886035b38261eb611d8641701ae43Michael Jurkapublic abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener { 54321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final String TAG = "PagedView"; 55785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung private static final boolean DEBUG = false; 560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final int INVALID_PAGE = -1; 57321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 5886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // the min drag distance for a fling to register, to prevent random page shifts 599cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_FLING = 25; 60321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 61f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung protected static final int PAGE_SNAP_ANIMATION_DURATION = 550; 62f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950; 630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 65b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen private static final float OVERSCROLL_ACCELERATE_FACTOR = 2; 66b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung private static final float OVERSCROLL_DAMP_FACTOR = 0.14f; 67867ca62c0e4b811d46734ff940d59c7a787525c8Winson Chung 68b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; 6900481b3ed322b4f36b669201d2287964febda65fAdam Cohen // The page is moved more than halfway, automatically move to the next page on touch up. 7000481b3ed322b4f36b669201d2287964febda65fAdam Cohen private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f; 7168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 72265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen // The following constants need to be scaled based on density. The scaled versions will be 73265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen // assigned to the corresponding member variables below. 74265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen private static final int FLING_THRESHOLD_VELOCITY = 500; 75265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen private static final int MIN_SNAP_VELOCITY = 1500; 76265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen private static final int MIN_FLING_VELOCITY = 250; 77265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen 78265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen protected int mFlingThresholdVelocity; 79265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen protected int mMinFlingVelocity; 80265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen protected int mMinSnapVelocity; 810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 82b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected float mDensity; 830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 85321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 9068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mMaxScrollX; 910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 92321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 93321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 957426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionX; 96c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung protected float mLastMotionXRemainder; 977426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionY; 98aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen protected float mTotalMotionX; 99f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private int mLastScreenCenter = -1; 10073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen private int[] mChildOffsets; 10173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen private int[] mChildRelativeOffsets; 10273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen private int[] mChildOffsetsWithLayoutScale; 103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 1050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 1060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 1070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 108e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; 109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 1112591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen protected boolean mForceScreenScrolled = false; 112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 114321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1157426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected boolean mAllowLongPress = true; 116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1177426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected int mTouchSlop; 118321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 1201908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung private int mMinimumWidth; 1219c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageSpacing; 1229c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingTop; 1239c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingBottom; 1249c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingLeft; 1259c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingRight; 126df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutWidthGap; 127df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutHeightGap; 12887b1490c5a100619648b251cb2be05c457bede08Michael Jurka protected int mCellCountX = 0; 12987b1490c5a100619648b251cb2be05c457bede08Michael Jurka protected int mCellCountY = 0; 1307da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung protected boolean mCenterPagesVertically; 13168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected boolean mAllowOverScroll = true; 13268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mUnboundedScrollX; 133dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka protected int[] mTempVisiblePagesRange = new int[2]; 134321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1358b805b17158886035b38261eb611d8641701ae43Michael Jurka // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise 136ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen // it is equal to the scaled overscroll position. We use a separate value so as to prevent 137ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen // the screens from continuing to translate beyond the normal bounds. 138ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen protected int mOverScrollX; 139ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen 1408c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // parameter that adjusts the layout to be optimized for pages with that scale factor 141d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected float mLayoutScale = 1.0f; 142d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1435f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected static final int INVALID_POINTER = -1; 144321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1455f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected int mActivePointerId = INVALID_POINTER; 146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 14786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 149e326f186af6b00e4ea32849f1527254c669d0600Michael Jurka protected ArrayList<Boolean> mDirtyPageContent; 150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1618b805b17158886035b38261eb611d8641701ae43Michael Jurka // If true, the subclass should directly update scrollX itself in its computeScroll method 1620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1651262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected boolean mIsPageMoving = false; 1661262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 167f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung // All syncs and layout passes are deferred until data is ready. 168f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected boolean mIsDataReady = false; 169f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 170007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Scrolling indicator 171bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chung private ValueAnimator mScrollIndicatorAnimator; 172afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka private View mScrollIndicator; 173f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung private int mScrollIndicatorPaddingLeft; 174f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung private int mScrollIndicatorPaddingRight; 175007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private boolean mHasScrollIndicator = true; 176bed61d22bdbfcdbe04f92675c7751b83c7a8947cMichael Jurka private boolean mShouldShowScrollIndicator = false; 177bed61d22bdbfcdbe04f92675c7751b83c7a8947cMichael Jurka private boolean mShouldShowScrollIndicatorImmediately = false; 178a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFadeInDuration = 150; 179a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFadeOutDuration = 650; 180a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFlashDuration = 650; 181007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 182b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // If set, will defer loading associated pages until the scrolling settles 1834e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung private boolean mDeferLoadAssociatedPagesUntilScrollCompletes; 184b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 18586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 18686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 187321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 189321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 190321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 197321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 199321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 2019c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView, defStyle, 0); 20260b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0)); 2039c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingTop = a.getDimensionPixelSize( 2041908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingTop, 0); 2059c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingBottom = a.getDimensionPixelSize( 2061908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingBottom, 0); 2079c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingLeft = a.getDimensionPixelSize( 2081908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingLeft, 0); 2099c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingRight = a.getDimensionPixelSize( 2101908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingRight, 0); 211df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutWidthGap = a.getDimensionPixelSize( 2127d7541e7b48fdc114c24b3b0aa75e70d7228041eWinson Chung R.styleable.PagedView_pageLayoutWidthGap, 0); 213df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutHeightGap = a.getDimensionPixelSize( 2147d7541e7b48fdc114c24b3b0aa75e70d7228041eWinson Chung R.styleable.PagedView_pageLayoutHeightGap, 0); 215f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung mScrollIndicatorPaddingLeft = 216f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0); 217f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung mScrollIndicatorPaddingRight = 218f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0); 2199c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen a.recycle(); 2209c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen 221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 2220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 224321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 2280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 22986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 23086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 231e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mScroller = new Scroller(getContext(), new ScrollInterpolator()); 23286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 2337da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung mCenterPagesVertically = true; 234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 239b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen mDensity = getResources().getDisplayMetrics().density; 240265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen 241265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); 242265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity); 243265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity); 2448b805b17158886035b38261eb611d8641701ae43Michael Jurka setOnHierarchyChangeListener(this); 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 } 273360e63fd3e77247002b86da2a77bd8dfe8c8a807Winson Chung int getNextPage() { 274360e63fd3e77247002b86da2a77bd8dfe8c8a807Winson Chung return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; 275360e63fd3e77247002b86da2a77bd8dfe8c8a807Winson Chung } 276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 27786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 28186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 285ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen protected int indexToPage(int index) { 286ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen return index; 287ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen } 288ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen 289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 290bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * Updates the scroll of the current page immediately to its final scroll position. We use this 291bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of 292bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * the previous tab page. 293bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung */ 294bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung protected void updateCurrentPageScroll() { 295a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung int offset = getChildOffset(mCurrentPage); 296a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung int relOffset = getRelativeChildOffset(mCurrentPage); 297a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung int newX = offset - relOffset; 298bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung scrollTo(newX, 0); 299bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung mScroller.setFinalX(newX); 300dd6c09123c2341a51fb285936cfffef48c594356Michael Jurka mScroller.forceFinished(true); 301bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung } 302bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung 303bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung /** 30486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 30686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 30772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (!mScroller.isFinished()) { 30872e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy mScroller.abortAnimation(); 30972e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 310d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // don't introduce any checks like mCurrentPage == currentPage here-- if we change the 311d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // the default 312d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (getChildCount() == 0) { 31372e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy return; 31472e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 315321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 31686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 317bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung updateCurrentPageScroll(); 3185a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung updateScrollingIndicator(); 31986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 320a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung invalidate(); 321321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 3230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 32486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 32586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 326321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 327321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 328321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 329ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageBeginMoving() { 330d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka if (!mIsPageMoving) { 331d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka mIsPageMoving = true; 332d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka onPageBeginMoving(); 333d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka } 3341262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3351262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 336ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageEndMoving() { 337d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka if (mIsPageMoving) { 338d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka mIsPageMoving = false; 339d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka onPageEndMoving(); 340d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka } 3411262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3421262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 34326976d9b53161f80faf822d482ac771b8621cf31Adam Cohen protected boolean isPageMoving() { 34426976d9b53161f80faf822d482ac771b8621cf31Adam Cohen return mIsPageMoving; 34526976d9b53161f80faf822d482ac771b8621cf31Adam Cohen } 34626976d9b53161f80faf822d482ac771b8621cf31Adam Cohen 3470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3481262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageBeginMoving() { 3490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3521262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageEndMoving() { 3530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 355321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 35686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 357321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 358321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 359321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 361321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 362321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 36386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 36586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 366321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 367321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 368321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 369321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 37068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen public void scrollBy(int x, int y) { 3718b805b17158886035b38261eb611d8641701ae43Michael Jurka scrollTo(mUnboundedScrollX + x, getScrollY() + y); 37268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 37368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 37468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen @Override 3750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 37668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mUnboundedScrollX = x; 37768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 37868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (x < 0) { 37968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(0, y); 38068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 38168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x); 38268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 38368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else if (x > mMaxScrollX) { 38468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(mMaxScrollX, y); 38568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 38668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x - mMaxScrollX); 38768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 38868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 389ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = x; 39068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(x, y); 39168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 39268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 3930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 3940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 3950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 3980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 400557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung // Don't bother scrolling if the page does not need to be moved 4018b805b17158886035b38261eb611d8641701ae43Michael Jurka if (getScrollX() != mScroller.getCurrX() 4028b805b17158886035b38261eb611d8641701ae43Michael Jurka || getScrollY() != mScroller.getCurrY() 403b06d95fc79389ce49dc97ecaae23e05d931a6ac2Michael Jurka || mOverScrollX != mScroller.getCurrX()) { 404557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 405557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung } 4060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 4070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 40886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 40986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 41086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 4110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 412b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 413b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // Load the associated pages if necessary 4144e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung if (mDeferLoadAssociatedPagesUntilScrollCompletes) { 415b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung loadAssociatedPages(mCurrentPage); 4164e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung mDeferLoadAssociatedPagesUntilScrollCompletes = false; 417b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 418b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 41973aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // We don't want to trigger a page end moving unless the page has settled 42073aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // and the user has stopped scrolling 42173aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen if (mTouchState == TOUCH_STATE_REST) { 42273aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen pageEndMoving(); 42373aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen } 424c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung 425c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung // Notify the user when the page changes 4268b805b17158886035b38261eb611d8641701ae43Michael Jurka AccessibilityManager accessibilityManager = (AccessibilityManager) 4278b805b17158886035b38261eb611d8641701ae43Michael Jurka getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); 4288b805b17158886035b38261eb611d8641701ae43Michael Jurka if (accessibilityManager.isEnabled()) { 429c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung AccessibilityEvent ev = 430c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); 431c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung ev.getText().add(getCurrentPageDescription()); 432c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung sendAccessibilityEventUnchecked(ev); 433c27d1bbb26f774a14122bc0b1d2ac33675439038Winson Chung } 4340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 4360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 4370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 4380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 4390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 4400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 4410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 442321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 444321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 445321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 446f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 447f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung super.onMeasure(widthMeasureSpec, heightMeasureSpec); 448f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 449f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 450f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 451321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 452321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 455321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 456321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4576b879f0a5885274a85333531e091283405d490ccAdam Lesinski /* Allow the height to be set as WRAP_CONTENT. This allows the particular case 4586b879f0a5885274a85333531e091283405d490ccAdam Lesinski * of the All apps view on XLarge displays to not take up more space then it needs. Width 4596b879f0a5885274a85333531e091283405d490ccAdam Lesinski * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect 4606b879f0a5885274a85333531e091283405d490ccAdam Lesinski * each page to have the same width. 4616b879f0a5885274a85333531e091283405d490ccAdam Lesinski */ 462321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 4636b879f0a5885274a85333531e091283405d490ccAdam Lesinski int heightSize = MeasureSpec.getSize(heightMeasureSpec); 4646b879f0a5885274a85333531e091283405d490ccAdam Lesinski int maxChildHeight = 0; 4656b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4668b805b17158886035b38261eb611d8641701ae43Michael Jurka final int verticalPadding = getPaddingTop() + getPaddingBottom(); 4678b805b17158886035b38261eb611d8641701ae43Michael Jurka final int horizontalPadding = getPaddingLeft() + getPaddingRight(); 468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 46936fcb74425bebc1d88c9e3102484ec902b68f202Michael Jurka 470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 4715f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // unless they were set to WRAP_CONTENT 472785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize); 473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 4755f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // disallowing padding in paged view (just pass 0) 47622f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View child = getPageAt(i); 4775f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 4785f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4795f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childWidthMode; 4805f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.width == LayoutParams.WRAP_CONTENT) { 4815f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.AT_MOST; 4825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4835f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.EXACTLY; 4845f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4865f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childHeightMode; 4875f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.height == LayoutParams.WRAP_CONTENT) { 4885f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.AT_MOST; 4895f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4905f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.EXACTLY; 4915f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4925f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childWidthMeasureSpec = 494ea359c6aee44c0fe3bb94f7002c3b49208b32b7fWinson Chung MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode); 4955f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childHeightMeasureSpec = 4966b879f0a5885274a85333531e091283405d490ccAdam Lesinski MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode); 4975f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4985f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4996b879f0a5885274a85333531e091283405d490ccAdam Lesinski maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); 500785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "\tmeasure-child" + i + ": " + child.getMeasuredWidth() + ", " 501785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + child.getMeasuredHeight()); 5026b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 5036b879f0a5885274a85333531e091283405d490ccAdam Lesinski 5046b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (heightMode == MeasureSpec.AT_MOST) { 5056b879f0a5885274a85333531e091283405d490ccAdam Lesinski heightSize = maxChildHeight + verticalPadding; 506321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 507ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung 508ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung setMeasuredDimension(widthSize, heightSize); 509ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung 510a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung if (childCount > 0) { 511a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", " 512a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung + getChildWidth(0)); 513a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung 514a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung // Calculate the variable page spacing if necessary 515a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung if (mPageSpacing < 0) { 516a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung // The gap between pages in the PagedView should be equal to the gap from the page 517a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung // to the edge of the screen (so it is not visible in the current screen). To 518a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung // account for unequal padding on each side of the paged view, we take the maximum 519a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung // of the left/right gap and use that as the gap between each page. 520a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung int offset = getRelativeChildOffset(0); 521a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung int spacing = Math.max(offset, widthSize - offset - 522a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung getChildAt(0).getMeasuredWidth()); 523a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung setPageSpacing(spacing); 524a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung } 525a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung } 526a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung 52725b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions. 52825b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen // We also wait until we set the measured dimensions before flushing the cache as well, to 52925b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen // ensure that the cache is filled with good values. 53025b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen invalidateCachedOffsets(); 53125b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen updateScrollingIndicatorPosition(); 53225b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen 533faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen if (childCount > 0) { 534faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1); 535faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } else { 536faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = 0; 537faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } 538cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka } 539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 5408c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) { 541af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 5428b805b17158886035b38261eb611d8641701ae43Michael Jurka int delta = newX - getScrollX(); 543af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 5448c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka final int pageCount = getChildCount(); 5458c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka for (int i = 0; i < pageCount; i++) { 54622f823d340710bcde587b18a1542570cbe0360f5Adam Cohen View page = (View) getPageAt(i); 5478c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka page.setX(page.getX() + delta); 548af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 549af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka setCurrentPage(newCurrentPage); 550af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 551af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 5528c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a 5538c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and 554d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // tightens the layout accordingly 555d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka public void setLayoutScale(float childrenScale) { 556d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mLayoutScale = childrenScale; 55773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen invalidateCachedOffsets(); 558d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 559d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Now we need to do a re-layout, but preserving absolute X and Y coordinates 560d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childCount = getChildCount(); 561d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenX[] = new float[childCount]; 562d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenY[] = new float[childCount]; 563d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 56422f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View child = getPageAt(i); 565d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenX[i] = child.getX(); 566d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenY[i] = child.getY(); 567d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 568b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung // Trigger a full re-layout (never just call onLayout directly!) 569b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY); 570b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY); 571b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung requestLayout(); 572b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung measure(widthSpec, heightSpec); 5738b805b17158886035b38261eb611d8641701ae43Michael Jurka layout(getLeft(), getTop(), getRight(), getBottom()); 574d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 57522f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View child = getPageAt(i); 576d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setX(childrenX[i]); 577d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setY(childrenY[i]); 578d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 579b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung 580d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Also, the page offset has changed (since the pages are now smaller); 581d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // update the page offset, but again preserving absolute X and Y coordinates 5828c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka scrollToNewPageWithoutMovingPages(mCurrentPage); 583d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 584d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 58560b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen public void setPageSpacing(int pageSpacing) { 58660b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen mPageSpacing = pageSpacing; 58760b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen invalidateCachedOffsets(); 58860b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen } 58960b0712bf555a32e609271e0b8ea90743a148efbAdam Cohen 590cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka @Override 59128750fba6a2d141eb9a1e566718c17236030b815Michael Jurka protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 592f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 593f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 594f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 595f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 596785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "PagedView.onLayout()"); 5978b805b17158886035b38261eb611d8641701ae43Michael Jurka final int verticalPadding = getPaddingTop() + getPaddingBottom(); 598321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 599a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung int childLeft = getRelativeChildOffset(0); 600321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 601321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 60222f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View child = getPageAt(i); 603321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 604d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int childWidth = getScaledMeasuredWidth(child); 6056b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int childHeight = child.getMeasuredHeight(); 6068b805b17158886035b38261eb611d8641701ae43Michael Jurka int childTop = getPaddingTop(); 6076b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (mCenterPagesVertically) { 6086b879f0a5885274a85333531e091283405d490ccAdam Lesinski childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2; 6096b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 610d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 611785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop); 6126b879f0a5885274a85333531e091283405d490ccAdam Lesinski child.layout(childLeft, childTop, 613d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childLeft + child.getMeasuredWidth(), childTop + childHeight); 6149c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen childLeft += childWidth + mPageSpacing; 615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 617c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung 618c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 619c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung setHorizontalScrollBarEnabled(false); 620dd6c09123c2341a51fb285936cfffef48c594356Michael Jurka updateCurrentPageScroll(); 621c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung setHorizontalScrollBarEnabled(true); 622c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung mFirstLayout = false; 623c3665faf4525de78c64e1a98106e12f1c23810c9Winson Chung } 624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 62673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected void screenScrolled(int screenCenter) { 62773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (isScrollingIndicatorEnabled()) { 62873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen updateScrollingIndicator(); 62973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 630869390b6abd6e005a5ce85a8ee3c11daeecce919Michael Jurka boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; 631869390b6abd6e005a5ce85a8ee3c11daeecce919Michael Jurka 632869390b6abd6e005a5ce85a8ee3c11daeecce919Michael Jurka if (mFadeInAdjacentScreens && !isInOverscroll) { 63373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen for (int i = 0; i < getChildCount(); i++) { 63473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen View child = getChildAt(i); 63573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (child != null) { 63673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen float scrollProgress = getScrollProgress(screenCenter, child, i); 63773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen float alpha = 1 - Math.abs(scrollProgress); 6387372c59277a23a31eeb1513a554b29d7bf05aedbMichael Jurka child.setAlpha(alpha); 639affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 64173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen invalidate(); 642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 643e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 6440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 645e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung @Override 6468b805b17158886035b38261eb611d8641701ae43Michael Jurka public void onChildViewAdded(View parent, View child) { 6472591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen // This ensures that when children are added, they get the correct transforms / alphas 6482591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen // in accordance with any scroll effects. 6492591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen mForceScreenScrolled = true; 6502591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen invalidate(); 65125b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen invalidateCachedOffsets(); 6522591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen } 6532591f6a5202664faf8cde2b68361e3842ce76ba9Adam Cohen 6548b805b17158886035b38261eb611d8641701ae43Michael Jurka @Override 6558b805b17158886035b38261eb611d8641701ae43Michael Jurka public void onChildViewRemoved(View parent, View child) { 6568b805b17158886035b38261eb611d8641701ae43Michael Jurka } 6578b805b17158886035b38261eb611d8641701ae43Michael Jurka 65873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected void invalidateCachedOffsets() { 65973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen int count = getChildCount(); 66025b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen if (count == 0) { 66125b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen mChildOffsets = null; 66225b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen mChildRelativeOffsets = null; 66325b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen mChildOffsetsWithLayoutScale = null; 66425b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen return; 66525b2995c6723c01fbabcea04636358501bc8d17aAdam Cohen } 66673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 66773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsets = new int[count]; 66873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildRelativeOffsets = new int[count]; 66973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsetsWithLayoutScale = new int[count]; 67073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen for (int i = 0; i < count; i++) { 67173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsets[i] = -1; 67273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildRelativeOffsets[i] = -1; 67373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsetsWithLayoutScale[i] = -1; 67473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 67573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 67673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 67773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected int getChildOffset(int index) { 67873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ? 67973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildOffsets : mChildOffsetsWithLayoutScale; 68073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 68173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (childOffsets != null && childOffsets[index] != -1) { 68273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return childOffsets[index]; 68373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } else { 68473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (getChildCount() == 0) 68573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return 0; 68673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 68773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen int offset = getRelativeChildOffset(0); 68873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen for (int i = 0; i < index; ++i) { 68973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing; 69073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 69173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (childOffsets != null) { 69273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen childOffsets[index] = offset; 69373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 69473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return offset; 69573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 69673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 69773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 69873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected int getRelativeChildOffset(int index) { 69973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) { 70073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return mChildRelativeOffsets[index]; 70173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } else { 7028b805b17158886035b38261eb611d8641701ae43Michael Jurka final int padding = getPaddingLeft() + getPaddingRight(); 7038b805b17158886035b38261eb611d8641701ae43Michael Jurka final int offset = getPaddingLeft() + 70473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen (getMeasuredWidth() - padding - getChildWidth(index)) / 2; 70573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen if (mChildRelativeOffsets != null) { 70673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen mChildRelativeOffsets[index] = offset; 70773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 70873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return offset; 70973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 71073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 71173894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 71273894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen protected int getScaledMeasuredWidth(View child) { 71373894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen // This functions are called enough times that it actually makes a difference in the 71473894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen // profiler -- so just inline the max() here 71573894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int measuredWidth = child.getMeasuredWidth(); 71673894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int minWidth = mMinimumWidth; 71773894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth; 71873894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen return (int) (maxWidth * mLayoutScale + 0.5f); 71973894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen } 72073894964c27f68a3a7ffb45bfba76520613fd3f0Adam Cohen 721dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka protected void getVisiblePages(int[] range) { 7220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 7234ff7d79ddcee0a95fe6e7458731decd694b91d03Michael Jurka 724c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka if (pageCount > 0) { 725c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int screenWidth = getMeasuredWidth(); 726c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int leftScreen = 0; 727c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int rightScreen = 0; 72847f747418efa337e0fc56e10748a6a595b8974f6Michael Jurka View currPage = getPageAt(leftScreen); 72980c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka while (leftScreen < pageCount - 1 && 7308b805b17158886035b38261eb611d8641701ae43Michael Jurka currPage.getX() + currPage.getWidth() - 7318b805b17158886035b38261eb611d8641701ae43Michael Jurka currPage.getPaddingRight() < getScrollX()) { 732c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka leftScreen++; 73347f747418efa337e0fc56e10748a6a595b8974f6Michael Jurka currPage = getPageAt(leftScreen); 734c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 735c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = leftScreen; 73647f747418efa337e0fc56e10748a6a595b8974f6Michael Jurka currPage = getPageAt(rightScreen + 1); 73780c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka while (rightScreen < pageCount - 1 && 7388b805b17158886035b38261eb611d8641701ae43Michael Jurka currPage.getX() - currPage.getPaddingLeft() < getScrollX() + screenWidth) { 739c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen++; 74047f747418efa337e0fc56e10748a6a595b8974f6Michael Jurka currPage = getPageAt(rightScreen + 1); 741c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 742dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka range[0] = leftScreen; 743dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka range[1] = rightScreen; 744dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka } else { 745dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka range[0] = -1; 746dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka range[1] = -1; 747dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka } 748dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka } 749dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka 750dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka @Override 751dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka protected void dispatchDraw(Canvas canvas) { 752dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka int halfScreenSize = getMeasuredWidth() / 2; 7538b805b17158886035b38261eb611d8641701ae43Michael Jurka // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. 7548b805b17158886035b38261eb611d8641701ae43Michael Jurka // Otherwise it is equal to the scaled overscroll position. 755ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen int screenCenter = mOverScrollX + halfScreenSize; 756dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka 757dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka if (screenCenter != mLastScreenCenter || mForceScreenScrolled) { 758b06d95fc79389ce49dc97ecaae23e05d931a6ac2Michael Jurka // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can 759b06d95fc79389ce49dc97ecaae23e05d931a6ac2Michael Jurka // set it for the next frame 760b06d95fc79389ce49dc97ecaae23e05d931a6ac2Michael Jurka mForceScreenScrolled = false; 761dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka screenScrolled(screenCenter); 762dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka mLastScreenCenter = screenCenter; 763dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka } 764dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka 765dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka // Find out which screens are visible; as an optimization we only call draw on them 766dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka final int pageCount = getChildCount(); 767dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka if (pageCount > 0) { 768dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka getVisiblePages(mTempVisiblePagesRange); 769dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka final int leftScreen = mTempVisiblePagesRange[0]; 770dde558b8b8bcf3fac6d2ceceafa1f7546f98a264Michael Jurka final int rightScreen = mTempVisiblePagesRange[1]; 771c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung if (leftScreen != -1 && rightScreen != -1) { 772c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung final long drawingTime = getDrawingTime(); 773c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung // Clip to the bounds 774c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung canvas.save(); 7758b805b17158886035b38261eb611d8641701ae43Michael Jurka canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(), 7768b805b17158886035b38261eb611d8641701ae43Michael Jurka getScrollY() + getBottom() - getTop()); 777c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung 77880c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka // On certain graphics drivers, if you draw to a off-screen buffer that's not 77980c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka // used, it can lead to poor performance. We were running into this when 78080c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka // setChildrenLayersEnabled was called on a CellLayout; that triggered a re-draw 78180c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka // of that CellLayout's hardware layer, even if that CellLayout wasn't visible. 78280c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka // As a fix, below we set pages that aren't going to be rendered are to be 78380c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka // View.INVISIBLE, preventing re-drawing of their hardware layer 78480c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka for (int i = getChildCount() - 1; i >= 0; i--) { 78580c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka final View v = getPageAt(i); 7868b805b17158886035b38261eb611d8641701ae43Michael Jurka if (leftScreen <= i && i <= rightScreen && v.getAlpha() > 0) { 78780c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka v.setVisibility(VISIBLE); 78880c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka drawChild(canvas, v, drawingTime); 78980c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka } else { 79080c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka v.setVisibility(INVISIBLE); 79180c69853d64b73e5d62ed33bea1b10680557ee3eMichael Jurka } 792c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung } 793c6f10b906fc9c120cafc81046a42b2434ff635a9Winson Chung canvas.restore(); 794c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 7950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 800ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen int page = indexToPage(indexOfChild(child)); 80186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 80286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 81086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 81186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 81286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 81486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 81686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 81876fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen return v.requestFocus(direction, previouslyFocusedRect); 819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 82686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 82786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 830321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 83186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 83286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 836321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 84186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 84286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 843321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 844321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 84586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 84686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 84986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 85086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 85786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 85986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 86086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 86486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 865321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 867321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 868321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 869321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 872321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 873321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 874321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 875321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 880321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 881321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 885321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 886321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 887321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 89122f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final View currentPage = getPageAt(mCurrentPage); 89286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 895321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 897d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 898d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the previous page. 899d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 900d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsPreviousPage(float x, float y) { 901d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing); 902d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 903d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 904d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 905d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the next page. 906d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 907d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsNextPage(float x, float y) { 908d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing)); 909d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 910d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 911321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 912321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 913321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 9186342bbae1a18f3c1862953e33309481703f541cfAdam Cohen acquireVelocityTrackerAndAddMovement(ev); 919321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 92045e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 92145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 92245e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 924321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 925321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 929321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 935321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 936321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 9401ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka if (mActivePointerId != INVALID_POINTER) { 9411ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka determineScrollingStart(ev); 9421ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka break; 9431ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka } 9441ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 9451ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 9461ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // i.e. fall through to the next case (don't break) 9471ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 9481ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // while it's small- this was causing a crash before we checked for INVALID_POINTER) 949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 954321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 956321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 958c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 959aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 966321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 968fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 9695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 9705f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 9715f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 9725f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 9735f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 9745f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 9755f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 976321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 97786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 978321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 979d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { 980321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 981d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (hitsPreviousPage(x, y)) { 982321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 983d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (hitsNextPage(x, y)) { 984321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 986321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 989321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 990321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 9921d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown case MotionEvent.ACTION_CANCEL: 993321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 994321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 995321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 9966342bbae1a18f3c1862953e33309481703f541cfAdam Cohen releaseVelocityTracker(); 997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 998321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 1000321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 10016342bbae1a18f3c1862953e33309481703f541cfAdam Cohen releaseVelocityTracker(); 1002321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1003321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1004321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1005321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1006321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 1008321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 1010321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1011321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1012f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev) { 1013f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen determineScrollingStart(ev, 1.0f); 1014f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 1015f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1017321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 1018321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 1019321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1020f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { 1021321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 1024321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 10262698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka if (pointerIndex == -1) { 10272698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka return; 10282698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka } 1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1030321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 1033321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1034f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final int touchSlop = Math.round(touchSlopScale * mTouchSlop); 1035321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 1036321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 1037321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 1038321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1039f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (xMoved || xPaged || yMoved) { 10400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 1041321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 1042321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 10436342bbae1a18f3c1862953e33309481703f541cfAdam Cohen mTotalMotionX += Math.abs(mLastMotionX - x); 1044321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 1045c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 10468b805b17158886035b38261eb611d8641701ae43Michael Jurka mTouchX = getScrollX(); 10470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 10480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1049321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1050321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 1051f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen cancelCurrentPageLongPress(); 1052f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 1053f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 1054f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 1055f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void cancelCurrentPageLongPress() { 1056f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (mAllowLongPress) { 1057f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen mAllowLongPress = false; 1058f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // Try canceling the long press. It could also have been scheduled 1059f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // by a distant descendant, so use the mAllowLongPress flag to block 1060f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // everything 1061f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final View currentPage = getPageAt(mCurrentPage); 1062f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (currentPage != null) { 1063f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen currentPage.cancelLongPress(); 1064321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1065321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1067321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1068b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected float getScrollProgress(int screenCenter, View v, int page) { 1069b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen final int halfScreenSize = getMeasuredWidth() / 2; 1070b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1071b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing; 1072b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen int delta = screenCenter - (getChildOffset(page) - 1073b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen getRelativeChildOffset(page) + halfScreenSize); 1074b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1075b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen float scrollProgress = delta / (totalDistance * 1.0f); 1076b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen scrollProgress = Math.min(scrollProgress, 1.0f); 1077b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen scrollProgress = Math.max(scrollProgress, -1.0f); 1078b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen return scrollProgress; 1079b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1080b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1081e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // This curve determines how the effect of scrolling over the limits of the page dimishes 1082e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as the user pulls further and further from the bounds 1083e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private float overScrollInfluenceCurve(float f) { 1084e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 1.0f; 1085e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return f * f * f + 1.0f; 1086e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1087e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1088b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected void acceleratedOverScroll(float amount) { 1089b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen int screenSize = getMeasuredWidth(); 1090b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1091b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen // We want to reach the max over scroll effect when the user has 1092b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen // over scrolled half the size of the screen 1093b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize); 1094b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1095b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen if (f == 0) return; 1096b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1097b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen // Clamp this factor, f, to -1 < f < 1 1098b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen if (Math.abs(f) >= 1) { 1099b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen f /= Math.abs(f); 1100b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1101b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1102b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen int overScrollAmount = (int) Math.round(f * screenSize); 1103b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen if (amount < 0) { 1104ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = overScrollAmount; 11058b805b17158886035b38261eb611d8641701ae43Michael Jurka super.scrollTo(0, getScrollY()); 1106b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } else { 1107ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = mMaxScrollX + overScrollAmount; 11088b805b17158886035b38261eb611d8641701ae43Michael Jurka super.scrollTo(mMaxScrollX, getScrollY()); 1109b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1110b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen invalidate(); 1111b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1112b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1113b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected void dampedOverScroll(float amount) { 1114e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int screenSize = getMeasuredWidth(); 1115e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1116e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float f = (amount / screenSize); 1117e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1118e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (f == 0) return; 1119e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 1120e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 11217bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen // Clamp this factor, f, to -1 < f < 1 11227bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen if (Math.abs(f) >= 1) { 11237bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen f /= Math.abs(f); 11247bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen } 11257bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen 1126e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); 112768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (amount < 0) { 1128ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = overScrollAmount; 11298b805b17158886035b38261eb611d8641701ae43Michael Jurka super.scrollTo(0, getScrollY()); 113068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 1131ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen mOverScrollX = mMaxScrollX + overScrollAmount; 11328b805b17158886035b38261eb611d8641701ae43Michael Jurka super.scrollTo(mMaxScrollX, getScrollY()); 113368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 113468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 113568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 113668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 1137b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen protected void overScroll(float amount) { 1138b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen dampedOverScroll(amount); 1139b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen } 1140b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen 1141c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka protected float maxOverScroll() { 1142c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not 1143b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen // exceed). Used to find out how much extra wallpaper we need for the over scroll effect 1144c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka float f = 1.0f; 1145c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 1146c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka return OVERSCROLL_DAMP_FACTOR * f; 1147c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka } 1148c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka 1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 115145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 115245e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onTouchEvent(ev); 115345e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 1154b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka acquireVelocityTrackerAndAddMovement(ev); 1155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1156321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 1157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 1159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 1160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 1162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 1163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 1165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 1166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 1169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 1170c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1171aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 1172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 11730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 11740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 11750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1176321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1177321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1178321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 1179321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 1180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 1181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 1182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1183c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung final float deltaX = mLastMotionX + mLastMotionXRemainder - x; 1184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1185aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX += Math.abs(deltaX); 1186aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1187c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // Only scroll and update mLastMotionX if we have moved some discrete amount. We 1188c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // keep the remainder because we are actually testing if we've moved from the last 1189c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // scrolled position (which is discrete). 1190c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung if (Math.abs(deltaX) >= 1.0f) { 119168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mTouchX += deltaX; 119268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 119368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (!mDeferScrollUpdate) { 1194c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung scrollBy((int) deltaX, 0); 1195785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX); 119668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 119768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 1198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1199c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionX = x; 1200c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = deltaX - (int) deltaX; 1201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 1203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1204564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen } else { 1205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 1206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 1210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 1211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 1212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 1213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 1215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 12179cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung final int deltaX = (int) (x - mDownMotionX); 121800481b3ed322b4f36b669201d2287964febda65fAdam Cohen final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage)); 121900481b3ed322b4f36b669201d2287964febda65fAdam Cohen boolean isSignificantMove = Math.abs(deltaX) > pageWidth * 122000481b3ed322b4f36b669201d2287964febda65fAdam Cohen SIGNIFICANT_MOVE_THRESHOLD; 1221aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1222b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); 1223b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen 122400481b3ed322b4f36b669201d2287964febda65fAdam Cohen boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING && 1225265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen Math.abs(velocityX) > mFlingThresholdVelocity; 122600481b3ed322b4f36b669201d2287964febda65fAdam Cohen 1227aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // In the case that the page is moved far to one direction and then is flung 1228aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // in the opposite direction, we use a threshold to determine whether we should 1229aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // just return to the starting page, or if we should skip one further. 1230aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen boolean returnToOriginalPage = false; 1231b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && 123200481b3ed322b4f36b669201d2287964febda65fAdam Cohen Math.signum(velocityX) != Math.signum(deltaX) && isFling) { 1233aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen returnToOriginalPage = true; 1234aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } 1235aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1236aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen int finalPage; 1237aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // We give flings precedence over large moves, which is why we short-circuit our 1238aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // test for a large move if a fling has been registered. That is, a large 1239aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // move to the left and fling to the right will register as a fling to the right. 1240aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen if (((isSignificantMove && deltaX > 0 && !isFling) || 1241aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX > 0)) && mCurrentPage > 0) { 1242aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; 1243aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1244aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } else if (((isSignificantMove && deltaX < 0 && !isFling) || 1245aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX < 0)) && 124686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 1247aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1; 1248aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1249321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1250321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1252d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 1253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 125686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 125786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 125886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1262d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 1263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 126686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 126786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 126886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1271321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 12721d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } else { 1273d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka onUnhandledTap(ev); 1274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1277b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 1281b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 1282b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka snapToDestination(); 1283b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1286b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 1290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 1295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1297185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung @Override 1298185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung public boolean onGenericMotionEvent(MotionEvent event) { 1299185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 1300185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung switch (event.getAction()) { 1301185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung case MotionEvent.ACTION_SCROLL: { 1302185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung // Handle mouse (or ext. device) by shifting the page depending on the scroll 1303185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung final float vscroll; 1304185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung final float hscroll; 1305185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { 1306185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung vscroll = 0; 1307185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1308185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } else { 1309185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1310185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); 1311185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1312185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if (hscroll != 0 || vscroll != 0) { 1313185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if (hscroll > 0 || vscroll > 0) { 1314185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung scrollRight(); 1315185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } else { 1316185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung scrollLeft(); 1317185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1318185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung return true; 1319185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1320185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1321185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1322185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1323185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung return super.onGenericMotionEvent(event); 1324185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1325185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung 1326b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { 1327b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker == null) { 1328b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = VelocityTracker.obtain(); 1329b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1330b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.addMovement(ev); 1331b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1332b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1333b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void releaseVelocityTracker() { 1334b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker != null) { 1335b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.recycle(); 1336b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = null; 1337b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1338b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1339b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 1341321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 1342321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 1343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 1344321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 1345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 1346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 1347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 1348321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1349321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 1350321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 1351c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1352321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 1353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 1354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 1355321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 13571d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 13581d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown 1359d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka protected void onUnhandledTap(MotionEvent ev) {} 1360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1361321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1362321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 1363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 1364ae4f155e090bf387b989fb738ee34ab045585711Adam Cohen int page = indexToPage(indexOfChild(child)); 136597d85d23b013347bead4e2f5fa430a79ce69431eWinson Chung if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) { 136686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 1367321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1368321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1369321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1370e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getChildIndexForRelativeOffset(int relativeOffset) { 1371e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int childCount = getChildCount(); 13729c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int left; 13739c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int right; 1374e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung for (int i = 0; i < childCount; ++i) { 13759c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen left = getRelativeChildOffset(i); 137622f823d340710bcde587b18a1542570cbe0360f5Adam Cohen right = (left + getScaledMeasuredWidth(getPageAt(i))); 1377e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung if (left <= relativeOffset && relativeOffset <= right) { 1378e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return i; 1379e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1380e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1381e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return -1; 1382e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1383e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 13841908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung protected int getChildWidth(int index) { 138563257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // This functions are called enough times that it actually makes a difference in the 138663257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // profiler -- so just inline the max() here 138722f823d340710bcde587b18a1542570cbe0360f5Adam Cohen final int measuredWidth = getPageAt(index).getMeasuredWidth(); 138863257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int minWidth = mMinimumWidth; 138963257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung return (minWidth > measuredWidth) ? minWidth : measuredWidth; 13901908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung } 13911908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung 1392d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen int getPageNearestToCenterOfScreen() { 139322f823d340710bcde587b18a1542570cbe0360f5Adam Cohen int minDistanceFromScreenCenter = Integer.MAX_VALUE; 1394321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 13958b805b17158886035b38261eb611d8641701ae43Michael Jurka int screenCenter = getScrollX() + (getMeasuredWidth() / 2); 1396321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 1397321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 139822f823d340710bcde587b18a1542570cbe0360f5Adam Cohen View layout = (View) getPageAt(i); 1399d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 1400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 1401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 1402321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 1403321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 1404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 1405321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 1406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1407321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1408d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen return minDistanceFromScreenCenterIndex; 1409d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen } 1410d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen 1411d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen protected void snapToDestination() { 1412d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); 1413321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1414321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1415e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static class ScrollInterpolator implements Interpolator { 1416e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public ScrollInterpolator() { 1417e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1418e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1419e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public float getInterpolation(float t) { 1420e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen t -= 1.0f; 1421e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return t*t*t*t*t + 1; 1422e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1423e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1424e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1425e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // We want the duration of the page snap animation to be influenced by the distance that 1426e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // the screen has to travel, however, we don't want this duration to be effected in a 1427e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // purely linear fashion. Instead, we use this method to moderate the effect that the distance 1428e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // of travel has on the overall snap duration. 1429e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceInfluenceForSnapDuration(float f) { 1430e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 0.5f; // center the values about 0. 1431e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f *= 0.3f * Math.PI / 2.0f; 1432e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return (float) Math.sin(f); 1433e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1434e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 14350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 1436e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); 1437e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int halfScreenSize = getMeasuredWidth() / 2; 1438e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1439785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); 1440785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): " 1441785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getMeasuredWidth() + ", " + getChildWidth(whichPage)); 1442e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 1443e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int delta = newX - mUnboundedScrollX; 1444e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int duration = 0; 1445e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1446265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen if (Math.abs(velocity) < mMinFlingVelocity) { 1447e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // If the velocity is low enough, then treat this more as an automatic page advance 1448e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as opposed to an apparent physical response to flinging 1449e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1450e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return; 1451e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1452e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1453e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // Here we compute a "distance" that will be used in the computation of the overall 1454e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // snap duration. This is a function of the actual distance that needs to be traveled; 1455e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we keep this value close to half screen size in order to reduce the variance in snap 1456e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // duration as a function of the distance the page needs to travel. 145720b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize)); 1458e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distance = halfScreenSize + halfScreenSize * 1459e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen distanceInfluenceForSnapDuration(distanceRatio); 1460e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1461e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.abs(velocity); 1462265b9a66542148e0ff13971001cb9461065e1e0eAdam Cohen velocity = Math.max(mMinSnapVelocity, velocity); 1463e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1464e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we want the page's snap velocity to approximately match the velocity at which the 1465e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // user flings, so we scale the duration by a value near to the derivative of the scroll 146620b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka // interpolator at zero, ie. 5. We use 4 to make it a little slower. 146720b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); 1468e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1469e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, delta, duration); 14700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 14735f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 14760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 147786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 1478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1479785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); 1480785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", " 1481785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getChildWidth(whichPage)); 148286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 148368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen final int sX = mUnboundedScrollX; 1484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 14850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 14860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 14890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 14900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 14920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 149322f823d340710bcde587b18a1542570cbe0360f5Adam Cohen focusedChild == getPageAt(mCurrentPage)) { 14940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 14950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 1499321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 1500321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 1501321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1502321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1503321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 150468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); 150580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1506b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // Load associated pages immediately if someone else is handling the scroll, otherwise defer 1507b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // loading associated pages until the scroll settles 1508b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung if (mDeferScrollUpdate) { 1509b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung loadAssociatedPages(mNextPage); 1510b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } else { 15114e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung mDeferLoadAssociatedPagesUntilScrollCompletes = true; 1512b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 15130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 1514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 1515321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1516321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1517321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 1518321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 151986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 1520321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 152186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 1522321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1523321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1524321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 1526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 152786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 1528321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 152986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 1530321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 153386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 1534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 1535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 1536321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 1537321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 1538321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 153922f823d340710bcde587b18a1542570cbe0360f5Adam Cohen if (vp == getPageAt(i)) { 1540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 1541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 1545321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1546321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1547321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 1548321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 1549321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1550321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 1551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 1552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 15540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 15550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 15560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 15570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 15580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 15590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 15600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 15610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1562321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 156386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 1564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 1566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 1567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1568321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1569321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 1570321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 157186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 1572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1573321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1574321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1575321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 1576321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 157786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 1578321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1579321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1580321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 1581321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 1582321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 1583321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 1584321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1585321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1586321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 1587321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 1588321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1589321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 1590321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1591321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1592f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung protected void loadAssociatedPages(int page) { 1593f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung loadAssociatedPages(page, false); 1594f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung } 1595f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung protected void loadAssociatedPages(int page, boolean immediateAndOnly) { 15960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 15970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 15980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 1599e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int lowerPageBound = getAssociatedLowerPageBound(page); 1600e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int upperPageBound = getAssociatedUpperPageBound(page); 1601785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/" 1602785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + upperPageBound); 16030cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka // First, clear any pages that should no longer be loaded 16040cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka for (int i = 0; i < count; ++i) { 16050cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka Page layout = (Page) getPageAt(i); 16062a4b1a809c6bc0af9ae3ad974fe50e2a91569543Michael Jurka if ((i < lowerPageBound) || (i > upperPageBound)) { 16070cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka if (layout.getPageChildCount() > 0) { 16080cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka layout.removeAllViewsOnPage(); 16090cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka } 16100cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka mDirtyPageContent.set(i, true); 16110cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka } 16120cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka } 16130cad1116add5303d7c06cc21182527cebc42aff6Michael Jurka // Next, load any new pages 16140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 1615f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung if ((i != page) && immediateAndOnly) { 1616f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung continue; 1617f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung } 16180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 16190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 1620f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung syncPageItems(i, (i == page) && immediateAndOnly); 16210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 16220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 162380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 162480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 162580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 162680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 162780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 162880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1629e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedLowerPageBound(int page) { 1630e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.max(0, page - 1); 1631e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1632e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedUpperPageBound(int page) { 1633e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int count = getChildCount(); 1634e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.min(page + 1, count - 1); 1635e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1636e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 163786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 163886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 163986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 164086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 164186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 164286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 164486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 164586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 164686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 164786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 164886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1649f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung public abstract void syncPageItems(int page, boolean immediate); 165086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1651244d74cb353f1260c4d633e719bf84bb3b6e52bcPatrick Dubroy protected void invalidatePageData() { 1652f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung invalidatePageData(-1, false); 16535a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 16545a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung protected void invalidatePageData(int currentPage) { 1655f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung invalidatePageData(currentPage, false); 1656f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung } 1657f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung protected void invalidatePageData(int currentPage, boolean immediateAndOnly) { 1658f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 1659f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 1660f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 1661f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 16620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 16630cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen // Force all scrolling-related behavior to end 16640cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen mScroller.forceFinished(true); 16650cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen mNextPage = INVALID_PAGE; 16660cd3b64e607cc1f81ed49b00ef7398c2a2b7a48dAdam Cohen 16670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 16680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 166986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 16705a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // We must force a measure after we've loaded the pages to update the content width and 16715a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // to determine the full scroll width 16725a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), 16735a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); 16745a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung 16755a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // Set a new page as the current page if necessary 16765a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung if (currentPage > -1) { 16775afbf7bdd6f6879124c9b8283e005fe57f310d02Winson Chung setCurrentPage(Math.min(getPageCount() - 1, currentPage)); 16785a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 16795a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung 16800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 16810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 16820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 16830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 16840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 16850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 168686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 16870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 1688f314b0eab03bb354c14b6c89f694a6685bd246d9Winson Chung loadAssociatedPages(mCurrentPage, immediateAndOnly); 16890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 16900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1692007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1693afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka protected View getScrollingIndicator() { 1694007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // We use mHasScrollIndicator to prevent future lookups if there is no sibling indicator 1695007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // found 1696007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mHasScrollIndicator && mScrollIndicator == null) { 1697007c69867d821ea2b271398577a8b3440b3a7046Winson Chung ViewGroup parent = (ViewGroup) getParent(); 1698a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung if (parent != null) { 1699a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung mScrollIndicator = (View) (parent.findViewById(R.id.paged_view_indicator)); 1700a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung mHasScrollIndicator = mScrollIndicator != null; 1701a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung if (mHasScrollIndicator) { 1702a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung mScrollIndicator.setVisibility(View.VISIBLE); 1703a128a7b9e5f00559ad6a443f7be8e8d7591942a3Winson Chung } 1704007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1705007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1706007c69867d821ea2b271398577a8b3440b3a7046Winson Chung return mScrollIndicator; 1707007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1708007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1709007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected boolean isScrollingIndicatorEnabled() { 1710649723cfb3d73af16dd02462725700897ca60e38Winson Chung return !LauncherApplication.isScreenLarge(); 1711007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1712007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 17135a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung Runnable hideScrollingIndicatorRunnable = new Runnable() { 17145a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung @Override 17155a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung public void run() { 17165a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung hideScrollingIndicator(false); 17175a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 17185a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung }; 1719b737ee6ab2dc4938a1b2a574c336af0eab826712Michael Jurka protected void flashScrollingIndicator(boolean animated) { 17205a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung removeCallbacks(hideScrollingIndicatorRunnable); 1721b737ee6ab2dc4938a1b2a574c336af0eab826712Michael Jurka showScrollingIndicator(!animated); 17225a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration); 17233ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 17243ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 1725430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka protected void showScrollingIndicator(boolean immediately) { 1726bed61d22bdbfcdbe04f92675c7751b83c7a8947cMichael Jurka mShouldShowScrollIndicator = true; 1727bed61d22bdbfcdbe04f92675c7751b83c7a8947cMichael Jurka mShouldShowScrollIndicatorImmediately = true; 1728007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1729007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1730007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1731bed61d22bdbfcdbe04f92675c7751b83c7a8947cMichael Jurka mShouldShowScrollIndicator = false; 1732007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1733007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1734007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Fade the indicator in 1735007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 173632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.setVisibility(View.VISIBLE); 173721b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen cancelScrollingIndicatorAnimations(); 1738430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka if (immediately) { 1739430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka mScrollIndicator.setAlpha(1f); 1740430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka } else { 1741430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f); 1742430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration); 1743430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka mScrollIndicatorAnimator.start(); 1744430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka } 1745007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1746007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1747007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 174821b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen protected void cancelScrollingIndicatorAnimations() { 174921b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen if (mScrollIndicatorAnimator != null) { 175021b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen mScrollIndicatorAnimator.cancel(); 175121b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen } 175221b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen } 175321b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen 1754007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected void hideScrollingIndicator(boolean immediately) { 1755007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1756007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1757007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1758007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1759007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1760007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Fade the indicator out 1761007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 176221b411074e39fd10cbc8dd0220bb159da4af2c76Adam Cohen cancelScrollingIndicatorAnimations(); 176332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (immediately) { 176481efbad05e0ce69b06d7323749ef2e200b84b98eMichael Jurka mScrollIndicator.setVisibility(View.INVISIBLE); 176532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.setAlpha(0f); 176632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } else { 176732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f); 176832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration); 176932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() { 177032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung private boolean cancelled = false; 177132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung @Override 177232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung public void onAnimationCancel(android.animation.Animator animation) { 177332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung cancelled = true; 177432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 177532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung @Override 177632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung public void onAnimationEnd(Animator animation) { 177732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (!cancelled) { 177881efbad05e0ce69b06d7323749ef2e200b84b98eMichael Jurka mScrollIndicator.setVisibility(View.INVISIBLE); 177932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 178032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 178132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung }); 178232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.start(); 178332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 1784007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1785007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1786007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 178732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung /** 178832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung * To be overridden by subclasses to determine whether the scroll indicator should stretch to 178932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung * fill its space on the track or not. 179032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung */ 179132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung protected boolean hasElasticScrollIndicator() { 1792dea74b7d12b0fcd50bfdb4274f9867ba76d75238Winson Chung return true; 179332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 179432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung 1795007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private void updateScrollingIndicator() { 1796007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1797007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1798007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1799007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1800007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1801007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 1802007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1803bed61d22bdbfcdbe04f92675c7751b83c7a8947cMichael Jurka if (mShouldShowScrollIndicator) { 1804bed61d22bdbfcdbe04f92675c7751b83c7a8947cMichael Jurka showScrollingIndicator(mShouldShowScrollIndicatorImmediately); 1805bed61d22bdbfcdbe04f92675c7751b83c7a8947cMichael Jurka } 1806007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1807007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1808007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private void updateScrollingIndicatorPosition() { 1809649723cfb3d73af16dd02462725700897ca60e38Winson Chung if (!isScrollingIndicatorEnabled()) return; 1810430e8a5f5b06181addfd37223518fc21c5c41b69Michael Jurka if (mScrollIndicator == null) return; 181132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int numPages = getChildCount(); 181232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int pageWidth = getMeasuredWidth(); 1813ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung int lastChildIndex = Math.max(0, getChildCount() - 1); 1814ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex); 1815f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight; 181632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorWidth = mScrollIndicator.getMeasuredWidth() - 181732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight(); 181832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung 1819ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX)); 182032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorSpace = trackWidth / numPages; 1821ae890b85dfbcdef592f4a97cca819922a07a83caWinson Chung int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft; 182232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (hasElasticScrollIndicator()) { 182332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) { 182432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.getLayoutParams().width = indicatorSpace; 182532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.requestLayout(); 182632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 182732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } else { 182832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2; 182932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung indicatorPos += indicatorCenterOffset; 183032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 1831007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.setTranslationX(indicatorPos); 18323ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 18333ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 18343ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung public void showScrollIndicatorTrack() { 18353ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 18363ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 18373ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung public void hideScrollIndicatorTrack() { 1838007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 18396a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18406a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung /* Accessibility */ 18416a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 18426a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 18436a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onInitializeAccessibilityNodeInfo(info); 18446a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung info.setScrollable(true); 18456a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18466a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18476a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 18486a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 18496a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onInitializeAccessibilityEvent(event); 18506a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setScrollable(true); 18516a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { 18526a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setFromIndex(mCurrentPage); 18536a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setToIndex(mCurrentPage); 18546a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setItemCount(getChildCount()); 18556a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18566a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18576a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18586a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung protected String getCurrentPageDescription() { 18598b805b17158886035b38261eb611d8641701ae43Michael Jurka return String.format(getContext().getString(R.string.default_scroll_format), 1860360e63fd3e77247002b86da2a77bd8dfe8c8a807Winson Chung getNextPage() + 1, getChildCount()); 18616a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 1862d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung 1863d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung @Override 1864d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung public boolean onHoverEvent(android.view.MotionEvent event) { 1865d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung return true; 1866d11265e3738d93df3d8f76732e027f4d9be2a0abWinson Chung } 1867321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1868