PagedView.java revision f8d2823d885ba682140aee1ae0504c1c5e67a24b
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 190499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport java.util.ArrayList; 209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 21228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.Animator; 22228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorInflater; 23228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.AnimatorListenerAdapter; 24228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chungimport android.animation.ObjectAnimator; 25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context; 269c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray; 27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas; 28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect; 29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel; 30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable; 31321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet; 325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode; 33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 38321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 39e0f66b546994a9bdee452851c17a148db02ec300Adam Cohenimport android.view.animation.Interpolator; 405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable; 41321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 42321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 430499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chungimport com.android.launcher.R; 4480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 45321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/** 46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a 470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages" 48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 49321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup { 50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final String TAG = "PagedView"; 510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final int INVALID_PAGE = -1; 52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 5386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // the min drag distance for a fling to register, to prevent random page shifts 549cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_FLING = 25; 559cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung // The min drag distance to trigger a page shift (regardless of velocity) 569cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_MOVE = 200; 57321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 58e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int PAGE_SNAP_ANIMATION_DURATION = 550; 590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 61e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final float OVERSCROLL_DAMP_FACTOR = 0.08f; 62e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MINIMUM_SNAP_VELOCITY = 2200; 63e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static final int MIN_FLING_VELOCITY = 250; 6468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 70321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 75a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung protected int mRestorePage = -1; 7668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mMaxScrollX; 770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 78321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 80321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 817426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionX; 827426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionY; 83f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private int mLastScreenCenter = -1; 84321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 89e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; 90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 92321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 957426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected boolean mAllowLongPress = true; 96321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 977426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected int mTouchSlop; 98321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 99321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 1009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageSpacing; 1019c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingTop; 1029c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingBottom; 1039c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingLeft; 1049c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingRight; 105df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutWidthGap; 106df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutHeightGap; 1079c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mCellCountX; 1089c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mCellCountY; 1097da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung protected boolean mCenterPagesVertically; 11068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected boolean mAllowOverScroll = true; 11168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mUnboundedScrollX; 112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1138c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // parameter that adjusts the layout to be optimized for pages with that scale factor 114d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected float mLayoutScale = 1.0f; 115d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1165f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected static final int INVALID_POINTER = -1; 117321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1185f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected int mActivePointerId = INVALID_POINTER; 119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 121321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private ArrayList<Boolean> mDirtyPageContent; 12386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private boolean mDirtyPageAlpha; 124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1255f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // choice modes 1265f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_NONE = 0; 1275f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_SINGLE = 1; 1285f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // Multiple selection mode is not supported by all Launcher actions atm 1295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_MULTIPLE = 2; 1309f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 131e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka protected int mChoiceMode; 1325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private ActionMode mActionMode; 1335f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 1340499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung // NOTE: This is a shared icon cache across all the PagedViews. Currently it is only used in 1350499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung // AllApps and Customize, and allows them to share holographic icons for the application view 1360499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung // (which is in both). 1370499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung protected static PagedViewIconCache mPageViewIconCache = new PagedViewIconCache(); 138241c3b451d7841ba08247beea784953eca4e8582Winson Chung 1390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1531262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected boolean mIsPageMoving = false; 1541262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 15586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 15686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 1695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1719c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 1729c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView, defStyle, 0); 1739c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0); 1749c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingTop = a.getDimensionPixelSize( 1759c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingTop, 10); 1769c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingBottom = a.getDimensionPixelSize( 1779c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingBottom, 10); 1789c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingLeft = a.getDimensionPixelSize( 1799c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingLeft, 10); 1809c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingRight = a.getDimensionPixelSize( 1819c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingRight, 10); 182df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutWidthGap = a.getDimensionPixelSize( 183df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutWidthGap, -1); 184df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutHeightGap = a.getDimensionPixelSize( 185df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutHeightGap, -1); 1869c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen a.recycle(); 1879c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen 188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 1890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 190321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 19686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 19786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 198e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mScroller = new Scroller(getContext(), new ScrollInterpolator()); 19986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 2007da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung mCenterPagesVertically = true; 201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 20886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 20986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 21086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 21186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 21686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 21886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 22086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 22186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 22486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 22886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 231321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int getScrollWidth() { 233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getWidth(); 234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 236be9093426aec3a6d412811a12d2400e645613242Adam Cohen public int getTouchState() { 237be9093426aec3a6d412811a12d2400e645613242Adam Cohen return mTouchState; 238be9093426aec3a6d412811a12d2400e645613242Adam Cohen } 239be9093426aec3a6d412811a12d2400e645613242Adam Cohen 240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 241bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * Updates the scroll of the current page immediately to its final scroll position. We use this 242bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of 243bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * the previous tab page. 244bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung */ 245bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung protected void updateCurrentPageScroll() { 246bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 247bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung scrollTo(newX, 0); 248bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung mScroller.setFinalX(newX); 249bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung } 250bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung 251bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung /** 25286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 25486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 25572e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (!mScroller.isFinished()) { 25672e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy mScroller.abortAnimation(); 25772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 258d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // don't introduce any checks like mCurrentPage == currentPage here-- if we change the 259d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // the default 260d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (getChildCount() == 0) { 26172e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy return; 26272e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 26486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 265bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung updateCurrentPageScroll(); 26686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 267a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung invalidate(); 268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 27186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 27286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2761262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy private void pageBeginMoving() { 2771262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = true; 2781262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageBeginMoving(); 2791262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 2801262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 2811262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy private void pageEndMoving() { 2821262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageEndMoving(); 2831262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = false; 2841262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 2851262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 2860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2871262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageBeginMoving() { 2880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2911262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageEndMoving() { 2920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 29586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 30286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 30486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 30968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen public void scrollBy(int x, int y) { 31068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen scrollTo(mUnboundedScrollX + x, mScrollY + y); 31168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 31268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 31368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen @Override 3140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 31568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mUnboundedScrollX = x; 31668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 31768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (x < 0) { 31868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(0, y); 31968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 32068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x); 32168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 32268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else if (x > mMaxScrollX) { 32368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(mMaxScrollX, y); 32468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 32568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x - mMaxScrollX); 32668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 32768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 32868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(x, y); 32968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 33068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 3310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 3320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 3330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 3360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 3385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 3400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 34286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 3435f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 34486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 34586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 3460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 34773aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // We don't want to trigger a page end moving unless the page has settled 34873aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // and the user has stopped scrolling 34973aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen if (mTouchState == TOUCH_STATE_REST) { 35073aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen pageEndMoving(); 35173aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen } 3520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 3540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 3550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 3580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 3590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 361321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 362321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 365321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 366321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 367321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 368321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 369321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 3706b879f0a5885274a85333531e091283405d490ccAdam Lesinski /* Allow the height to be set as WRAP_CONTENT. This allows the particular case 3716b879f0a5885274a85333531e091283405d490ccAdam Lesinski * of the All apps view on XLarge displays to not take up more space then it needs. Width 3726b879f0a5885274a85333531e091283405d490ccAdam Lesinski * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect 3736b879f0a5885274a85333531e091283405d490ccAdam Lesinski * each page to have the same width. 3746b879f0a5885274a85333531e091283405d490ccAdam Lesinski */ 375321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 3766b879f0a5885274a85333531e091283405d490ccAdam Lesinski int heightSize = MeasureSpec.getSize(heightMeasureSpec); 3776b879f0a5885274a85333531e091283405d490ccAdam Lesinski int maxChildHeight = 0; 3786b879f0a5885274a85333531e091283405d490ccAdam Lesinski 3796b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 380321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 381321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 3825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // unless they were set to WRAP_CONTENT 383321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 384321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 3855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // disallowing padding in paged view (just pass 0) 3865f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final View child = getChildAt(i); 3875f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 3885f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 3895f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childWidthMode; 3905f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.width == LayoutParams.WRAP_CONTENT) { 3915f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.AT_MOST; 3925f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 3935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.EXACTLY; 3945f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 3955f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 3965f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childHeightMode; 3975f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.height == LayoutParams.WRAP_CONTENT) { 3985f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.AT_MOST; 3995f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4005f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.EXACTLY; 4015f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4025f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4035f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childWidthMeasureSpec = 4045f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka MeasureSpec.makeMeasureSpec(widthSize, childWidthMode); 4055f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childHeightMeasureSpec = 4066b879f0a5885274a85333531e091283405d490ccAdam Lesinski MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode); 4075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4085f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4096b879f0a5885274a85333531e091283405d490ccAdam Lesinski maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); 4106b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 4116b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4126b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (heightMode == MeasureSpec.AT_MOST) { 4136b879f0a5885274a85333531e091283405d490ccAdam Lesinski heightSize = maxChildHeight + verticalPadding; 414321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 415faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen if (childCount > 0) { 416faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1); 417faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } else { 418faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = 0; 419faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } 420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setMeasuredDimension(widthSize, heightSize); 422cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka } 423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4248c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) { 425af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 426af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int delta = newX - mScrollX; 427af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 4288c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka final int pageCount = getChildCount(); 4298c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka for (int i = 0; i < pageCount; i++) { 4308c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka View page = (View) getChildAt(i); 4318c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka page.setX(page.getX() + delta); 432af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 433af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka setCurrentPage(newCurrentPage); 434af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 435af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 4368c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a 4378c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and 438d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // tightens the layout accordingly 439d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka public void setLayoutScale(float childrenScale) { 440d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mLayoutScale = childrenScale; 441d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 442d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Now we need to do a re-layout, but preserving absolute X and Y coordinates 443d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childCount = getChildCount(); 444d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenX[] = new float[childCount]; 445d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenY[] = new float[childCount]; 446d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 447d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 448d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenX[i] = child.getX(); 449d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenY[i] = child.getY(); 450d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 451d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka onLayout(false, mLeft, mTop, mRight, mBottom); 452d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 453d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 454d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setX(childrenX[i]); 455d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setY(childrenY[i]); 456d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 457d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Also, the page offset has changed (since the pages are now smaller); 458d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // update the page offset, but again preserving absolute X and Y coordinates 4598c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka scrollToNewPageWithoutMovingPages(mCurrentPage); 460d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 461d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 462cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka @Override 46328750fba6a2d141eb9a1e566718c17236030b815Michael Jurka protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 4645f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(false); 466cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 467cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka scrollTo(newX, 0); 468cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka mScroller.setFinalX(newX); 469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(true); 470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mFirstLayout = false; 471321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4736b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 476321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 477e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung childLeft = getRelativeChildOffset(0); 478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 480321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final View child = getChildAt(i); 482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 483d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int childWidth = getScaledMeasuredWidth(child); 4846b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int childHeight = child.getMeasuredHeight(); 4856b879f0a5885274a85333531e091283405d490ccAdam Lesinski int childTop = mPaddingTop; 4866b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (mCenterPagesVertically) { 4876b879f0a5885274a85333531e091283405d490ccAdam Lesinski childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2; 4886b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 489d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 4906b879f0a5885274a85333531e091283405d490ccAdam Lesinski child.layout(childLeft, childTop, 491d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childLeft + child.getMeasuredWidth(), childTop + childHeight); 4929c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen childLeft += childWidth + mPageSpacing; 493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 494321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 495d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 496d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mFirstLayout = false; 497d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 499321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 500e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void updateAdjacentPagesAlpha() { 5010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 5020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { 503e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int halfScreenSize = getMeasuredWidth() / 2; 504e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int screenCenter = mScrollX + halfScreenSize; 5050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = getChildCount(); 5060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < childCount; ++i) { 5070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 508d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 5090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int halfChildWidth = (childWidth / 2); 5100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childCenter = getChildOffset(i) + halfChildWidth; 511e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 512b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // On the first layout, we may not have a width nor a proper offset, so for now 513b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // we should just assume full page width (and calculate the offset according to 514b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // that). 515b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung if (childWidth <= 0) { 516b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childWidth = getMeasuredWidth(); 517b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childCenter = (i * childWidth) + (childWidth / 2); 518b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung } 519b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 520e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int d = halfChildWidth; 521e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int distanceFromScreenCenter = childCenter - screenCenter; 522e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (distanceFromScreenCenter > 0) { 523e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i > 0) { 524d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i - 1)) / 2; 525e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 5260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 527e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i < childCount - 1) { 528d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i + 1)) / 2; 529e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 5300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 5319c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen d += mPageSpacing; 532e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 533b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // Preventing potential divide-by-zero 534b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung d = Math.max(1, d); 535b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 536e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d; 537e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); 538e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float alpha = 1.0f - dimAlpha; 539e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 540f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (alpha < ALPHA_QUANTIZE_LEVEL) { 541f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 0.0f; 542f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) { 543f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 1.0f; 544f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 545f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 5460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (Float.compare(alpha, layout.getAlpha()) != 0) { 5470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.setAlpha(alpha); 5480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 549affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 5500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = false; 551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 553e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 5540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 555f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen protected void screenScrolled(int screenCenter) { 556f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 557f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 558e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung @Override 559e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void dispatchDraw(Canvas canvas) { 560f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int halfScreenSize = getMeasuredWidth() / 2; 561f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int screenCenter = mScrollX + halfScreenSize; 562f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 563f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (screenCenter != mLastScreenCenter) { 564f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen screenScrolled(screenCenter); 565f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen updateAdjacentPagesAlpha(); 566f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mLastScreenCenter = screenCenter; 567f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 5680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 569e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung // Find out which screens are visible; as an optimization we only call draw on them 5700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // As an optimization, this code assumes that all pages have the same width as the 0th 5710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page. 5720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 573c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka if (pageCount > 0) { 574d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int pageWidth = getScaledMeasuredWidth(getChildAt(0)); 575c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int screenWidth = getMeasuredWidth(); 576c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int x = getRelativeChildOffset(0) + pageWidth; 577c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int leftScreen = 0; 578c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int rightScreen = 0; 579c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka while (x <= mScrollX) { 580c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka leftScreen++; 581c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(leftScreen)) + mPageSpacing; 582c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 583c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = leftScreen; 584c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung while (x < mScrollX + screenWidth && rightScreen < pageCount) { 585c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen++; 586c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung if (rightScreen < pageCount) { 587c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(rightScreen)) + mPageSpacing; 588c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung } 589c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 590c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = Math.min(getChildCount() - 1, rightScreen); 5910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 592c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final long drawingTime = getDrawingTime(); 59329d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung // Clip to the bounds 59429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.save(); 59529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft, 59629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung mScrollY + mBottom - mTop); 59729d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 598c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka for (int i = leftScreen; i <= rightScreen; i++) { 599c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka drawChild(canvas, getChildAt(i), drawingTime); 600c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 60129d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.restore(); 6020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 603321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 604321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 605321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 606321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 60786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 60886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 60986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 610321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 611321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 612321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 613321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 614321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 61786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 61886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 61986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 620321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 62186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 622321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 62386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v.requestFocus(direction, previouslyFocusedRect); 626321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 631321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 63386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 63486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 635321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 636321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 63886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 63986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 646321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 647321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 64886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 64986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 651321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 65286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 65386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 654321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 655321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 65686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 65786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 661321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 66486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 665321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 66686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 66786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 669321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 670321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 67186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 673321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 674321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 675321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 677321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 679321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 680321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 683321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 684321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 685321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 686321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 688321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 690321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 69886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getChildAt(mCurrentPage); 69986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 704d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 705d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the previous page. 706d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 707d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsPreviousPage(float x, float y) { 708d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing); 709d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 710d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 711d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 712d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the next page. 713d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 714d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsNextPage(float x, float y) { 715d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing)); 716d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 717d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 72645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 72745e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 72845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 729321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 730321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 731321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 740321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 741321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 7461ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka if (mActivePointerId != INVALID_POINTER) { 7471ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka determineScrollingStart(ev); 7481ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka break; 7491ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka } 7501ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 7511ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 7521ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // i.e. fall through to the next case (don't break) 7531ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 7541ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // while it's small- this was causing a crash before we checked for INVALID_POINTER) 755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 772fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 7735f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 7745f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 7755f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 7765f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 7775f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 7785f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 7795f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 78186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 783d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { 784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 785d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (hitsPreviousPage(x, y)) { 786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 787d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (hitsNextPage(x, y)) { 788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 791321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 793321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 7961d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 7971d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown case MotionEvent.ACTION_CANCEL: 798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 81580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 81680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 817228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung ObjectAnimator anim = (ObjectAnimator) AnimatorInflater. 818228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung loadAnimator(mContext, R.anim.paged_view_click_feedback); 819228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.setTarget(v); 820228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.addListener(new AnimatorListenerAdapter() { 821228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung public void onAnimationRepeat(Animator animation) { 82280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 82380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 82480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 825228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.start(); 82680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 82780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 828f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev) { 829f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen determineScrollingStart(ev, 1.0f); 830f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 831f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 832321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 836f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { 837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 841321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 842321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 843321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 844321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 845321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 846321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 847f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final int touchSlop = Math.round(touchSlopScale * mTouchSlop); 848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 852f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (xMoved || xPaged || yMoved) { 8530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 8570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 8580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 8590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 862f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen cancelCurrentPageLongPress(); 863f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 864f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 865f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 866f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void cancelCurrentPageLongPress() { 867f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (mAllowLongPress) { 868f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen mAllowLongPress = false; 869f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // Try canceling the long press. It could also have been scheduled 870f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // by a distant descendant, so use the mAllowLongPress flag to block 871f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // everything 872f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final View currentPage = getPageAt(mCurrentPage); 873f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (currentPage != null) { 874f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen currentPage.cancelLongPress(); 875321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 879e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // This curve determines how the effect of scrolling over the limits of the page dimishes 880e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as the user pulls further and further from the bounds 881e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private float overScrollInfluenceCurve(float f) { 882e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 1.0f; 883e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return f * f * f + 1.0f; 884e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 885e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 88668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected void overScroll(float amount) { 887e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int screenSize = getMeasuredWidth(); 888e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 889e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float f = (amount / screenSize); 890e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 891e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (f == 0) return; 892e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 893e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 8947bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen // Clamp this factor, f, to -1 < f < 1 8957bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen if (Math.abs(f) >= 1) { 8967bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen f /= Math.abs(f); 8977bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen } 8987bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen 899e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); 90068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (amount < 0) { 90168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = overScrollAmount; 90268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 90368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = mMaxScrollX + overScrollAmount; 90468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 90568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 90668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 90768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 908c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka protected float maxOverScroll() { 909c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not 910c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // exceed). Used to find out how much extra wallpaper we need for the overscroll effect 911c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka float f = 1.0f; 912c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 913c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka return OVERSCROLL_DAMP_FACTOR * f; 914c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka } 915c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka 916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 91845e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 91945e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onTouchEvent(ev); 92045e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 921b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka acquireVelocityTrackerAndAddMovement(ev); 922321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 924321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 925321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 929321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 935321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 936321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 9380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 9390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 9400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 945321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 948321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int deltaX = (int) (mLastMotionX - x); 949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 95168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (deltaX != 0) { 95268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mTouchX += deltaX; 95368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 95468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (!mDeferScrollUpdate) { 95568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen scrollBy(deltaX, 0); 95668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 95768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 962564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen } else { 963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 966321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 968321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 969321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 970321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 971321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 972321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 973321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 974321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 9759cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung final int deltaX = (int) (x - mDownMotionX); 9769cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung boolean isfling = Math.abs(deltaX) > MIN_LENGTH_FOR_FLING; 9779cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE; 978321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 9790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 9809cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung if ((isSignificantMove && deltaX > 0 || 981e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen (isfling && velocityX > snapVelocity)) && mCurrentPage > 0) { 9820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage - 1, velocityX); 9839cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung } else if ((isSignificantMove && deltaX < 0 || 9849cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung (isfling && velocityX < -snapVelocity)) && 98586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 9860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage + 1, velocityX); 987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 989321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 990d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 992321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 993321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 99486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 99586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 99686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 998321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1000d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 1001321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1002321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1003321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 100486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 100586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 100686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1008321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 10101d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } else { 10111d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 1012321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1013321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1014321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1015b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1017321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1018321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 1019b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 1020b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka snapToDestination(); 1021b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1022321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1023321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1024b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1027321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 1028321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1030321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 1033321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1034321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1035b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { 1036b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker == null) { 1037b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = VelocityTracker.obtain(); 1038b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1039b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.addMovement(ev); 1040b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1041b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1042b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void releaseVelocityTracker() { 1043b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker != null) { 1044b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.recycle(); 1045b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = null; 1046b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1047b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1048b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1049321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 1050321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 1051321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 1052321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 1053321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 1054321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 1055321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 1056321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1058321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 1059321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 1060321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 1061321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 1062321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 1063321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1064321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 10651d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown if (mTouchState == TOUCH_STATE_REST) { 10661d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 10671d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 10681d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 10691d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown 10701d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown protected void onWallpaperTap(MotionEvent ev) { 1071321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1072321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 1075321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 107686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 107786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page >= 0 && !isInTouchMode()) { 107886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 1079321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1080321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1081321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1082e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getChildIndexForRelativeOffset(int relativeOffset) { 1083e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int childCount = getChildCount(); 10849c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int left; 10859c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int right; 1086e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung for (int i = 0; i < childCount; ++i) { 10879c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen left = getRelativeChildOffset(i); 1088d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka right = (left + getScaledMeasuredWidth(getChildAt(i))); 1089e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung if (left <= relativeOffset && relativeOffset <= right) { 1090e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return i; 1091e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1092e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1093e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return -1; 1094e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1095e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 1096321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getRelativeChildOffset(int index) { 1097321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2; 1098321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1099321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getChildOffset(int index) { 1101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) 1102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return 0; 1103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1104321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int offset = getRelativeChildOffset(0); 1105321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < index; ++i) { 1106d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka offset += getScaledMeasuredWidth(getChildAt(i)) + mPageSpacing; 1107321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return offset; 1109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1110321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1111d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected int getScaledMeasuredWidth(View child) { 1112d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka return (int) (child.getMeasuredWidth() * mLayoutScale + 0.5f); 1113d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 1114d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1115d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen int getPageNearestToCenterOfScreen() { 1116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenter = getMeasuredWidth(); 1117321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 1118321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 1119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 1120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 11210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 1122d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 1123321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 1127321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 1128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 1129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1130321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1131d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen return minDistanceFromScreenCenterIndex; 1132d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen } 1133d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen 1134d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen protected void snapToDestination() { 1135d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); 1136321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1137321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1138e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static class ScrollInterpolator implements Interpolator { 1139e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public ScrollInterpolator() { 1140e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1141e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1142e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public float getInterpolation(float t) { 1143e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen t -= 1.0f; 1144e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return t*t*t*t*t + 1; 1145e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1146e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1147e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1148e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // We want the duration of the page snap animation to be influenced by the distance that 1149e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // the screen has to travel, however, we don't want this duration to be effected in a 1150e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // purely linear fashion. Instead, we use this method to moderate the effect that the distance 1151e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // of travel has on the overall snap duration. 1152e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceInfluenceForSnapDuration(float f) { 1153e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 0.5f; // center the values about 0. 1154e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f *= 0.3f * Math.PI / 2.0f; 1155e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return (float) Math.sin(f); 1156e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1157e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 11580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 1159e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); 1160e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int halfScreenSize = getMeasuredWidth() / 2; 1161e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1162e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 1163e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int delta = newX - mUnboundedScrollX; 1164e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int duration = 0; 1165e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1166e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (Math.abs(velocity) < MIN_FLING_VELOCITY) { 1167e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // If the velocity is low enough, then treat this more as an automatic page advance 1168e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as opposed to an apparent physical response to flinging 1169e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1170e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return; 1171e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1172e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1173e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // Here we compute a "distance" that will be used in the computation of the overall 1174e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // snap duration. This is a function of the actual distance that needs to be traveled; 1175e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we keep this value close to half screen size in order to reduce the variance in snap 1176e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // duration as a function of the distance the page needs to travel. 1177e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceRatio = 1.0f * Math.abs(delta) / 2 * halfScreenSize; 1178e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distance = halfScreenSize + halfScreenSize * 1179e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen distanceInfluenceForSnapDuration(distanceRatio); 1180e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1181e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.abs(velocity); 1182e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); 1183e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1184e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we want the page's snap velocity to approximately match the velocity at which the 1185e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // user flings, so we scale the duration by a value near to the derivative of the scroll 1186e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // interpolator at zero, ie. 5. We use 6 to make it a little slower. 1187e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen duration = 6 * Math.round(1000 * Math.abs(distance / velocity)); 1188e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1189e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, delta, duration); 11900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 11910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 11920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 11935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 11960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 119786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 1198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 119986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 120068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen final int sX = mUnboundedScrollX; 1201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 12020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 12030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 12040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 12060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 12070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 12090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 12100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild == getChildAt(mCurrentPage)) { 12110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 12120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 12130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 1216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 1217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 1218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 122168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); 122280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 122380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // only load some associated pages 122486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung loadAssociatedPages(mNextPage); 12250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 1226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 1227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 1230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 123186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 1232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 123386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 1234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 1238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 123986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 1240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 124186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 1242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 124586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 1246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 1247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 1248321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 1249321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 1250321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 1251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (vp == getChildAt(i)) { 1252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 1253321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1255321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 1257321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1258321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 1263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 12670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 12680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 12690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 12700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 12710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 12720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 12730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 127586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 1276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 1278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 1282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 128386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 1284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 1288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 128986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 1290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 1293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 1294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 1295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 1296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 1299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 1300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 1302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 130486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void loadAssociatedPages(int page) { 13050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 13060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 13070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 1308e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int lowerPageBound = getAssociatedLowerPageBound(page); 1309e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int upperPageBound = getAssociatedUpperPageBound(page); 13100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 13110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final ViewGroup layout = (ViewGroup) getChildAt(i); 13120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = layout.getChildCount(); 13130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 13140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 13150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPageItems(i); 13160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 13170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 13190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (childCount > 0) { 13200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.removeAllViews(); 13210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, true); 132380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 132480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 132580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 132680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 132780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 132880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1329e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedLowerPageBound(int page) { 1330e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.max(0, page - 1); 1331e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1332e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedUpperPageBound(int page) { 1333e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int count = getChildCount(); 1334e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.min(page + 1, count - 1); 1335e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1336e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 13375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void startChoiceMode(int mode, ActionMode.Callback callback) { 1338430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy if (isChoiceMode(CHOICE_MODE_NONE)) { 1339430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mChoiceMode = mode; 1340430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mActionMode = startActionMode(callback); 1341430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy } 13425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13442b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public void endChoiceMode() { 13455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (!isChoiceMode(CHOICE_MODE_NONE)) { 13465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 13475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung resetCheckedGrandchildren(); 1348e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka if (mActionMode != null) mActionMode.finish(); 13499f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy mActionMode = null; 13505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected boolean isChoiceMode(int mode) { 13545f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return mChoiceMode == mode; 13555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected ArrayList<Checkable> getCheckedGrandchildren() { 13585f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung ArrayList<Checkable> checked = new ArrayList<Checkable>(); 13595f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int childCount = getChildCount(); 13605f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < childCount; ++i) { 1361d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung final ViewGroup layout = (ViewGroup) getChildAt(i); 13625f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int grandChildCount = layout.getChildCount(); 13635f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int j = 0; j < grandChildCount; ++j) { 13645f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final View v = layout.getChildAt(j); 13659f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 13665f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung checked.add((Checkable) v); 13675f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13685f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13695f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13705f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return checked; 13715f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13725f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13739f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy /** 13749f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * If in CHOICE_MODE_SINGLE and an item is checked, returns that item. 13759f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * Otherwise, returns null. 13769f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy */ 13779f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy protected Checkable getSingleCheckedGrandchild() { 13789f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (mChoiceMode == CHOICE_MODE_SINGLE) { 13799f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int childCount = getChildCount(); 13809f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int i = 0; i < childCount; ++i) { 1381d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung final ViewGroup layout = (ViewGroup) getChildAt(i); 13829f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int grandChildCount = layout.getChildCount(); 13839f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int j = 0; j < grandChildCount; ++j) { 13849f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final View v = layout.getChildAt(j); 13859f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 13869f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return (Checkable) v; 13879f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 13889f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 13899f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 13909f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 13919f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return null; 13929f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 13939f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 13942b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public Object getChosenItem() { 13952b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy View checkedView = (View) getSingleCheckedGrandchild(); 13962b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy if (checkedView != null) { 13972b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy return checkedView.getTag(); 13982b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy } 13992b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy return null; 14002b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy } 14012b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy 14025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void resetCheckedGrandchildren() { 14035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // loop through children, and set all of their children to _not_ be checked 14045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final ArrayList<Checkable> checked = getCheckedGrandchildren(); 14055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < checked.size(); ++i) { 14065f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final Checkable c = checked.get(i); 14075f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung c.setChecked(false); 14085f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14095f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14105f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 1411a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung public void setRestorePage(int restorePage) { 1412a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung mRestorePage = restorePage; 1413a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung } 1414a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung 141586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 141686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 141786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 141886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 141986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 142086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 142286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 142386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 142486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 142586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 142686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPageItems(int page); 142886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1429321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void invalidatePageData() { 14300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 14310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 14320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 143386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 14340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 14350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 14360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 14370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 14380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 14390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 144086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1441a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung // Use the restore page if necessary 1442a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung if (mRestorePage > -1) { 1443a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung mCurrentPage = mRestorePage; 1444a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung mRestorePage = -1; 1445a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung } 1446a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung 14470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 14480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka loadAssociatedPages(mCurrentPage); 14490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = true; 1450b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung updateAdjacentPagesAlpha(); 14510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 14520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1455