PagedView.java revision 6a0f57dfafced837a2a282d8feec28d5418be3b9
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; 23321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context; 249c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray; 25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas; 26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect; 27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel; 28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable; 29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet; 30785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chungimport android.util.Log; 315f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode; 32185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.InputDevice; 33185d71647c8859cae7a375773b31c03f2f22ade1Winson Chungimport android.view.KeyEvent; 34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 38321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 39321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 406a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityEvent; 416a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport android.view.accessibility.AccessibilityNodeInfo; 42e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator; 435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable; 44007c69867d821ea2b271398577a8b3440b3a7046Winson Chungimport android.widget.ImageView; 45321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 470499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R; 4880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 496a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chungimport java.util.ArrayList; 506a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 51321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/** 52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a 530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages" 54321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 55321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup { 56321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final String TAG = "PagedView"; 57785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung private static final boolean DEBUG = false; 580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final int INVALID_PAGE = -1; 59321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 6086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // the min drag distance for a fling to register, to prevent random page shifts 619cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_FLING = 25; 629cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung // The min drag distance to trigger a page shift (regardless of velocity) 639cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_MOVE = 200; 64321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 65e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int PAGE_SNAP_ANIMATION_DURATION = 550; 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 68b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung private static final float OVERSCROLL_DAMP_FACTOR = 0.14f; 69e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MINIMUM_SNAP_VELOCITY = 2200; 70e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MIN_FLING_VELOCITY = 250; 71b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; 7268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 78321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 8368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mMaxScrollX; 840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 85321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 887426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionX; 89c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung protected float mLastMotionXRemainder; 907426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionY; 91aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen protected float mTotalMotionX; 92f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private int mLastScreenCenter = -1; 93321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 98e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; 99321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1047426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected boolean mAllowLongPress = true; 105321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1067426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected int mTouchSlop; 107321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 1091908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung private int mMinimumWidth; 1109c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageSpacing; 1119c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingTop; 1129c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingBottom; 1139c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingLeft; 1149c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingRight; 115df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutWidthGap; 116df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutHeightGap; 11787b1490c5a100619648b251cb2be05c457bede08Michael Jurka protected int mCellCountX = 0; 11887b1490c5a100619648b251cb2be05c457bede08Michael Jurka protected int mCellCountY = 0; 1197da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung protected boolean mCenterPagesVertically; 12068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected boolean mAllowOverScroll = true; 12168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mUnboundedScrollX; 122321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1238c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // parameter that adjusts the layout to be optimized for pages with that scale factor 124d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected float mLayoutScale = 1.0f; 125d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1265f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected static final int INVALID_POINTER = -1; 127321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1285f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected int mActivePointerId = INVALID_POINTER; 129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 13086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 131321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 13286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private ArrayList<Boolean> mDirtyPageContent; 13386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private boolean mDirtyPageAlpha; 134321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1355f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // choice modes 1365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_NONE = 0; 1375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_SINGLE = 1; 1385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // Multiple selection mode is not supported by all Launcher actions atm 1395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_MULTIPLE = 2; 1409f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 141e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka protected int mChoiceMode; 1425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private ActionMode mActionMode; 1435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 1440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1581262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected boolean mIsPageMoving = false; 1591262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 160f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung // All syncs and layout passes are deferred until data is ready. 161f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected boolean mIsDataReady = false; 162f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 163007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Scrolling indicator 164007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private ImageView mScrollIndicator; 165007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private boolean mHasScrollIndicator = true; 166007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private static final int sScrollIndicatorFadeInDuration = 150; 167007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private static final int sScrollIndicatorFastFadeOutDuration = 50; 168007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private static final int sScrollIndicatorFadeOutDuration = 650; 169007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 170b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // If set, will defer loading associated pages until the scrolling settles 1714e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung private boolean mDeferLoadAssociatedPagesUntilScrollCompletes; 172b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 17386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 17486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 176321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 177321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 178321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 179321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 183321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 185321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 186321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 1875f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1899c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 1909c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView, defStyle, 0); 1919c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0); 1929c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingTop = a.getDimensionPixelSize( 1931908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingTop, 0); 1949c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingBottom = a.getDimensionPixelSize( 1951908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingBottom, 0); 1969c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingLeft = a.getDimensionPixelSize( 1971908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingLeft, 0); 1989c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingRight = a.getDimensionPixelSize( 1991908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung R.styleable.PagedView_pageLayoutPaddingRight, 0); 200df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutWidthGap = a.getDimensionPixelSize( 201df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutWidthGap, -1); 202df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutHeightGap = a.getDimensionPixelSize( 203df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutHeightGap, -1); 2049c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen a.recycle(); 2059c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen 206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 2070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 2130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 21486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 21586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 216e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mScroller = new Scroller(getContext(), new ScrollInterpolator()); 21786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 2187da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung mCenterPagesVertically = true; 219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 224321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 22686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 22786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 22886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 22986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 234f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung * Called by subclasses to mark that data is ready, and that we can begin loading and laying 235f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung * out pages. 236f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung */ 237f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected void setDataIsReady() { 238f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung mIsDataReady = true; 239f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 240f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected boolean isDataReady() { 241f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return mIsDataReady; 242f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 243f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 244f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung /** 24586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 24786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 248321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 24986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 25086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 25386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 25786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 258321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int getScrollWidth() { 262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getWidth(); 263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 266bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * Updates the scroll of the current page immediately to its final scroll position. We use this 267bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of 268bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * the previous tab page. 269bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung */ 270bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung protected void updateCurrentPageScroll() { 271bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 272bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung scrollTo(newX, 0); 273bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung mScroller.setFinalX(newX); 274bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung } 275bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung 276bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung /** 27786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 27986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 28072e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (!mScroller.isFinished()) { 28172e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy mScroller.abortAnimation(); 28272e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 283d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // don't introduce any checks like mCurrentPage == currentPage here-- if we change the 284d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // the default 285d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (getChildCount() == 0) { 28672e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy return; 28772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 28986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 290bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung updateCurrentPageScroll(); 29186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 292a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung invalidate(); 293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 29686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 29786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 301ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageBeginMoving() { 3021262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = true; 3031262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageBeginMoving(); 3041262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3051262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 306ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageEndMoving() { 3071262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageEndMoving(); 3081262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = false; 3091262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 3101262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 31126976d9b53161f80faf822d482ac771b8621cf31Adam Cohen protected boolean isPageMoving() { 31226976d9b53161f80faf822d482ac771b8621cf31Adam Cohen return mIsPageMoving; 31326976d9b53161f80faf822d482ac771b8621cf31Adam Cohen } 31426976d9b53161f80faf822d482ac771b8621cf31Adam Cohen 3150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3161262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageBeginMoving() { 317007c69867d821ea2b271398577a8b3440b3a7046Winson Chung showScrollingIndicator(); 3180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 3211262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageEndMoving() { 322007c69867d821ea2b271398577a8b3440b3a7046Winson Chung hideScrollingIndicator(false); 3230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 32686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 327321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 328321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 329321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 330321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 331321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 332321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 33386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 334321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 33586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 336321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 338321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 34068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen public void scrollBy(int x, int y) { 34168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen scrollTo(mUnboundedScrollX + x, mScrollY + y); 34268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 34368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 34468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen @Override 3450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 34668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mUnboundedScrollX = x; 34768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 34868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (x < 0) { 34968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(0, y); 35068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 35168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x); 35268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 35368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else if (x > mMaxScrollX) { 35468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(mMaxScrollX, y); 35568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 35668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x - mMaxScrollX); 35768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 35868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 35968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(x, y); 36068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 36168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 3620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 3630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 3640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 3670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 368321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 3695f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 370321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 3720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 37386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 3745f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 37586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 37686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 3770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 378b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 379b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // Load the associated pages if necessary 3804e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung if (mDeferLoadAssociatedPagesUntilScrollCompletes) { 381b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung loadAssociatedPages(mCurrentPage); 3824e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung mDeferLoadAssociatedPagesUntilScrollCompletes = false; 383b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 384b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung 38573aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // We don't want to trigger a page end moving unless the page has settled 38673aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // and the user has stopped scrolling 38773aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen if (mTouchState == TOUCH_STATE_REST) { 38873aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen pageEndMoving(); 38973aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen } 3900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 391321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 3920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 3930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 3960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 3970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 402f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 403f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung super.onMeasure(widthMeasureSpec, heightMeasureSpec); 404f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 405f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 406f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 407321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 408321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 409321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 411321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 412321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4136b879f0a5885274a85333531e091283405d490ccAdam Lesinski /* Allow the height to be set as WRAP_CONTENT. This allows the particular case 4146b879f0a5885274a85333531e091283405d490ccAdam Lesinski * of the All apps view on XLarge displays to not take up more space then it needs. Width 4156b879f0a5885274a85333531e091283405d490ccAdam Lesinski * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect 4166b879f0a5885274a85333531e091283405d490ccAdam Lesinski * each page to have the same width. 4176b879f0a5885274a85333531e091283405d490ccAdam Lesinski */ 418321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 4196b879f0a5885274a85333531e091283405d490ccAdam Lesinski int heightSize = MeasureSpec.getSize(heightMeasureSpec); 4206b879f0a5885274a85333531e091283405d490ccAdam Lesinski int maxChildHeight = 0; 4216b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4226b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 42436fcb74425bebc1d88c9e3102484ec902b68f202Michael Jurka 425321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 4265f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // unless they were set to WRAP_CONTENT 427785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize); 428321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 429321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 4305f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // disallowing padding in paged view (just pass 0) 4315f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final View child = getChildAt(i); 4325f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 4335f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4345f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childWidthMode; 4355f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.width == LayoutParams.WRAP_CONTENT) { 4365f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.AT_MOST; 4375f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.EXACTLY; 4395f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4405f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4415f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childHeightMode; 4425f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.height == LayoutParams.WRAP_CONTENT) { 4435f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.AT_MOST; 4445f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4455f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.EXACTLY; 4465f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4475f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4485f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childWidthMeasureSpec = 4495f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka MeasureSpec.makeMeasureSpec(widthSize, childWidthMode); 4505f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childHeightMeasureSpec = 4516b879f0a5885274a85333531e091283405d490ccAdam Lesinski MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode); 4525f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4535f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4546b879f0a5885274a85333531e091283405d490ccAdam Lesinski maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); 455785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "\tmeasure-child" + i + ": " + child.getMeasuredWidth() + ", " 456785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + child.getMeasuredHeight()); 4576b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 4586b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4596b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (heightMode == MeasureSpec.AT_MOST) { 4606b879f0a5885274a85333531e091283405d490ccAdam Lesinski heightSize = maxChildHeight + verticalPadding; 461321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 462faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen if (childCount > 0) { 463faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1); 464faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } else { 465faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = 0; 466faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } 467321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setMeasuredDimension(widthSize, heightSize); 469cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka } 470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4718c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) { 472af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 473af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int delta = newX - mScrollX; 474af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 4758c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka final int pageCount = getChildCount(); 4768c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka for (int i = 0; i < pageCount; i++) { 4778c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka View page = (View) getChildAt(i); 4788c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka page.setX(page.getX() + delta); 479af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 480af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka setCurrentPage(newCurrentPage); 481af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 482af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 4838c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a 4848c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and 485d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // tightens the layout accordingly 486d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka public void setLayoutScale(float childrenScale) { 487d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mLayoutScale = childrenScale; 488d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 489d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Now we need to do a re-layout, but preserving absolute X and Y coordinates 490d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childCount = getChildCount(); 491d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenX[] = new float[childCount]; 492d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenY[] = new float[childCount]; 493d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 494d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 495d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenX[i] = child.getX(); 496d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenY[i] = child.getY(); 497d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 498b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung // Trigger a full re-layout (never just call onLayout directly!) 499b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY); 500b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY); 501b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung requestLayout(); 502b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung measure(widthSpec, heightSpec); 503b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung layout(mLeft, mTop, mRight, mBottom); 504d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 505d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 506d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setX(childrenX[i]); 507d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setY(childrenY[i]); 508d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 509b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung 510d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Also, the page offset has changed (since the pages are now smaller); 511d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // update the page offset, but again preserving absolute X and Y coordinates 5128c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka scrollToNewPageWithoutMovingPages(mCurrentPage); 513d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 514d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 515cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka @Override 51628750fba6a2d141eb9a1e566718c17236030b815Michael Jurka protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 517f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 518f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 519f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 520f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 521785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "PagedView.onLayout()"); 5225f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 523321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(false); 524cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 525cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka scrollTo(newX, 0); 526cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka mScroller.setFinalX(newX); 527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(true); 528321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mFirstLayout = false; 529321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 530321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 5316b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 535785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", " 536785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getChildWidth(0)); 537e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung childLeft = getRelativeChildOffset(0); 538321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final View child = getChildAt(i); 542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 543d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int childWidth = getScaledMeasuredWidth(child); 5446b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int childHeight = child.getMeasuredHeight(); 5456b879f0a5885274a85333531e091283405d490ccAdam Lesinski int childTop = mPaddingTop; 5466b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (mCenterPagesVertically) { 5476b879f0a5885274a85333531e091283405d490ccAdam Lesinski childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2; 5486b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 549d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 550785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop); 5516b879f0a5885274a85333531e091283405d490ccAdam Lesinski child.layout(childLeft, childTop, 552d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childLeft + child.getMeasuredWidth(), childTop + childHeight); 5539c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen childLeft += childWidth + mPageSpacing; 554321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 555321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 556d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 557d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mFirstLayout = false; 558d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 559321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 560321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 56103929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung protected void forceUpdateAdjacentPagesAlpha() { 56203929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung mDirtyPageAlpha = true; 56303929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung updateAdjacentPagesAlpha(); 56403929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung } 56503929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung 566e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void updateAdjacentPagesAlpha() { 5670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 5680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { 56963257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung int screenWidth = getMeasuredWidth(); 57063257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung int halfScreenSize = screenWidth / 2; 571e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int screenCenter = mScrollX + halfScreenSize; 5720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = getChildCount(); 5730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < childCount; ++i) { 5740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 575d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 5760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int halfChildWidth = (childWidth / 2); 5770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childCenter = getChildOffset(i) + halfChildWidth; 578e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 579b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // On the first layout, we may not have a width nor a proper offset, so for now 580b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // we should just assume full page width (and calculate the offset according to 581b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // that). 582b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung if (childWidth <= 0) { 58363257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung childWidth = screenWidth; 584b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childCenter = (i * childWidth) + (childWidth / 2); 585b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung } 586b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 587e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int d = halfChildWidth; 588e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int distanceFromScreenCenter = childCenter - screenCenter; 589e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (distanceFromScreenCenter > 0) { 590e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i > 0) { 591d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i - 1)) / 2; 592f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } else { 593f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung continue; 594e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 5950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 596e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i < childCount - 1) { 597d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i + 1)) / 2; 598f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } else { 599f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung continue; 600e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 6010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 6029c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen d += mPageSpacing; 603e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 604b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // Preventing potential divide-by-zero 605b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung d = Math.max(1, d); 606b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 607e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d; 608e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); 609e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float alpha = 1.0f - dimAlpha; 610e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 611f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (alpha < ALPHA_QUANTIZE_LEVEL) { 612f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 0.0f; 613f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) { 614f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 1.0f; 615f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 616f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 617a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // Due to the way we're setting alpha on our children in PagedViewCellLayout, 618a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // this optimization causes alpha to not be properly updated sometimes (repro 619a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // case: in xlarge mode, swipe to second page in All Apps, then click on "My 620a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // Apps" tab. the page will have alpha 0 until you swipe it). Removing 621a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // optimization fixes the issue, but we should fix this in a better manner 622cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka //if (Float.compare(alpha, layout.getAlpha()) != 0) { 6230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.setAlpha(alpha); 624cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka //} 625affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 6260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = false; 627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 629e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 6300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 631f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen protected void screenScrolled(int screenCenter) { 632007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicator(); 633f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 634f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 635e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung @Override 636e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void dispatchDraw(Canvas canvas) { 637f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int halfScreenSize = getMeasuredWidth() / 2; 638f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int screenCenter = mScrollX + halfScreenSize; 639f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 640f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (screenCenter != mLastScreenCenter) { 641f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen screenScrolled(screenCenter); 642f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen updateAdjacentPagesAlpha(); 643f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mLastScreenCenter = screenCenter; 644f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 6450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 646e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung // Find out which screens are visible; as an optimization we only call draw on them 6470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // As an optimization, this code assumes that all pages have the same width as the 0th 6480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page. 6490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 650c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka if (pageCount > 0) { 651d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int pageWidth = getScaledMeasuredWidth(getChildAt(0)); 652c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int screenWidth = getMeasuredWidth(); 653c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int x = getRelativeChildOffset(0) + pageWidth; 654c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int leftScreen = 0; 655c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int rightScreen = 0; 656c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka while (x <= mScrollX) { 657c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka leftScreen++; 658c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(leftScreen)) + mPageSpacing; 659c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 660c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = leftScreen; 661c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung while (x < mScrollX + screenWidth && rightScreen < pageCount) { 662c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen++; 663c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung if (rightScreen < pageCount) { 664c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(rightScreen)) + mPageSpacing; 665c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung } 666c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 667c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = Math.min(getChildCount() - 1, rightScreen); 6680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 669c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final long drawingTime = getDrawingTime(); 67029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung // Clip to the bounds 67129d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.save(); 67229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft, 67329d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung mScrollY + mBottom - mTop); 67429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 675c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka for (int i = leftScreen; i <= rightScreen; i++) { 676c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka drawChild(canvas, getChildAt(i), drawingTime); 677c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 67829d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.restore(); 6790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 680321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 683321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 68486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 68586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 68686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 688321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 690321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 69486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 69586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 69686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 69886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 70086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 70276fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen return v.requestFocus(direction, previouslyFocusedRect); 703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 71086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 71186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 712321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 714321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 71586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 71686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 717321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 72586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 72686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 727321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 728321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 72986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 73086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 731321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 73386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 73486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 740321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 74186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 74386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 74486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 74886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 753321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 77586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getChildAt(mCurrentPage); 77686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 777321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 778321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 779321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 781d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 782d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the previous page. 783d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 784d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsPreviousPage(float x, float y) { 785d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing); 786d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 787d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 788d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 789d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the next page. 790d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 791d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsNextPage(float x, float y) { 792d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing)); 793d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 794d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 8026342bbae1a18f3c1862953e33309481703f541cfAdam Cohen acquireVelocityTrackerAndAddMovement(ev); 803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 80445e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 80545e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 80645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 8241ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka if (mActivePointerId != INVALID_POINTER) { 8251ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka determineScrollingStart(ev); 8261ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka break; 8271ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka } 8281ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 8291ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 8301ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // i.e. fall through to the next case (don't break) 8311ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 8321ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // while it's small- this was causing a crash before we checked for INVALID_POINTER) 833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 836321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 841321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 842c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 843aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 844321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 852fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 8535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 8545f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 8555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 8565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 8575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 8585f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 8595f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 86186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 863d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { 864321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 865d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (hitsPreviousPage(x, y)) { 866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 867d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (hitsNextPage(x, y)) { 868321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 869321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 872321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 873321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 874321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 875321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 8761d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown case MotionEvent.ACTION_CANCEL: 877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 8806342bbae1a18f3c1862953e33309481703f541cfAdam Cohen releaseVelocityTracker(); 881321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 8856342bbae1a18f3c1862953e33309481703f541cfAdam Cohen releaseVelocityTracker(); 886321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 887321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 895321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 89680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 89780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 898228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung ObjectAnimator anim = (ObjectAnimator) AnimatorInflater. 899228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung loadAnimator(mContext, R.anim.paged_view_click_feedback); 900228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.setTarget(v); 901228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.addListener(new AnimatorListenerAdapter() { 902228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung public void onAnimationRepeat(Animator animation) { 90380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 90480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 90580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 906228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.start(); 90780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 90880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 909f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev) { 910f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen determineScrollingStart(ev, 1.0f); 911f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 912f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 913321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 917f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { 918321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 919321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 920321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 922321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 924321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 925321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 928f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final int touchSlop = Math.round(touchSlopScale * mTouchSlop); 929321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 933f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (xMoved || xPaged || yMoved) { 9340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 935321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 936321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 9376342bbae1a18f3c1862953e33309481703f541cfAdam Cohen mTotalMotionX += Math.abs(mLastMotionX - x); 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 939c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 9400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 9410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 9420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 945f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen cancelCurrentPageLongPress(); 946f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 947f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 948f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 949f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void cancelCurrentPageLongPress() { 950f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (mAllowLongPress) { 951f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen mAllowLongPress = false; 952f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // Try canceling the long press. It could also have been scheduled 953f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // by a distant descendant, so use the mAllowLongPress flag to block 954f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // everything 955f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final View currentPage = getPageAt(mCurrentPage); 956f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (currentPage != null) { 957f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen currentPage.cancelLongPress(); 958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 962e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // This curve determines how the effect of scrolling over the limits of the page dimishes 963e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as the user pulls further and further from the bounds 964e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private float overScrollInfluenceCurve(float f) { 965e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 1.0f; 966e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return f * f * f + 1.0f; 967e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 968e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 96968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected void overScroll(float amount) { 970e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int screenSize = getMeasuredWidth(); 971e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 972e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float f = (amount / screenSize); 973e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 974e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (f == 0) return; 975e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 976e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 9777bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen // Clamp this factor, f, to -1 < f < 1 9787bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen if (Math.abs(f) >= 1) { 9797bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen f /= Math.abs(f); 9807bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen } 9817bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen 982e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); 98368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (amount < 0) { 98468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = overScrollAmount; 98568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 98668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = mMaxScrollX + overScrollAmount; 98768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 98868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 98968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 99068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 991c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka protected float maxOverScroll() { 992c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not 993c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // exceed). Used to find out how much extra wallpaper we need for the overscroll effect 994c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka float f = 1.0f; 995c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 996c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka return OVERSCROLL_DAMP_FACTOR * f; 997c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka } 998c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka 999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1000321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 100145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 100245e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onTouchEvent(ev); 100345e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 1004b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka acquireVelocityTrackerAndAddMovement(ev); 1005321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1006321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1008321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 1010321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 1011321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 1012321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 1013321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1014321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1017321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1018321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 1019321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 1020c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1021aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 10230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 10240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 10250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1028321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 1030321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1033c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung final float deltaX = mLastMotionX + mLastMotionXRemainder - x; 1034321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1035aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX += Math.abs(deltaX); 1036aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1037c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // Only scroll and update mLastMotionX if we have moved some discrete amount. We 1038c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // keep the remainder because we are actually testing if we've moved from the last 1039c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // scrolled position (which is discrete). 1040c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung if (Math.abs(deltaX) >= 1.0f) { 104168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mTouchX += deltaX; 104268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 104368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (!mDeferScrollUpdate) { 1044c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung scrollBy((int) deltaX, 0); 1045785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX); 104668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 104768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 1048321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1049c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionX = x; 1050c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = deltaX - (int) deltaX; 1051321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1052321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 1053321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1054564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen } else { 1055321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 1056321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1058321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1059321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 1060321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 1061321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 1062321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 1063321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 1064321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 1065321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 10679cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung final int deltaX = (int) (x - mDownMotionX); 1068b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE; 10690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 1070aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1071b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); 1072b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen 1073aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // In the case that the page is moved far to one direction and then is flung 1074aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // in the opposite direction, we use a threshold to determine whether we should 1075aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // just return to the starting page, or if we should skip one further. 1076aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen boolean returnToOriginalPage = false; 1077aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen final int pageWidth = getScaledMeasuredWidth(getChildAt(mCurrentPage)); 1078b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && 1079aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen Math.signum(velocityX) != Math.signum(deltaX)) { 1080aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen returnToOriginalPage = true; 1081aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } 1082aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1083b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING && 1084aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen Math.abs(velocityX) > snapVelocity; 1085aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1086aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen int finalPage; 1087aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // We give flings precedence over large moves, which is why we short-circuit our 1088aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // test for a large move if a fling has been registered. That is, a large 1089aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // move to the left and fling to the right will register as a fling to the right. 1090aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen if (((isSignificantMove && deltaX > 0 && !isFling) || 1091aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX > 0)) && mCurrentPage > 0) { 1092aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; 1093aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1094aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } else if (((isSignificantMove && deltaX < 0 && !isFling) || 1095aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX < 0)) && 109686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 1097aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1; 1098aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1099321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1102d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 1103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1104321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1105321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 110686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 110786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 110886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1110321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1111321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1112d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 1113321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1114321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 111686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 111786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 111886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1121321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 11221d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } else { 11231d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1127b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1130321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 1131b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 1132b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka snapToDestination(); 1133b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1134321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1135321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1136b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1137321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1138321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 1140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 1141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1142321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1144321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1147185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung @Override 1148185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung public boolean onGenericMotionEvent(MotionEvent event) { 1149185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 1150185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung switch (event.getAction()) { 1151185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung case MotionEvent.ACTION_SCROLL: { 1152185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung // Handle mouse (or ext. device) by shifting the page depending on the scroll 1153185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung final float vscroll; 1154185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung final float hscroll; 1155185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { 1156185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung vscroll = 0; 1157185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1158185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } else { 1159185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1160185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); 1161185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1162185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if (hscroll != 0 || vscroll != 0) { 1163185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung if (hscroll > 0 || vscroll > 0) { 1164185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung scrollRight(); 1165185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } else { 1166185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung scrollLeft(); 1167185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1168185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung return true; 1169185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1170185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1171185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1172185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1173185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung return super.onGenericMotionEvent(event); 1174185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung } 1175185d71647c8859cae7a375773b31c03f2f22ade1Winson Chung 1176b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { 1177b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker == null) { 1178b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = VelocityTracker.obtain(); 1179b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1180b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.addMovement(ev); 1181b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1182b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1183b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void releaseVelocityTracker() { 1184b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker != null) { 1185b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.recycle(); 1186b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = null; 1187b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1188b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1189b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1190321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 1191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 1192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 1193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 1194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 1195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 1196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 1197321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 1198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1199321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 1200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 1201c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 1203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 1204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 1205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 12071d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown if (mTouchState == TOUCH_STATE_REST) { 12081d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 12091d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 12101d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 12111d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown 1212f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung protected void onWallpaperTap(MotionEvent ev) {} 1213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 1216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 121786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 121897d85d23b013347bead4e2f5fa430a79ce69431eWinson Chung if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) { 121986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 1220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1223e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getChildIndexForRelativeOffset(int relativeOffset) { 1224e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int childCount = getChildCount(); 12259c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int left; 12269c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int right; 1227e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung for (int i = 0; i < childCount; ++i) { 12289c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen left = getRelativeChildOffset(i); 1229d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka right = (left + getScaledMeasuredWidth(getChildAt(i))); 1230e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung if (left <= relativeOffset && relativeOffset <= right) { 1231e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return i; 1232e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1233e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1234e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return -1; 1235e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1236e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 12371908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung protected void setMinimumWidthOverride(int minimumWidth) { 12381908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung mMinimumWidth = minimumWidth; 12391908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung } 124034b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung protected void resetMinimumWidthOverride() { 124134b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung mMinimumWidth = 0; 124234b23d5c8ce33450bd7640559bffcd77c5b6fb4dWinson Chung } 12431908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung 12441908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung protected int getChildWidth(int index) { 124563257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // This functions are called enough times that it actually makes a difference in the 124663257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // profiler -- so just inline the max() here 124763257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int measuredWidth = getChildAt(index).getMeasuredWidth(); 124863257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int minWidth = mMinimumWidth; 124963257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung return (minWidth > measuredWidth) ? minWidth : measuredWidth; 12501908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung } 12511908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung 1252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getRelativeChildOffset(int index) { 12531908d07151e0d3f82899f368874e41ed1e49ba16Winson Chung return (getMeasuredWidth() - getChildWidth(index)) / 2; 1254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getChildOffset(int index) { 1257321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) 1258321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return 0; 1259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int offset = getRelativeChildOffset(0); 1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < index; ++i) { 1262d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka offset += getScaledMeasuredWidth(getChildAt(i)) + mPageSpacing; 1263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return offset; 1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1267d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected int getScaledMeasuredWidth(View child) { 126863257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // This functions are called enough times that it actually makes a difference in the 126963257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung // profiler -- so just inline the max() here 127063257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int measuredWidth = child.getMeasuredWidth(); 127163257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int minWidth = mMinimumWidth; 127263257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth; 127363257c110a4ee54d5e8872c471cce254cf613c7aWinson Chung return (int) (maxWidth * mLayoutScale + 0.5f); 1274d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 1275d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1276d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen int getPageNearestToCenterOfScreen() { 1277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenter = getMeasuredWidth(); 1278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 1281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 12820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 1283d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 1284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 1286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 1288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 1289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 1290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1292d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen return minDistanceFromScreenCenterIndex; 1293d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen } 1294d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen 1295d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen protected void snapToDestination() { 1296d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); 1297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1299e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static class ScrollInterpolator implements Interpolator { 1300e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public ScrollInterpolator() { 1301e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1302e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1303e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public float getInterpolation(float t) { 1304e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen t -= 1.0f; 1305e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return t*t*t*t*t + 1; 1306e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1307e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1308e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1309e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // We want the duration of the page snap animation to be influenced by the distance that 1310e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // the screen has to travel, however, we don't want this duration to be effected in a 1311e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // purely linear fashion. Instead, we use this method to moderate the effect that the distance 1312e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // of travel has on the overall snap duration. 1313e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceInfluenceForSnapDuration(float f) { 1314e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 0.5f; // center the values about 0. 1315e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f *= 0.3f * Math.PI / 2.0f; 1316e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return (float) Math.sin(f); 1317e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1318e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 13190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 1320e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); 1321e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int halfScreenSize = getMeasuredWidth() / 2; 1322e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1323785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); 1324785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): " 1325785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getMeasuredWidth() + ", " + getChildWidth(whichPage)); 1326e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 1327e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int delta = newX - mUnboundedScrollX; 1328e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int duration = 0; 1329e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1330e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (Math.abs(velocity) < MIN_FLING_VELOCITY) { 1331e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // If the velocity is low enough, then treat this more as an automatic page advance 1332e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as opposed to an apparent physical response to flinging 1333e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1334e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return; 1335e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1336e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1337e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // Here we compute a "distance" that will be used in the computation of the overall 1338e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // snap duration. This is a function of the actual distance that needs to be traveled; 1339e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we keep this value close to half screen size in order to reduce the variance in snap 1340e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // duration as a function of the distance the page needs to travel. 134120b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize)); 1342e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distance = halfScreenSize + halfScreenSize * 1343e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen distanceInfluenceForSnapDuration(distanceRatio); 1344e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1345e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.abs(velocity); 1346e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); 1347e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1348e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we want the page's snap velocity to approximately match the velocity at which the 1349e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // user flings, so we scale the duration by a value near to the derivative of the scroll 135020b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka // interpolator at zero, ie. 5. We use 4 to make it a little slower. 135120b7ca91b277e9668b6d4b4b3c9116b6778d22a5Michael Jurka duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); 1352e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1353e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, delta, duration); 13540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 13560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 13575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1358321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1359321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 13600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 136186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 1362321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1363785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); 1364785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", " 1365785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + getChildWidth(whichPage)); 136686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 136768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen final int sX = mUnboundedScrollX; 1368321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 13690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 13700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 13720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 13730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 13740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 13750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 13760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 13770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild == getChildAt(mCurrentPage)) { 13780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 13790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 13810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1382321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 1383321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 1384321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 1385321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1386321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 138868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); 138980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1390b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // Load associated pages immediately if someone else is handling the scroll, otherwise defer 1391b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung // loading associated pages until the scroll settles 1392b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung if (mDeferScrollUpdate) { 1393b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung loadAssociatedPages(mNextPage); 1394b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } else { 13954e076545e4ccdbd3c045a3fa33869a2b7519a0ccWinson Chung mDeferLoadAssociatedPagesUntilScrollCompletes = true; 1396b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 13970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 1398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 1399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 1402321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 140386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 1404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 140586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 1406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1407321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1408321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1409321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 1410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 141186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 1412321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 141386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 1414321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1415321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1416321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 141786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 1418321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 1419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 1420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 1421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 1422321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 1423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (vp == getChildAt(i)) { 1424321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 1425321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1428321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 1429321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1430321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1431321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 1432321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 1433321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1434321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 1435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 1436321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 14380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 14390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 14400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 14410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 14420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 14430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 14440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1446321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 144786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 1448321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1449321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 1450321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 1451321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1452321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 1454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 145586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 1456321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1457321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1458321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1459321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 1460321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 146186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 1462321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1463321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1464321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 1465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 1466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 1467321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 1468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 1471321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 1472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 1474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 147686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void loadAssociatedPages(int page) { 14770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 14780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 14790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 1480e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int lowerPageBound = getAssociatedLowerPageBound(page); 1481e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int upperPageBound = getAssociatedUpperPageBound(page); 1482785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/" 1483785d2eb2b8d7072c8124300dd9168ff51a91cf38Winson Chung + upperPageBound); 14840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 14858245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 14868245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int childCount = layout.getPageChildCount(); 14870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 14880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 14890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPageItems(i); 14900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 14910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 14930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (childCount > 0) { 14948245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka layout.removeAllViewsOnPage(); 14950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 14960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, true); 149780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 149880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 149980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 150080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 150180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 150280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1503e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedLowerPageBound(int page) { 1504e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.max(0, page - 1); 1505e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1506e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedUpperPageBound(int page) { 1507e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int count = getChildCount(); 1508e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.min(page + 1, count - 1); 1509e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1510e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 15115f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void startChoiceMode(int mode, ActionMode.Callback callback) { 1512430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy if (isChoiceMode(CHOICE_MODE_NONE)) { 1513430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mChoiceMode = mode; 1514430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mActionMode = startActionMode(callback); 1515430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy } 15165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 15182b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public void endChoiceMode() { 15195f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (!isChoiceMode(CHOICE_MODE_NONE)) { 15205f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 15215f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung resetCheckedGrandchildren(); 1522e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka if (mActionMode != null) mActionMode.finish(); 15239f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy mActionMode = null; 15245f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15255f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15265f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 15275f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected boolean isChoiceMode(int mode) { 15285f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return mChoiceMode == mode; 15295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 15315f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected ArrayList<Checkable> getCheckedGrandchildren() { 15325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung ArrayList<Checkable> checked = new ArrayList<Checkable>(); 15335f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int childCount = getChildCount(); 15345f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < childCount; ++i) { 15358245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 15368245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int grandChildCount = layout.getPageChildCount(); 15375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int j = 0; j < grandChildCount; ++j) { 15388245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final View v = layout.getChildOnPageAt(j); 15399f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 15405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung checked.add((Checkable) v); 15415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return checked; 15455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 15479f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy /** 15489f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * If in CHOICE_MODE_SINGLE and an item is checked, returns that item. 15499f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * Otherwise, returns null. 15509f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy */ 15519f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy protected Checkable getSingleCheckedGrandchild() { 15526f13342ffd3f968de9ff86b988621cc91d94adffPatrick Dubroy if (mChoiceMode != CHOICE_MODE_MULTIPLE) { 15539f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int childCount = getChildCount(); 15549f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int i = 0; i < childCount; ++i) { 15558245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 15568245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int grandChildCount = layout.getPageChildCount(); 15579f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int j = 0; j < grandChildCount; ++j) { 15588245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final View v = layout.getChildOnPageAt(j); 15599f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 15609f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return (Checkable) v; 15619f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15629f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15639f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15649f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15659f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return null; 15669f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 15679f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 15685f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void resetCheckedGrandchildren() { 15695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // loop through children, and set all of their children to _not_ be checked 15705f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final ArrayList<Checkable> checked = getCheckedGrandchildren(); 15715f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < checked.size(); ++i) { 15725f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final Checkable c = checked.get(i); 15735f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung c.setChecked(false); 15745f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15755f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 15765f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 157786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 157886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 157986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 158086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 158186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 158286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1583321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 158486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 158586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 158686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 158786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 158886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1589321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPageItems(int page); 159086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1591983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka protected void postInvalidatePageData(final boolean clearViews) { 1592983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka post(new Runnable() { 1593983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka // post the call to avoid a call to requestLayout from a layout pass 1594983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka public void run() { 1595983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka if (clearViews) { 1596983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka removeAllViews(); 1597983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka } 1598983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka invalidatePageData(); 1599983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka } 1600983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka }); 1601983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka } 1602983e3fdf20acd22b57e3e6f0a309f524c52b62feMichael Jurka 1603244d74cb353f1260c4d633e719bf84bb3b6e52bcPatrick Dubroy protected void invalidatePageData() { 1604f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsDataReady) { 1605f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return; 1606f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 1607f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 16080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 1609007c69867d821ea2b271398577a8b3440b3a7046Winson Chung hideScrollingIndicator(true); 1610007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 16110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 16120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 161386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 16140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 16150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 16160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 16170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 16180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 16190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 162086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 16210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 16220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka loadAssociatedPages(mCurrentPage); 16230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = true; 1624b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung updateAdjacentPagesAlpha(); 16250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 16260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1628007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1629007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private ImageView getScrollingIndicator() { 1630007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // We use mHasScrollIndicator to prevent future lookups if there is no sibling indicator 1631007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // found 1632007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mHasScrollIndicator && mScrollIndicator == null) { 1633007c69867d821ea2b271398577a8b3440b3a7046Winson Chung ViewGroup parent = (ViewGroup) getParent(); 1634007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator = (ImageView) (parent.findViewById(R.id.paged_view_indicator)); 1635007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mHasScrollIndicator = mScrollIndicator != null; 1636007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mHasScrollIndicator) { 1637007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.setVisibility(View.VISIBLE); 1638007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1639007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1640007c69867d821ea2b271398577a8b3440b3a7046Winson Chung return mScrollIndicator; 1641007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1642007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1643007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected boolean isScrollingIndicatorEnabled() { 1644007c69867d821ea2b271398577a8b3440b3a7046Winson Chung return true; 1645007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1646007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1647007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected void showScrollingIndicator() { 1648007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (LauncherApplication.isScreenLarge()) return; 1649007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1650007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1651007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1652007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1653007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1654007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Update the width of the indicator to the approx. width of each page in the full bar 1655007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.getLayoutParams().width = getPageWidthForScrollingIndicator() / getChildCount(); 1656007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.requestLayout(); 1657007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1658007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Fade the indicator in 1659007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 1660007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.animate().alpha(1f).setDuration(sScrollIndicatorFadeInDuration); 1661007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1662007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1663007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1664007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected void hideScrollingIndicator(boolean immediately) { 1665007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (LauncherApplication.isScreenLarge()) return; 1666007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1667007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1668007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1669007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1670007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1671007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // Fade the indicator out 1672007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 1673007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.animate().alpha(0f).setDuration(immediately ? 1674007c69867d821ea2b271398577a8b3440b3a7046Winson Chung sScrollIndicatorFastFadeOutDuration : sScrollIndicatorFadeOutDuration); 1675007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1676007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1677007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1678007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private void updateScrollingIndicator() { 1679007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (LauncherApplication.isScreenLarge()) return; 1680007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (getChildCount() <= 1) return; 1681007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (!isScrollingIndicatorEnabled()) return; 1682007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1683007c69867d821ea2b271398577a8b3440b3a7046Winson Chung getScrollingIndicator(); 1684007c69867d821ea2b271398577a8b3440b3a7046Winson Chung if (mScrollIndicator != null) { 1685007c69867d821ea2b271398577a8b3440b3a7046Winson Chung updateScrollingIndicatorPosition(); 1686007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1687007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1688007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1689007c69867d821ea2b271398577a8b3440b3a7046Winson Chung protected int getPageWidthForScrollingIndicator() { 1690007c69867d821ea2b271398577a8b3440b3a7046Winson Chung return getMeasuredWidth(); 1691007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 1692007c69867d821ea2b271398577a8b3440b3a7046Winson Chung 1693007c69867d821ea2b271398577a8b3440b3a7046Winson Chung private void updateScrollingIndicatorPosition() { 1694007c69867d821ea2b271398577a8b3440b3a7046Winson Chung // We can make the page width smaller to make it look more centered 1695007c69867d821ea2b271398577a8b3440b3a7046Winson Chung int pageWidth = getPageWidthForScrollingIndicator(); 1696007c69867d821ea2b271398577a8b3440b3a7046Winson Chung int pageOffset = (getMeasuredWidth() - pageWidth) / 2; 1697007c69867d821ea2b271398577a8b3440b3a7046Winson Chung int maxPageWidth = getChildCount() * pageWidth; 1698007c69867d821ea2b271398577a8b3440b3a7046Winson Chung float offset = (float) getScrollX() / maxPageWidth; 1699007c69867d821ea2b271398577a8b3440b3a7046Winson Chung int indicatorWidth = pageWidth / getChildCount(); 1700007c69867d821ea2b271398577a8b3440b3a7046Winson Chung int indicatorCenterOffset = indicatorWidth / 2 - mScrollIndicator.getMeasuredWidth() / 2; 1701007c69867d821ea2b271398577a8b3440b3a7046Winson Chung int indicatorPos = (int) (offset * pageWidth) + pageOffset + indicatorCenterOffset; 1702007c69867d821ea2b271398577a8b3440b3a7046Winson Chung mScrollIndicator.setTranslationX(indicatorPos); 1703007c69867d821ea2b271398577a8b3440b3a7046Winson Chung } 17046a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17056a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung /* Accessibility */ 17066a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 17076a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 17086a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onInitializeAccessibilityNodeInfo(info); 17096a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung info.setScrollable(true); 17106a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 17116a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17126a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 17136a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 17146a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onInitializeAccessibilityEvent(event); 17156a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setScrollable(true); 17166a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { 17176a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setFromIndex(mCurrentPage); 17186a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setToIndex(mCurrentPage); 17196a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.setItemCount(getChildCount()); 17206a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 17216a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 17226a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17236a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 17246a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 17256a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung // Do not append text content to scroll events they are fired frequently 17266a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung // and the client has already received another event type with the text. 17276a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) { 17286a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.dispatchPopulateAccessibilityEvent(event); 17296a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 17306a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17316a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung onPopulateAccessibilityEvent(event); 17326a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung return false; 17336a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 17346a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17356a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung @Override 17366a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 17376a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung super.onPopulateAccessibilityEvent(event); 17386a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17396a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { 17406a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung event.getText().add(getCurrentPageDescription()); 17416a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 17426a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 17436a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung 17446a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung protected String getCurrentPageDescription() { 17456a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; 17466a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung return String.format(mContext.getString(R.string.default_scroll_format), 17476a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung page + 1, getChildCount()); 17486a0f57dfafced837a2a282d8feec28d5418be3b9Winson Chung } 1749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1750