PagedView.java revision a6427b15c18d5b8f3078f553d78f8432de9f46e9
1321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/* 2321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Copyright (C) 2010 The Android Open Source Project 3321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 4321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Licensed under the Apache License, Version 2.0 (the "License"); 5321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * you may not use this file except in compliance with the License. 6321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * You may obtain a copy of the License at 7321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 8321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * http://www.apache.org/licenses/LICENSE-2.0 9321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 10321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Unless required by applicable law or agreed to in writing, software 11321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * distributed under the License is distributed on an "AS IS" BASIS, 12321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * See the License for the specific language governing permissions and 14321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * limitations under the License. 15321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 16321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 17321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpackage com.android.launcher2; 18321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 19228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.Animator; 20228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorInflater; 21228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorListenerAdapter; 22228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.ObjectAnimator; 23bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chungimport android.animation.ValueAnimator; 24321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context; 259c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray; 26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas; 27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect; 28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel; 29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable; 30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet; 31785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chungimport android.util.Log; 325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode; 33185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.InputDevice; 34185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.KeyEvent; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 38321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 39321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 40321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 416a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityEvent; 426a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityNodeInfo; 43e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator; 445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable; 45007c69867d821ea2b271398577a8b3440b3a7046Winson Chungimport android.widget.ImageView; 46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 480499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R; 4980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 506a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport java.util.ArrayList; 516a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/** 53321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a 540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages" 55321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 56321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup { 57321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final String TAG = "PagedView"; 58785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung private static final boolean DEBUG = false; 590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final int INVALID_PAGE = -1; 60321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 6186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // the min drag distance for a fling to register, to prevent random page shifts 629cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_FLING = 25; 639cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung // The min drag distance to trigger a page shift (regardless of velocity) 649cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_MOVE = 200; 65321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 66e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int PAGE_SNAP_ANIMATION_DURATION = 550; 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 69b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung private static final float OVERSCROLL_DAMP_FACTOR = 0.14f; 70e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MINIMUM_SNAP_VELOCITY = 2200; 71e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MIN_FLING_VELOCITY = 250; 72b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; 7368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 8468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mMaxScrollX; 850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 88321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 897426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionX; 90c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung protected float mLastMotionXRemainder; 917426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionY; 92aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen protected float mTotalMotionX; 93f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private int mLastScreenCenter = -1; 94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 99e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; 100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 104321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1057426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected boolean mAllowLongPress = true; 106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1077426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected int mTouchSlop; 108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 1101908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung private int mMinimumWidth; 1119c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageSpacing; 1129c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingTop; 1139c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingBottom; 1149c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingLeft; 1159c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingRight; 116df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutWidthGap; 117df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutHeightGap; 11887b1490c5a100619648b251cb2be05c457bede08Michael Jurka protected int mCellCountX = 0; 11987b1490c5a100619648b251cb2be05c457bede08Michael Jurka protected int mCellCountY = 0; 1207da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung protected boolean mCenterPagesVertically; 12168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected boolean mAllowOverScroll = true; 12268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mUnboundedScrollX; 123321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1248c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // parameter that adjusts the layout to be optimized for pages with that scale factor 125d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected float mLayoutScale = 1.0f; 126d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1275f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected static final int INVALID_POINTER = -1; 128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1295f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected int mActivePointerId = INVALID_POINTER; 130321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 13186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 132321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 13386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private ArrayList<Boolean> mDirtyPageContent; 13486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private boolean mDirtyPageAlpha; 135321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // choice modes 1375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_NONE = 0; 1385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_SINGLE = 1; 1395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // Multiple selection mode is not supported by all Launcher actions atm 1405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_MULTIPLE = 2; 1419f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 142e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka protected int mChoiceMode; 1435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private ActionMode mActionMode; 1445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1591262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected boolean mIsPageMoving = false; 1601262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 161f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung // All syncs and layout passes are deferred until data is ready. 162f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected boolean mIsDataReady = false; 163f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 164007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Scrolling indicator 165bb6f6a52b6d176be253b1514af459a7aa4e998f8Winson Chung private ValueAnimator mScrollIndicatorAnimator; 166007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private ImageView mScrollIndicator; 167f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung private int mScrollIndicatorPaddingLeft; 168f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung private int mScrollIndicatorPaddingRight; 169007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private boolean mHasScrollIndicator = true; 170a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFadeInDuration = 150; 171a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFadeOutDuration = 650; 172a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung protected static final int sScrollIndicatorFlashDuration = 650; 173007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 174b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // If set, will defer loading associated pages until the scrolling settles 1754e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung private boolean mDeferLoadAssociatedPagesUntilScrollCompletes; 176b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 17786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 17886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 179321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 183321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 185321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 186321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 187321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 189321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 190321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 1915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1939c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 1949c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView, defStyle, 0); 1959c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0); 1969c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingTop = a.getDimensionPixelSize( 1971908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingTop, 0); 1989c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingBottom = a.getDimensionPixelSize( 1991908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingBottom, 0); 2009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingLeft = a.getDimensionPixelSize( 2011908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingLeft, 0); 2029c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingRight = a.getDimensionPixelSize( 2031908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingRight, 0); 204df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutWidthGap = a.getDimensionPixelSize( 205df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutWidthGap, -1); 206df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutHeightGap = a.getDimensionPixelSize( 207df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutHeightGap, -1); 208f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung mScrollIndicatorPaddingLeft = 209f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0); 210f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung mScrollIndicatorPaddingRight = 211f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0); 2129c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen a.recycle(); 2139c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen 214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 2150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 2210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 22286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 22386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 224e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mScroller = new Scroller(getContext(), new ScrollInterpolator()); 22586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 2267da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung mCenterPagesVertically = true; 227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 23486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 23586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 23686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 23786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 242f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung * Called by subclasses to mark that data is ready, and that we can begin loading and laying 243f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung * out pages. 244f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung */ 245f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected void setDataIsReady() { 246f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung mIsDataReady = true; 247f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 248f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected boolean isDataReady() { 249f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return mIsDataReady; 250f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 251f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 252f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung /** 25386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 25586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 25786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 25886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 26186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 26586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 270bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * Updates the scroll of the current page immediately to its final scroll position. We use this 271bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of 272bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * the previous tab page. 273bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung */ 274bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung protected void updateCurrentPageScroll() { 275bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 276bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung scrollTo(newX, 0); 277bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung mScroller.setFinalX(newX); 278bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung } 279bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung 280bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung /** 28186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 28386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 28472e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (!mScroller.isFinished()) { 28572e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy mScroller.abortAnimation(); 28672e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 287d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // don't introduce any checks like mCurrentPage == currentPage here-- if we change the 288d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // the default 289d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (getChildCount() == 0) { 29072e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy return; 29172e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 29386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 294bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung updateCurrentPageScroll(); 2955a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung updateScrollingIndicator(); 29686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 297a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung invalidate(); 298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 3000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 30186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 30286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 306ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageBeginMoving() { 307d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka if (!mIsPageMoving) { 308d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka mIsPageMoving = true; 309d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka onPageBeginMoving(); 310d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka } 3111262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3121262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 313ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageEndMoving() { 314d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka if (mIsPageMoving) { 315d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka mIsPageMoving = false; 316d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka onPageEndMoving(); 317d74c984fcc61f86160476a2543faff715e3ee2fdMichael Jurka } 3181262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3191262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 32026976d9b53161f80faf822d482ac771b8621cf31Adam Cohen protected boolean isPageMoving() { 32126976d9b53161f80faf822d482ac771b8621cf31Adam Cohen return mIsPageMoving; 32226976d9b53161f80faf822d482ac771b8621cf31Adam Cohen } 32326976d9b53161f80faf822d482ac771b8621cf31Adam Cohen 3240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3251262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageBeginMoving() { 326007c69867d821ea2b271398577a8b3440b3a7046Winson Chung showScrollingIndicator(); 3270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3301262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageEndMoving() { 331007c69867d821ea2b271398577a8b3440b3a7046Winson Chung hideScrollingIndicator(false); 3320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 334321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 33586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 336321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 338321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 341321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 34286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 34486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 348321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 34968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen public void scrollBy(int x, int y) { 35068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen scrollTo(mUnboundedScrollX + x, mScrollY + y); 35168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 35268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 35368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen @Override 3540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 35568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mUnboundedScrollX = x; 35668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 35768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (x < 0) { 35868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(0, y); 35968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 36068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x); 36168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 36268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else if (x > mMaxScrollX) { 36368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(mMaxScrollX, y); 36468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 36568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x - mMaxScrollX); 36668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 36768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 36868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(x, y); 36968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 37068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 3720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 3730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 3760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 377321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 378557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung // Don't bother scrolling if the page does not need to be moved 379557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung if (mScrollX != mScroller.getCurrX() || mScrollY != mScroller.getCurrY()) { 380557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung mDirtyPageAlpha = true; 381557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 382557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung } 3830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 3840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 38586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 3865f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 38786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 38886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 3890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 390b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 391b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // Load the associated pages if necessary 3924e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung if (mDeferLoadAssociatedPagesUntilScrollCompletes) { 393b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung loadAssociatedPages(mCurrentPage); 3944e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung mDeferLoadAssociatedPagesUntilScrollCompletes = false; 395b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 396b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 39773aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // We don't want to trigger a page end moving unless the page has settled 39873aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // and the user has stopped scrolling 39973aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen if (mTouchState == TOUCH_STATE_REST) { 40073aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen pageEndMoving(); 40173aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen } 4020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 403321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 4040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 4050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 4060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 4070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 4080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 4090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 411321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 412321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 413321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 414f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 415f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung super.onMeasure(widthMeasureSpec, heightMeasureSpec); 416f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 417f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 418f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 422321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 424321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4256b879f0a5885274a85333531e091283405d490ccAdam Lesinski /* Allow the height to be set as WRAP_CONTENT. This allows the particular case 4266b879f0a5885274a85333531e091283405d490ccAdam Lesinski * of the All apps view on XLarge displays to not take up more space then it needs. Width 4276b879f0a5885274a85333531e091283405d490ccAdam Lesinski * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect 4286b879f0a5885274a85333531e091283405d490ccAdam Lesinski * each page to have the same width. 4296b879f0a5885274a85333531e091283405d490ccAdam Lesinski */ 430321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 4316b879f0a5885274a85333531e091283405d490ccAdam Lesinski int heightSize = MeasureSpec.getSize(heightMeasureSpec); 4326b879f0a5885274a85333531e091283405d490ccAdam Lesinski int maxChildHeight = 0; 4336b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4346b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 43636fcb74425bebc1d88c9e3102484ec902b68f202Michael Jurka 437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 4385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // unless they were set to WRAP_CONTENT 439785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize); 440321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 441321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 4425f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // disallowing padding in paged view (just pass 0) 4435f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final View child = getChildAt(i); 4445f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 4455f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4465f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childWidthMode; 4475f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.width == LayoutParams.WRAP_CONTENT) { 4485f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.AT_MOST; 4495f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4505f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.EXACTLY; 4515f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4525f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4535f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childHeightMode; 4545f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.height == LayoutParams.WRAP_CONTENT) { 4555f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.AT_MOST; 4565f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4575f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.EXACTLY; 4585f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4595f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4605f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childWidthMeasureSpec = 4615f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka MeasureSpec.makeMeasureSpec(widthSize, childWidthMode); 4625f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childHeightMeasureSpec = 4636b879f0a5885274a85333531e091283405d490ccAdam Lesinski MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode); 4645f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4655f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4666b879f0a5885274a85333531e091283405d490ccAdam Lesinski maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); 467785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "\tmeasure-child" + i + ": " + child.getMeasuredWidth() + ", " 468785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + child.getMeasuredHeight()); 4696b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 4706b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4716b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (heightMode == MeasureSpec.AT_MOST) { 4726b879f0a5885274a85333531e091283405d490ccAdam Lesinski heightSize = maxChildHeight + verticalPadding; 473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 474faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen if (childCount > 0) { 475faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1); 476faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } else { 477faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = 0; 478faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } 479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 480321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setMeasuredDimension(widthSize, heightSize); 481cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka } 482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4838c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) { 484af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 485af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int delta = newX - mScrollX; 486af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 4878c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka final int pageCount = getChildCount(); 4888c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka for (int i = 0; i < pageCount; i++) { 4898c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka View page = (View) getChildAt(i); 4908c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka page.setX(page.getX() + delta); 491af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 492af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka setCurrentPage(newCurrentPage); 493af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 494af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 4958c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a 4968c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and 497d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // tightens the layout accordingly 498d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka public void setLayoutScale(float childrenScale) { 499d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mLayoutScale = childrenScale; 500d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 501d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Now we need to do a re-layout, but preserving absolute X and Y coordinates 502d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childCount = getChildCount(); 503d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenX[] = new float[childCount]; 504d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenY[] = new float[childCount]; 505d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 506d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 507d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenX[i] = child.getX(); 508d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenY[i] = child.getY(); 509d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 510b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung // Trigger a full re-layout (never just call onLayout directly!) 511b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY); 512b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY); 513b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung requestLayout(); 514b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung measure(widthSpec, heightSpec); 515b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung layout(mLeft, mTop, mRight, mBottom); 516d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 517d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 518d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setX(childrenX[i]); 519d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setY(childrenY[i]); 520d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 521b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung 522d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Also, the page offset has changed (since the pages are now smaller); 523d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // update the page offset, but again preserving absolute X and Y coordinates 5248c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka scrollToNewPageWithoutMovingPages(mCurrentPage); 525d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 526d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 527cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka @Override 52828750fba6a2d141eb9a1e566718c17236030b815Michael Jurka protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 529f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 530f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 531f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 532f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 533785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "PagedView.onLayout()"); 5345f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(false); 536cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 537cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka scrollTo(newX, 0); 538cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka mScroller.setFinalX(newX); 539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(true); 540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mFirstLayout = false; 541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 5436b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 545321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 546321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 547785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", " 548785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getChildWidth(0)); 549e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung childLeft = getRelativeChildOffset(0); 550321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final View child = getChildAt(i); 554321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 555d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int childWidth = getScaledMeasuredWidth(child); 5566b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int childHeight = child.getMeasuredHeight(); 5576b879f0a5885274a85333531e091283405d490ccAdam Lesinski int childTop = mPaddingTop; 5586b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (mCenterPagesVertically) { 5596b879f0a5885274a85333531e091283405d490ccAdam Lesinski childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2; 5606b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 561d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 562785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop); 5636b879f0a5885274a85333531e091283405d490ccAdam Lesinski child.layout(childLeft, childTop, 564d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childLeft + child.getMeasuredWidth(), childTop + childHeight); 5659c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen childLeft += childWidth + mPageSpacing; 566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 568d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 569d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mFirstLayout = false; 570d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 57303929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung protected void forceUpdateAdjacentPagesAlpha() { 57403929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung mDirtyPageAlpha = true; 57503929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung updateAdjacentPagesAlpha(); 57603929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung } 57703929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung 578e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void updateAdjacentPagesAlpha() { 5790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 5800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { 58163257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung int screenWidth = getMeasuredWidth(); 58263257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung int halfScreenSize = screenWidth / 2; 583e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int screenCenter = mScrollX + halfScreenSize; 5840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = getChildCount(); 5850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < childCount; ++i) { 5860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 587d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 5880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int halfChildWidth = (childWidth / 2); 5890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childCenter = getChildOffset(i) + halfChildWidth; 590e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 591b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // On the first layout, we may not have a width nor a proper offset, so for now 592b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // we should just assume full page width (and calculate the offset according to 593b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // that). 594b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung if (childWidth <= 0) { 59563257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung childWidth = screenWidth; 596b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childCenter = (i * childWidth) + (childWidth / 2); 597b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung } 598b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 599e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int d = halfChildWidth; 600e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int distanceFromScreenCenter = childCenter - screenCenter; 601e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (distanceFromScreenCenter > 0) { 602e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i > 0) { 603d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i - 1)) / 2; 604f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } else { 605f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung continue; 606e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 6070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 608e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i < childCount - 1) { 609d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i + 1)) / 2; 610f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } else { 611f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung continue; 612e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 6130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 6149c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen d += mPageSpacing; 615e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 616b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // Preventing potential divide-by-zero 617b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung d = Math.max(1, d); 618b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 619e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d; 620e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); 621e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float alpha = 1.0f - dimAlpha; 622e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 623f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (alpha < ALPHA_QUANTIZE_LEVEL) { 624f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 0.0f; 625f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) { 626f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 1.0f; 627f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 628f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 629a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // Due to the way we're setting alpha on our children in PagedViewCellLayout, 630a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // this optimization causes alpha to not be properly updated sometimes (repro 631a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // case: in xlarge mode, swipe to second page in All Apps, then click on "My 632a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // Apps" tab. the page will have alpha 0 until you swipe it). Removing 633a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // optimization fixes the issue, but we should fix this in a better manner 634cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka //if (Float.compare(alpha, layout.getAlpha()) != 0) { 6350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.setAlpha(alpha); 636cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka //} 637affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 6380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = false; 639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 641e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 6420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 643f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen protected void screenScrolled(int screenCenter) { 644007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicator(); 645f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 646f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 647e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung @Override 648e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void dispatchDraw(Canvas canvas) { 649f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int halfScreenSize = getMeasuredWidth() / 2; 650f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int screenCenter = mScrollX + halfScreenSize; 651f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 652f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (screenCenter != mLastScreenCenter) { 653f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen screenScrolled(screenCenter); 654f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen updateAdjacentPagesAlpha(); 655f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mLastScreenCenter = screenCenter; 656f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 6570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 658e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung // Find out which screens are visible; as an optimization we only call draw on them 6590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // As an optimization, this code assumes that all pages have the same width as the 0th 6600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page. 6610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 662c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka if (pageCount > 0) { 663d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int pageWidth = getScaledMeasuredWidth(getChildAt(0)); 664c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int screenWidth = getMeasuredWidth(); 665557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung int x = getScaledRelativeChildOffset(0) + pageWidth; 666c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int leftScreen = 0; 667c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int rightScreen = 0; 668c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka while (x <= mScrollX) { 669c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka leftScreen++; 670c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(leftScreen)) + mPageSpacing; 671c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 672c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = leftScreen; 673c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung while (x < mScrollX + screenWidth && rightScreen < pageCount) { 674c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen++; 675c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung if (rightScreen < pageCount) { 676c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(rightScreen)) + mPageSpacing; 677c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung } 678c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 679c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = Math.min(getChildCount() - 1, rightScreen); 6800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 681c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final long drawingTime = getDrawingTime(); 68229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung // Clip to the bounds 68329d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.save(); 68429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft, 68529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung mScrollY + mBottom - mTop); 68629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 687c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka for (int i = leftScreen; i <= rightScreen; i++) { 688c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka drawChild(canvas, getChildAt(i), drawingTime); 689c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 69029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.restore(); 6910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 69686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 69786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 69886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 70686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 70786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 70886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 71086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 71286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 71476fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen return v.requestFocus(direction, previouslyFocusedRect); 715321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 716321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 717321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 72286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 72386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 726321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 72786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 72886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 729321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 730321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 731321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 73786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 73886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 740321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 74186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 74286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 74586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 74686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 75386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 75586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 75686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 76086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 777321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 778321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 779321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 78786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getChildAt(mCurrentPage); 78886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 791321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 793d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 794d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the previous page. 795d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 796d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsPreviousPage(float x, float y) { 797d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing); 798d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 799d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 800d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 801d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the next page. 802d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 803d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsNextPage(float x, float y) { 804d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing)); 805d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 806d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 8146342bbae1a18f3c1862953e33309481703f541cfAdam Cohen acquireVelocityTrackerAndAddMovement(ev); 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 81645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 81745e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 81845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 826321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 827321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 830321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 831321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 832321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 8361ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka if (mActivePointerId != INVALID_POINTER) { 8371ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka determineScrollingStart(ev); 8381ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka break; 8391ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka } 8401ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 8411ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 8421ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // i.e. fall through to the next case (don't break) 8431ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 8441ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // while it's small- this was causing a crash before we checked for INVALID_POINTER) 845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 854c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 855aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 864fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 8655f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 8665f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 8675f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 8685f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 8695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 8705f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 8715f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 872321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 87386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 874321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 875d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { 876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 877d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (hitsPreviousPage(x, y)) { 878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 879d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (hitsNextPage(x, y)) { 880321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 881321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 885321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 886321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 887321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 8881d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown case MotionEvent.ACTION_CANCEL: 889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 8926342bbae1a18f3c1862953e33309481703f541cfAdam Cohen releaseVelocityTracker(); 893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 895321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 8976342bbae1a18f3c1862953e33309481703f541cfAdam Cohen releaseVelocityTracker(); 898321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 899321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 901321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 904321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 906321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 90880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 90980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 910228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung ObjectAnimator anim = (ObjectAnimator) AnimatorInflater. 911228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung loadAnimator(mContext, R.anim.paged_view_click_feedback); 912228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.setTarget(v); 913228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.addListener(new AnimatorListenerAdapter() { 914228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung public void onAnimationRepeat(Animator animation) { 91580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 91680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 91780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 918228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.start(); 91980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 92080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 921f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev) { 922f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen determineScrollingStart(ev, 1.0f); 923f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 924f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 925321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 929f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { 930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 9352698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka if (pointerIndex == -1) { 9362698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka return; 9372698db4df3d2f9aa2a2c16af6760d63ba1ac354eMichael Jurka } 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 943f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final int touchSlop = Math.round(touchSlopScale * mTouchSlop); 944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 945321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 948f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (xMoved || xPaged || yMoved) { 9490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 9526342bbae1a18f3c1862953e33309481703f541cfAdam Cohen mTotalMotionX += Math.abs(mLastMotionX - x); 953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 954c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 9550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 9560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 9570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 960f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen cancelCurrentPageLongPress(); 961f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 962f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 963f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 964f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void cancelCurrentPageLongPress() { 965f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (mAllowLongPress) { 966f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen mAllowLongPress = false; 967f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // Try canceling the long press. It could also have been scheduled 968f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // by a distant descendant, so use the mAllowLongPress flag to block 969f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // everything 970f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final View currentPage = getPageAt(mCurrentPage); 971f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (currentPage != null) { 972f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen currentPage.cancelLongPress(); 973321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 974321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 975321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 976321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 977e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // This curve determines how the effect of scrolling over the limits of the page dimishes 978e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as the user pulls further and further from the bounds 979e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private float overScrollInfluenceCurve(float f) { 980e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 1.0f; 981e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return f * f * f + 1.0f; 982e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 983e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 98468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected void overScroll(float amount) { 985e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int screenSize = getMeasuredWidth(); 986e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 987e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float f = (amount / screenSize); 988e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 989e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (f == 0) return; 990e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 991e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 9927bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen // Clamp this factor, f, to -1 < f < 1 9937bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen if (Math.abs(f) >= 1) { 9947bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen f /= Math.abs(f); 9957bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen } 9967bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen 997e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); 99868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (amount < 0) { 99968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = overScrollAmount; 100068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 100168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = mMaxScrollX + overScrollAmount; 100268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 100368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 100468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 100568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 1006c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka protected float maxOverScroll() { 1007c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not 1008c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // exceed). Used to find out how much extra wallpaper we need for the overscroll effect 1009c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka float f = 1.0f; 1010c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 1011c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka return OVERSCROLL_DAMP_FACTOR * f; 1012c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka } 1013c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka 1014321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 101645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 101745e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onTouchEvent(ev); 101845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 1019b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka acquireVelocityTrackerAndAddMovement(ev); 1020321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1021321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 1024321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 1028321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 1030321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1033321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 1034321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 1035c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1036aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 1037321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 10380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 10390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 10400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1041321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1042321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1043321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 1044321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 1045321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 1046321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 1047321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1048c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung final float deltaX = mLastMotionX + mLastMotionXRemainder - x; 1049321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1050aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX += Math.abs(deltaX); 1051aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1052c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // Only scroll and update mLastMotionX if we have moved some discrete amount. We 1053c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // keep the remainder because we are actually testing if we've moved from the last 1054c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // scrolled position (which is discrete). 1055c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung if (Math.abs(deltaX) >= 1.0f) { 105668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mTouchX += deltaX; 105768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 105868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (!mDeferScrollUpdate) { 1059c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung scrollBy((int) deltaX, 0); 1060785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX); 106168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 106268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 1063321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1064c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionX = x; 1065c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = deltaX - (int) deltaX; 1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1067321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 1068321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1069564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen } else { 1070321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 1071321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1072321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 1075321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 1076321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 1077321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 1078321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1079321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 1080321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1081321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 10829cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung final int deltaX = (int) (x - mDownMotionX); 1083b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE; 10840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 1085aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1086b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); 1087b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen 1088aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // In the case that the page is moved far to one direction and then is flung 1089aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // in the opposite direction, we use a threshold to determine whether we should 1090aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // just return to the starting page, or if we should skip one further. 1091aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen boolean returnToOriginalPage = false; 1092aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen final int pageWidth = getScaledMeasuredWidth(getChildAt(mCurrentPage)); 1093b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && 1094aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen Math.signum(velocityX) != Math.signum(deltaX)) { 1095aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen returnToOriginalPage = true; 1096aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } 1097aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1098b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING && 1099aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen Math.abs(velocityX) > snapVelocity; 1100aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1101aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen int finalPage; 1102aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // We give flings precedence over large moves, which is why we short-circuit our 1103aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // test for a large move if a fling has been registered. That is, a large 1104aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // move to the left and fling to the right will register as a fling to the right. 1105aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen if (((isSignificantMove && deltaX > 0 && !isFling) || 1106aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX > 0)) && mCurrentPage > 0) { 1107aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; 1108aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1109aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } else if (((isSignificantMove && deltaX < 0 && !isFling) || 1110aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX < 0)) && 111186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 1112aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1; 1113aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1114321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1117d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 1118321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 112186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 112286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 112386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1127d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 1128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1130321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 113186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 113286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 113386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1134321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1135321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1136321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 11371d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } else { 11381d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 1139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1142b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1144321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 1146b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 1147b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka snapToDestination(); 1148b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1151b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1152321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1153321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1154321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 1155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 1156321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 1160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1162185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung @Override 1163185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung public boolean onGenericMotionEvent(MotionEvent event) { 1164185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 1165185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung switch (event.getAction()) { 1166185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung case MotionEvent.ACTION_SCROLL: { 1167185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung // Handle mouse (or ext. device) by shifting the page depending on the scroll 1168185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung final float vscroll; 1169185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung final float hscroll; 1170185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { 1171185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung vscroll = 0; 1172185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1173185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } else { 1174185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1175185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); 1176185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1177185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if (hscroll != 0 || vscroll != 0) { 1178185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if (hscroll > 0 || vscroll > 0) { 1179185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung scrollRight(); 1180185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } else { 1181185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung scrollLeft(); 1182185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1183185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung return true; 1184185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1185185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1186185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1187185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1188185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung return super.onGenericMotionEvent(event); 1189185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1190185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung 1191b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { 1192b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker == null) { 1193b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = VelocityTracker.obtain(); 1194b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1195b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.addMovement(ev); 1196b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1197b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1198b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void releaseVelocityTracker() { 1199b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker != null) { 1200b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.recycle(); 1201b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = null; 1202b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1203b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1204b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 1206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 1207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 1208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 1209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 1210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 1211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 1212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 1213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 1215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 1216c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 1218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 1219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 1220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 12221d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown if (mTouchState == TOUCH_STATE_REST) { 12231d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 12241d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 12251d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 12261d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown 1227f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected void onWallpaperTap(MotionEvent ev) {} 1228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 1231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 123286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 123397d85d23b013347bead4e2f5fa430a79ce69431eWinson Chung if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) { 123486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 1235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1238e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getChildIndexForRelativeOffset(int relativeOffset) { 1239e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int childCount = getChildCount(); 12409c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int left; 12419c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int right; 1242e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung for (int i = 0; i < childCount; ++i) { 12439c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen left = getRelativeChildOffset(i); 1244d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka right = (left + getScaledMeasuredWidth(getChildAt(i))); 1245e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung if (left <= relativeOffset && relativeOffset <= right) { 1246e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return i; 1247e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1248e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1249e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return -1; 1250e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1251e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 12521908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung protected void setMinimumWidthOverride(int minimumWidth) { 12531908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung mMinimumWidth = minimumWidth; 12541908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung } 125534b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung protected void resetMinimumWidthOverride() { 125634b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung mMinimumWidth = 0; 125734b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung } 12581908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung 12591908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung protected int getChildWidth(int index) { 126063257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // This functions are called enough times that it actually makes a difference in the 126163257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // profiler -- so just inline the max() here 126263257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int measuredWidth = getChildAt(index).getMeasuredWidth(); 126363257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int minWidth = mMinimumWidth; 126463257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung return (minWidth > measuredWidth) ? minWidth : measuredWidth; 12651908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung } 12661908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung 1267321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getRelativeChildOffset(int index) { 12681908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung return (getMeasuredWidth() - getChildWidth(index)) / 2; 1269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1271557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung protected int getScaledRelativeChildOffset(int index) { 1272557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung return (getMeasuredWidth() - getScaledMeasuredWidth(getChildAt(index))) / 2; 1273557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung } 1274557d6ed50cc6560163440aa134870ef0bb11415bWinson Chung 1275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getChildOffset(int index) { 1276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) 1277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return 0; 1278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int offset = getRelativeChildOffset(0); 1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < index; ++i) { 1281d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka offset += getScaledMeasuredWidth(getChildAt(i)) + mPageSpacing; 1282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return offset; 1284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1286d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected int getScaledMeasuredWidth(View child) { 128763257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // This functions are called enough times that it actually makes a difference in the 128863257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // profiler -- so just inline the max() here 128963257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int measuredWidth = child.getMeasuredWidth(); 129063257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int minWidth = mMinimumWidth; 129163257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth; 129263257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung return (int) (maxWidth * mLayoutScale + 0.5f); 1293d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 1294d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1295d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen int getPageNearestToCenterOfScreen() { 1296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenter = getMeasuredWidth(); 1297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 1298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 1299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 1300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 13010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 1302d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 1303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 1304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 1305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 1306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 1307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 1308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 1309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1311d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen return minDistanceFromScreenCenterIndex; 1312d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen } 1313d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen 1314d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen protected void snapToDestination() { 1315d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); 1316321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1317321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1318e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static class ScrollInterpolator implements Interpolator { 1319e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public ScrollInterpolator() { 1320e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1321e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1322e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public float getInterpolation(float t) { 1323e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen t -= 1.0f; 1324e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return t*t*t*t*t + 1; 1325e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1326e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1327e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1328e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // We want the duration of the page snap animation to be influenced by the distance that 1329e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // the screen has to travel, however, we don't want this duration to be effected in a 1330e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // purely linear fashion. Instead, we use this method to moderate the effect that the distance 1331e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // of travel has on the overall snap duration. 1332e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceInfluenceForSnapDuration(float f) { 1333e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 0.5f; // center the values about 0. 1334e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f *= 0.3f * Math.PI / 2.0f; 1335e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return (float) Math.sin(f); 1336e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1337e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 13380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 1339e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); 1340e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int halfScreenSize = getMeasuredWidth() / 2; 1341e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1342785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); 1343785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): " 1344785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getMeasuredWidth() + ", " + getChildWidth(whichPage)); 1345e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 1346e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int delta = newX - mUnboundedScrollX; 1347e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int duration = 0; 1348e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1349e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (Math.abs(velocity) < MIN_FLING_VELOCITY) { 1350e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // If the velocity is low enough, then treat this more as an automatic page advance 1351e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as opposed to an apparent physical response to flinging 1352e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1353e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return; 1354e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1355e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1356e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // Here we compute a "distance" that will be used in the computation of the overall 1357e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // snap duration. This is a function of the actual distance that needs to be traveled; 1358e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we keep this value close to half screen size in order to reduce the variance in snap 1359e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // duration as a function of the distance the page needs to travel. 136020b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize)); 1361e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distance = halfScreenSize + halfScreenSize * 1362e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen distanceInfluenceForSnapDuration(distanceRatio); 1363e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1364e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.abs(velocity); 1365e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); 1366e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1367e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we want the page's snap velocity to approximately match the velocity at which the 1368e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // user flings, so we scale the duration by a value near to the derivative of the scroll 136920b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka // interpolator at zero, ie. 5. We use 4 to make it a little slower. 137020b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); 1371e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1372e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, delta, duration); 13730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 13750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 13765f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1377321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1378321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 13790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 138086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 1381321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1382785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); 1383785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", " 1384785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getChildWidth(whichPage)); 138586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 138668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen final int sX = mUnboundedScrollX; 1387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 13880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 13890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 13910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 13920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 13930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 13940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 13950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 13960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild == getChildAt(mCurrentPage)) { 13970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 13980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13990142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 14000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 1402321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 1403321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 1404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1405321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 140768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); 140880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1409b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // Load associated pages immediately if someone else is handling the scroll, otherwise defer 1410b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // loading associated pages until the scroll settles 1411b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung if (mDeferScrollUpdate) { 1412b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung loadAssociatedPages(mNextPage); 1413b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } else { 14144e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung mDeferLoadAssociatedPagesUntilScrollCompletes = true; 1415b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 14160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 1417321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 1418321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 1421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 142286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 1423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 142486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 1425321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1428321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 1429321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 143086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 1431321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 143286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 1433321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1434321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 143686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 1437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 1438321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 1439321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 1440321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 1441321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 1442321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (vp == getChildAt(i)) { 1443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 1444321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1445321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1446321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1447321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 1448321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1449321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1450321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 1451321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 1452321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 1454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 1455321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1456321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 14570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 14580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 14590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 14600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 14610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 14620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 14630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 146686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 1467321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 1469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 1470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1471321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 1473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 147486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 1475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1476321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1477321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 1479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 148086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 1481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1483321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 1484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 1485321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 1486321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 1487321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1488321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 1490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 1491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 1493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1494321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 149586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void loadAssociatedPages(int page) { 14960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 14970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 14980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 1499e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int lowerPageBound = getAssociatedLowerPageBound(page); 1500e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int upperPageBound = getAssociatedUpperPageBound(page); 1501785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/" 1502785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + upperPageBound); 15030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 15048245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 15058245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int childCount = layout.getPageChildCount(); 15060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 15070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 15080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPageItems(i); 15090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 15100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 15110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 15120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (childCount > 0) { 15138245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka layout.removeAllViewsOnPage(); 15140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 15150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, true); 151680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 151780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 151880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 151980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 152080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 152180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1522e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedLowerPageBound(int page) { 1523e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.max(0, page - 1); 1524e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1525e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedUpperPageBound(int page) { 1526e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int count = getChildCount(); 1527e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.min(page + 1, count - 1); 1528e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1529e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 15305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void startChoiceMode(int mode, ActionMode.Callback callback) { 1531430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy if (isChoiceMode(CHOICE_MODE_NONE)) { 1532430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mChoiceMode = mode; 1533430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mActionMode = startActionMode(callback); 1534430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy } 15355f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 15372b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public void endChoiceMode() { 15385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (!isChoiceMode(CHOICE_MODE_NONE)) { 15395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 15405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung resetCheckedGrandchildren(); 1541e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka if (mActionMode != null) mActionMode.finish(); 15429f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy mActionMode = null; 15435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 15465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected boolean isChoiceMode(int mode) { 15475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return mChoiceMode == mode; 15485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 15505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected ArrayList<Checkable> getCheckedGrandchildren() { 15515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung ArrayList<Checkable> checked = new ArrayList<Checkable>(); 15525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int childCount = getChildCount(); 15535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < childCount; ++i) { 15548245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 15558245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int grandChildCount = layout.getPageChildCount(); 15565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int j = 0; j < grandChildCount; ++j) { 15578245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final View v = layout.getChildOnPageAt(j); 15589f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 15595f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung checked.add((Checkable) v); 15605f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15615f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15625f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15635f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return checked; 15645f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15655f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 15669f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy /** 15679f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * If in CHOICE_MODE_SINGLE and an item is checked, returns that item. 15689f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * Otherwise, returns null. 15699f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy */ 15709f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy protected Checkable getSingleCheckedGrandchild() { 15716f13342ffd3f968de9ff86b988621cc91d94adffPatrick Dubroy if (mChoiceMode != CHOICE_MODE_MULTIPLE) { 15729f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int childCount = getChildCount(); 15739f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int i = 0; i < childCount; ++i) { 15748245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 15758245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int grandChildCount = layout.getPageChildCount(); 15769f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int j = 0; j < grandChildCount; ++j) { 15778245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final View v = layout.getChildOnPageAt(j); 15789f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 15799f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return (Checkable) v; 15809f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15819f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15829f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15839f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15849f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return null; 15859f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15869f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 15875f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void resetCheckedGrandchildren() { 15885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // loop through children, and set all of their children to _not_ be checked 15895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final ArrayList<Checkable> checked = getCheckedGrandchildren(); 15905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < checked.size(); ++i) { 15915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final Checkable c = checked.get(i); 15925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung c.setChecked(false); 15935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15955f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 159686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 159786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 159886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 159986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 160086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 160186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1602321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 160386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 160486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 160586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 160686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 160786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1608321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPageItems(int page); 160986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1610983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka protected void postInvalidatePageData(final boolean clearViews) { 1611983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka post(new Runnable() { 1612983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka // post the call to avoid a call to requestLayout from a layout pass 1613983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka public void run() { 1614983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka if (clearViews) { 1615983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka removeAllViews(); 1616983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka } 1617983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka invalidatePageData(); 1618983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka } 1619983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka }); 1620983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka } 1621983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka 1622244d74cb353f1260c4d633e719bf84bb3b6e52bcPatrick Dubroy protected void invalidatePageData() { 16235a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung invalidatePageData(-1); 16245a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 16255a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung protected void invalidatePageData(int currentPage) { 1626f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 1627f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 1628f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 1629f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 16300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 16310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 16320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 163386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 16345a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // We must force a measure after we've loaded the pages to update the content width and 16355a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // to determine the full scroll width 16365a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), 16375a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); 16385a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung 16395a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung // Set a new page as the current page if necessary 16405a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung if (currentPage > -1) { 16415a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung setCurrentPage(currentPage); 16425a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 16435a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung 16440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 16450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 16460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 16470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 16480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 16490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 165086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 16510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 16520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka loadAssociatedPages(mCurrentPage); 16530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = true; 1654b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung updateAdjacentPagesAlpha(); 16550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 16560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1657321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1658007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1659007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private ImageView getScrollingIndicator() { 1660007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // We use mHasScrollIndicator to prevent future lookups if there is no sibling indicator 1661007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // found 1662007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mHasScrollIndicator && mScrollIndicator == null) { 1663007c69867d821ea2b271398577a8b3440b3a7046Winson Chung ViewGroup parent = (ViewGroup) getParent(); 1664007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator = (ImageView) (parent.findViewById(R.id.paged_view_indicator)); 1665007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mHasScrollIndicator = mScrollIndicator != null; 1666007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mHasScrollIndicator) { 1667007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.setVisibility(View.VISIBLE); 1668007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1669007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1670007c69867d821ea2b271398577a8b3440b3a7046Winson Chung return mScrollIndicator; 1671007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1672007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1673007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected boolean isScrollingIndicatorEnabled() { 1674649723cfb3d73af16dd02462725700897ca60e38Winson Chung return !LauncherApplication.isScreenLarge(); 1675007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1676007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 16775a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung Runnable hideScrollingIndicatorRunnable = new Runnable() { 16785a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung @Override 16795a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung public void run() { 16805a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung hideScrollingIndicator(false); 16815a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung } 16825a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung }; 16833ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung protected void flashScrollingIndicator() { 16845a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung removeCallbacks(hideScrollingIndicatorRunnable); 16853ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung showScrollingIndicator(); 16865a808358f1a773dab8babae44899ffd4ed1810c1Winson Chung postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration); 16873ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 16883ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 1689007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected void showScrollingIndicator() { 1690007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1691007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1692007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1693007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1694007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1695007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Fade the indicator in 1696007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 169732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.setVisibility(View.VISIBLE); 169832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (mScrollIndicatorAnimator != null) { 169932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.cancel(); 170032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 170132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f); 170232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration); 170332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.start(); 1704007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1705007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1706007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1707007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected void hideScrollingIndicator(boolean immediately) { 1708007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1709007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1710007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1711007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1712007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1713007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Fade the indicator out 1714007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 171532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (mScrollIndicatorAnimator != null) { 171632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.cancel(); 171732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 171832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (immediately) { 171932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.setVisibility(View.GONE); 172032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.setAlpha(0f); 172132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } else { 172232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f); 172332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration); 172432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() { 172532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung private boolean cancelled = false; 172632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung @Override 172732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung public void onAnimationCancel(android.animation.Animator animation) { 172832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung cancelled = true; 172932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 173032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung @Override 173132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung public void onAnimationEnd(Animator animation) { 173232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (!cancelled) { 173332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.setVisibility(View.GONE); 173432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 173532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 173632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung }); 173732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicatorAnimator.start(); 173832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 1739007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1740007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1741007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 174232174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung /** 174332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung * To be overridden by subclasses to determine whether the scroll indicator should stretch to 174432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung * fill its space on the track or not. 174532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung */ 174632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung protected boolean hasElasticScrollIndicator() { 174732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung return false; 174832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 174932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung 1750007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private void updateScrollingIndicator() { 1751007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1752007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1753007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1754007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1755007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1756007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 1757007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1758007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1759007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1760007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private void updateScrollingIndicatorPosition() { 1761649723cfb3d73af16dd02462725700897ca60e38Winson Chung if (!isScrollingIndicatorEnabled()) return; 1762649723cfb3d73af16dd02462725700897ca60e38Winson Chung 176332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int numPages = getChildCount(); 176432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int pageWidth = getMeasuredWidth(); 176532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int maxPageWidth = (numPages * getMeasuredWidth()) + ((numPages - 1) * mPageSpacing); 1766f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight; 176732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorWidth = mScrollIndicator.getMeasuredWidth() - 176832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight(); 176932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung 1770007c69867d821ea2b271398577a8b3440b3a7046Winson Chung float offset = (float) getScrollX() / maxPageWidth; 177132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorSpace = trackWidth / numPages; 1772f5f8cefb215fad98ae6d4487852e8b948aba3619Winson Chung int indicatorPos = (int) (offset * trackWidth) + mScrollIndicatorPaddingLeft; 177332174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (hasElasticScrollIndicator()) { 177432174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) { 177532174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.getLayoutParams().width = indicatorSpace; 177632174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung mScrollIndicator.requestLayout(); 177732174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 177832174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } else { 177932174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2; 178032174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung indicatorPos += indicatorCenterOffset; 178132174c878c77b4e0f13449f4c26ff6b487bc7ca6Winson Chung } 1782007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.setTranslationX(indicatorPos); 17833ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung mScrollIndicator.invalidate(); 17843ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 17853ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 17863ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung public void showScrollIndicatorTrack() { 17873ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung } 17883ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung 17893ac74c55cf8baef29db80e8c67ab4ab033b04417Winson Chung public void hideScrollIndicatorTrack() { 1790007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 17916a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17926a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung /* Accessibility */ 17936a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 17946a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 17956a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onInitializeAccessibilityNodeInfo(info); 17966a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung info.setScrollable(true); 17976a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 17986a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17996a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 18006a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 18016a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onInitializeAccessibilityEvent(event); 18026a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setScrollable(true); 18036a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { 18046a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setFromIndex(mCurrentPage); 18056a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setToIndex(mCurrentPage); 18066a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setItemCount(getChildCount()); 18076a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18086a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18096a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18106a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 18116a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 18126a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung // Do not append text content to scroll events they are fired frequently 18136a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung // and the client has already received another event type with the text. 18146a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) { 18156a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.dispatchPopulateAccessibilityEvent(event); 18166a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18176a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18186a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung onPopulateAccessibilityEvent(event); 18196a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung return false; 18206a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18216a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18226a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 18236a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 18246a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onPopulateAccessibilityEvent(event); 18256a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18266a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { 18276a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.getText().add(getCurrentPageDescription()); 18286a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18296a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 18306a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 18316a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung protected String getCurrentPageDescription() { 18326a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; 18336a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung return String.format(mContext.getString(R.string.default_scroll_format), 18346a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung page + 1, getChildCount()); 18356a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 1836321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1837