PagedView.java revision 6f13342ffd3f968de9ff86b988621cc91d94adff
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; 64b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; 6568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 71321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 76a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung protected int mRestorePage = -1; 7768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mMaxScrollX; 780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 80321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 81321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 827426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionX; 83c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung protected float mLastMotionXRemainder; 847426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected float mLastMotionY; 85aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen protected float mTotalMotionX; 86f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private int mLastScreenCenter = -1; 87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 92e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; 93321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 95321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 97321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 987426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected boolean mAllowLongPress = true; 99321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1007426c42ce01e132781faa68941d79d23cd7fdf1eMichael Jurka protected int mTouchSlop; 101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 1039c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageSpacing; 1049c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingTop; 1059c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingBottom; 1069c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingLeft; 1079c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingRight; 108df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutWidthGap; 109df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutHeightGap; 1109c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mCellCountX; 1119c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mCellCountY; 1127da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung protected boolean mCenterPagesVertically; 11368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected boolean mAllowOverScroll = true; 11468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected int mUnboundedScrollX; 115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1168c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // parameter that adjusts the layout to be optimized for pages with that scale factor 117d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected float mLayoutScale = 1.0f; 118d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1195f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected static final int INVALID_POINTER = -1; 120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1215f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected int mActivePointerId = INVALID_POINTER; 122321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private ArrayList<Boolean> mDirtyPageContent; 12686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private boolean mDirtyPageAlpha; 127321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1285f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // choice modes 1295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_NONE = 0; 1305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_SINGLE = 1; 1315f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // Multiple selection mode is not supported by all Launcher actions atm 1325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_MULTIPLE = 2; 1339f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 134e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka protected int mChoiceMode; 1355f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private ActionMode mActionMode; 1365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 1370499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung // NOTE: This is a shared icon cache across all the PagedViews. Currently it is only used in 1380499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung // AllApps and Customize, and allows them to share holographic icons for the application view 1390499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung // (which is in both). 1400499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung protected static PagedViewIconCache mPageViewIconCache = new PagedViewIconCache(); 141241c3b451d7841ba08247beea784953eca4e8582Winson Chung 1420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1561262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected boolean mIsPageMoving = false; 1571262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 15886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 15986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 1725f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1749c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 1759c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView, defStyle, 0); 1769c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0); 1779c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingTop = a.getDimensionPixelSize( 1789c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingTop, 10); 1799c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingBottom = a.getDimensionPixelSize( 1809c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingBottom, 10); 1819c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingLeft = a.getDimensionPixelSize( 1829c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingLeft, 10); 1839c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingRight = a.getDimensionPixelSize( 1849c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingRight, 10); 185df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutWidthGap = a.getDimensionPixelSize( 186df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutWidthGap, -1); 187df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutHeightGap = a.getDimensionPixelSize( 188df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutHeightGap, -1); 1899c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen a.recycle(); 1909c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen 191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 1920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 197321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 19986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 20086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 201e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mScroller = new Scroller(getContext(), new ScrollInterpolator()); 20286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 2037da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung mCenterPagesVertically = true; 204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 21186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 21286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 21386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 21486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 21986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 220321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 22186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 22386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 22486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 22786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 23186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 232321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int getScrollWidth() { 236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getWidth(); 237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 239be9093426aec3a6d412811a12d2400e645613242Adam Cohen public int getTouchState() { 240be9093426aec3a6d412811a12d2400e645613242Adam Cohen return mTouchState; 241be9093426aec3a6d412811a12d2400e645613242Adam Cohen } 242be9093426aec3a6d412811a12d2400e645613242Adam Cohen 243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 244bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * Updates the scroll of the current page immediately to its final scroll position. We use this 245bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of 246bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * the previous tab page. 247bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung */ 248bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung protected void updateCurrentPageScroll() { 249bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 250bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung scrollTo(newX, 0); 251bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung mScroller.setFinalX(newX); 252bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung } 253bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung 254bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung /** 25586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 25786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 25872e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (!mScroller.isFinished()) { 25972e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy mScroller.abortAnimation(); 26072e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 261d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // don't introduce any checks like mCurrentPage == currentPage here-- if we change the 262d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // the default 263d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (getChildCount() == 0) { 26472e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy return; 26572e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 26786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 268bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung updateCurrentPageScroll(); 26986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 270a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung invalidate(); 271321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 27486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 27586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 279ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageBeginMoving() { 2801262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = true; 2811262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageBeginMoving(); 2821262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 2831262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 284ce7e05fbe8abd5f25ec47e0e05b5cc76ceb39d2eMichael Jurka protected void pageEndMoving() { 2851262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageEndMoving(); 2861262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = false; 2871262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 2881262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 2890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2901262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageBeginMoving() { 2910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2941262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageEndMoving() { 2950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 29886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 30586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 30786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 311321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 31268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen public void scrollBy(int x, int y) { 31368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen scrollTo(mUnboundedScrollX + x, mScrollY + y); 31468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 31568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 31668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen @Override 3170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 31868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mUnboundedScrollX = x; 31968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 32068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (x < 0) { 32168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(0, y); 32268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 32368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x); 32468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 32568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else if (x > mMaxScrollX) { 32668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(mMaxScrollX, y); 32768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (mAllowOverScroll) { 32868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen overScroll(x - mMaxScrollX); 32968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 33068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 33168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen super.scrollTo(x, y); 33268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 33368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 3340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 3350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 3360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 3390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 3415f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 342321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 3430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 3440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 34586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 3465f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 34786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 34886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 3490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 35073aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // We don't want to trigger a page end moving unless the page has settled 35173aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen // and the user has stopped scrolling 35273aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen if (mTouchState == TOUCH_STATE_REST) { 35373aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen pageEndMoving(); 35473aa9755d3db1a76e9de0f55271ef5984d78ef6fAdam Cohen } 3550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 3570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 3580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 3610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 3620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 365321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 366321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 367321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 368321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 369321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 370321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 371321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 372321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 3736b879f0a5885274a85333531e091283405d490ccAdam Lesinski /* Allow the height to be set as WRAP_CONTENT. This allows the particular case 3746b879f0a5885274a85333531e091283405d490ccAdam Lesinski * of the All apps view on XLarge displays to not take up more space then it needs. Width 3756b879f0a5885274a85333531e091283405d490ccAdam Lesinski * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect 3766b879f0a5885274a85333531e091283405d490ccAdam Lesinski * each page to have the same width. 3776b879f0a5885274a85333531e091283405d490ccAdam Lesinski */ 378321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 3796b879f0a5885274a85333531e091283405d490ccAdam Lesinski int heightSize = MeasureSpec.getSize(heightMeasureSpec); 3806b879f0a5885274a85333531e091283405d490ccAdam Lesinski int maxChildHeight = 0; 3816b879f0a5885274a85333531e091283405d490ccAdam Lesinski 3826b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 383321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 384321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 3855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // unless they were set to WRAP_CONTENT 386321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 3885f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // disallowing padding in paged view (just pass 0) 3895f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final View child = getChildAt(i); 3905f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 3915f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 3925f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childWidthMode; 3935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.width == LayoutParams.WRAP_CONTENT) { 3945f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.AT_MOST; 3955f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 3965f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.EXACTLY; 3975f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 3985f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 3995f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childHeightMode; 4005f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.height == LayoutParams.WRAP_CONTENT) { 4015f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.AT_MOST; 4025f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 4035f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.EXACTLY; 4045f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 4055f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4065f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childWidthMeasureSpec = 4075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka MeasureSpec.makeMeasureSpec(widthSize, childWidthMode); 4085f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childHeightMeasureSpec = 4096b879f0a5885274a85333531e091283405d490ccAdam Lesinski MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode); 4105f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 4115f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4126b879f0a5885274a85333531e091283405d490ccAdam Lesinski maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); 4136b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 4146b879f0a5885274a85333531e091283405d490ccAdam Lesinski 4156b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (heightMode == MeasureSpec.AT_MOST) { 4166b879f0a5885274a85333531e091283405d490ccAdam Lesinski heightSize = maxChildHeight + verticalPadding; 417321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 418faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen if (childCount > 0) { 419faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1); 420faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } else { 421faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen mMaxScrollX = 0; 422faa28305134b0d391d2baf1bff5bff4710fe819fAdam Cohen } 423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 424321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setMeasuredDimension(widthSize, heightSize); 425cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka } 426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4278c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) { 428af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 429af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka int delta = newX - mScrollX; 430af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 4318c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka final int pageCount = getChildCount(); 4328c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka for (int i = 0; i < pageCount; i++) { 4338c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka View page = (View) getChildAt(i); 4348c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka page.setX(page.getX() + delta); 435af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 436af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka setCurrentPage(newCurrentPage); 437af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka } 438af91de06b99e2d5d41ce79fefa34ce2111e51917Michael Jurka 4398c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a 4408c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and 441d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // tightens the layout accordingly 442d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka public void setLayoutScale(float childrenScale) { 443d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mLayoutScale = childrenScale; 444d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 445d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Now we need to do a re-layout, but preserving absolute X and Y coordinates 446d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childCount = getChildCount(); 447d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenX[] = new float[childCount]; 448d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka float childrenY[] = new float[childCount]; 449d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 450d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 451d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenX[i] = child.getX(); 452d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childrenY[i] = child.getY(); 453d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 454d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka onLayout(false, mLeft, mTop, mRight, mBottom); 455d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka for (int i = 0; i < childCount; i++) { 456d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final View child = getChildAt(i); 457d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setX(childrenX[i]); 458d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka child.setY(childrenY[i]); 459d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 460d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // Also, the page offset has changed (since the pages are now smaller); 461d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka // update the page offset, but again preserving absolute X and Y coordinates 4628c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka scrollToNewPageWithoutMovingPages(mCurrentPage); 463d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 464d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 465cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka @Override 46628750fba6a2d141eb9a1e566718c17236030b815Michael Jurka protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 4675f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(false); 469cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 470cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka scrollTo(newX, 0); 471cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka mScroller.setFinalX(newX); 472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(true); 473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mFirstLayout = false; 474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4766b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int verticalPadding = mPaddingTop + mPaddingBottom; 477321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 480e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung childLeft = getRelativeChildOffset(0); 481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 483321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final View child = getChildAt(i); 485321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 486d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int childWidth = getScaledMeasuredWidth(child); 4876b879f0a5885274a85333531e091283405d490ccAdam Lesinski final int childHeight = child.getMeasuredHeight(); 4886b879f0a5885274a85333531e091283405d490ccAdam Lesinski int childTop = mPaddingTop; 4896b879f0a5885274a85333531e091283405d490ccAdam Lesinski if (mCenterPagesVertically) { 4906b879f0a5885274a85333531e091283405d490ccAdam Lesinski childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2; 4916b879f0a5885274a85333531e091283405d490ccAdam Lesinski } 492d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 4936b879f0a5885274a85333531e091283405d490ccAdam Lesinski child.layout(childLeft, childTop, 494d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka childLeft + child.getMeasuredWidth(), childTop + childHeight); 4959c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen childLeft += childWidth + mPageSpacing; 496321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 497321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 498d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 499d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka mFirstLayout = false; 500d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 501321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 502321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 50303929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung protected void forceUpdateAdjacentPagesAlpha() { 50403929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung mDirtyPageAlpha = true; 50503929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung updateAdjacentPagesAlpha(); 50603929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung } 50703929773a45a21c1ff0d4e0ae152bbad1b8585a4Winson Chung 508e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void updateAdjacentPagesAlpha() { 5090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 5100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { 511e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int halfScreenSize = getMeasuredWidth() / 2; 512e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int screenCenter = mScrollX + halfScreenSize; 5130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = getChildCount(); 5140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < childCount; ++i) { 5150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 516d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 5170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int halfChildWidth = (childWidth / 2); 5180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childCenter = getChildOffset(i) + halfChildWidth; 519e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 520b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // On the first layout, we may not have a width nor a proper offset, so for now 521b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // we should just assume full page width (and calculate the offset according to 522b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // that). 523b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung if (childWidth <= 0) { 524b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childWidth = getMeasuredWidth(); 525b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childCenter = (i * childWidth) + (childWidth / 2); 526b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung } 527b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 528e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int d = halfChildWidth; 529e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int distanceFromScreenCenter = childCenter - screenCenter; 530e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (distanceFromScreenCenter > 0) { 531e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i > 0) { 532d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i - 1)) / 2; 533e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 5340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 535e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i < childCount - 1) { 536d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka d += getScaledMeasuredWidth(getChildAt(i + 1)) / 2; 537e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 5380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 5399c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen d += mPageSpacing; 540e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 541b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // Preventing potential divide-by-zero 542b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung d = Math.max(1, d); 543b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 544e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d; 545e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); 546e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float alpha = 1.0f - dimAlpha; 547e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 548f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (alpha < ALPHA_QUANTIZE_LEVEL) { 549f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 0.0f; 550f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) { 551f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 1.0f; 552f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 553f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 554a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // Due to the way we're setting alpha on our children in PagedViewCellLayout, 555a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // this optimization causes alpha to not be properly updated sometimes (repro 556a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // case: in xlarge mode, swipe to second page in All Apps, then click on "My 557a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // Apps" tab. the page will have alpha 0 until you swipe it). Removing 558a40829a4a86294237f5ea665e670ee100d9c02e5Michael Jurka // optimization fixes the issue, but we should fix this in a better manner 559cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka //if (Float.compare(alpha, layout.getAlpha()) != 0) { 5600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.setAlpha(alpha); 561cfdb096c31356a1d28a9c62b9f09b7c4b5e990f5Michael Jurka //} 562affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 5630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = false; 564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 566e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 5670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 568f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen protected void screenScrolled(int screenCenter) { 569f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 570f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 571e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung @Override 572e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void dispatchDraw(Canvas canvas) { 573f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int halfScreenSize = getMeasuredWidth() / 2; 574f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int screenCenter = mScrollX + halfScreenSize; 575f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 576f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (screenCenter != mLastScreenCenter) { 577f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen screenScrolled(screenCenter); 578f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen updateAdjacentPagesAlpha(); 579f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mLastScreenCenter = screenCenter; 580f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 5810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 582e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung // Find out which screens are visible; as an optimization we only call draw on them 5830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // As an optimization, this code assumes that all pages have the same width as the 0th 5840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page. 5850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 586c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka if (pageCount > 0) { 587d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka final int pageWidth = getScaledMeasuredWidth(getChildAt(0)); 588c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int screenWidth = getMeasuredWidth(); 589c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int x = getRelativeChildOffset(0) + pageWidth; 590c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int leftScreen = 0; 591c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int rightScreen = 0; 592c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka while (x <= mScrollX) { 593c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka leftScreen++; 594c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(leftScreen)) + mPageSpacing; 595c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 596c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = leftScreen; 597c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung while (x < mScrollX + screenWidth && rightScreen < pageCount) { 598c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen++; 599c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung if (rightScreen < pageCount) { 600c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung x += getScaledMeasuredWidth(getChildAt(rightScreen)) + mPageSpacing; 601c3f9f4fcbd3fba3753335d5c9671c0893393b164Winson Chung } 602c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 603c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = Math.min(getChildCount() - 1, rightScreen); 6040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 605c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final long drawingTime = getDrawingTime(); 60629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung // Clip to the bounds 60729d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.save(); 60829d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.clipRect(mScrollX, mScrollY, mScrollX + mRight - mLeft, 60929d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung mScrollY + mBottom - mTop); 61029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 611c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka for (int i = leftScreen; i <= rightScreen; i++) { 612c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka drawChild(canvas, getChildAt(i), drawingTime); 613c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 61429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung canvas.restore(); 6150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 617321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 618321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 619321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 62086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 62186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 62286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 626321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 63086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 63186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 63286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 633321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 63486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 635321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 63686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 638321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v.requestFocus(direction, previouslyFocusedRect); 639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 64686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 64786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 648321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 649321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 65186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 65286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 653321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 654321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 655321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 656321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 657321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 66186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 66286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 66586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 66686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 66986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 67086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 671321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 673321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 674321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 675321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 67786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 67986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 68086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 683321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 68486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 685321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 686321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 688321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 690321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 710321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 71186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getChildAt(mCurrentPage); 71286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 714321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 715321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 716321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 717d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 718d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the previous page. 719d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 720d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsPreviousPage(float x, float y) { 721d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing); 722d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 723d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 724d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy /** 725d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy * Return true if a tap at (x, y) should trigger a flip to the next page. 726d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy */ 727d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy protected boolean hitsNextPage(float x, float y) { 728d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing)); 729d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } 730d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy 731321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 732321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 73945e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 74045e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 74145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 753321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 7591ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka if (mActivePointerId != INVALID_POINTER) { 7601ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka determineScrollingStart(ev); 7611ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka break; 7621ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka } 7631ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 7641ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 7651ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // i.e. fall through to the next case (don't break) 7661ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 7671ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // while it's small- this was causing a crash before we checked for INVALID_POINTER) 768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 777c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 778aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 779321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 787fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 7885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 7895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 7905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 7915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 7925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 7935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 7945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 79686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 798d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 800d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy if (hitsPreviousPage(x, y)) { 801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 802d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (hitsNextPage(x, y)) { 803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 8111d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 8121d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown case MotionEvent.ACTION_CANCEL: 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 826321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 827321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 83080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 83180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 832228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung ObjectAnimator anim = (ObjectAnimator) AnimatorInflater. 833228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung loadAnimator(mContext, R.anim.paged_view_click_feedback); 834228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.setTarget(v); 835228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.addListener(new AnimatorListenerAdapter() { 836228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung public void onAnimationRepeat(Animator animation) { 83780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 83880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 83980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 840228a0faca6fd77f5cdd28a32ff20a311ec1bbffaWinson Chung anim.start(); 84180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 84280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 843f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev) { 844f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen determineScrollingStart(ev, 1.0f); 845f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 846f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 847321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 851f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { 852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 862f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final int touchSlop = Math.round(touchSlopScale * mTouchSlop); 863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 864321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 865321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 867f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (xMoved || xPaged || yMoved) { 8680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 869321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 872c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 8730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 8740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 8750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 878f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen cancelCurrentPageLongPress(); 879f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 880f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen } 881f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen 882f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen protected void cancelCurrentPageLongPress() { 883f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (mAllowLongPress) { 884f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen mAllowLongPress = false; 885f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // Try canceling the long press. It could also have been scheduled 886f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // by a distant descendant, so use the mAllowLongPress flag to block 887f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen // everything 888f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen final View currentPage = getPageAt(mCurrentPage); 889f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen if (currentPage != null) { 890f8d2823d885ba682140aee1ae0504c1c5e67a24bAdam Cohen currentPage.cancelLongPress(); 891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 895e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // This curve determines how the effect of scrolling over the limits of the page dimishes 896e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as the user pulls further and further from the bounds 897e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private float overScrollInfluenceCurve(float f) { 898e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 1.0f; 899e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return f * f * f + 1.0f; 900e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 901e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 90268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen protected void overScroll(float amount) { 903e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int screenSize = getMeasuredWidth(); 904e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 905e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float f = (amount / screenSize); 906e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 907e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (f == 0) return; 908e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 909e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 9107bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen // Clamp this factor, f, to -1 < f < 1 9117bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen if (Math.abs(f) >= 1) { 9127bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen f /= Math.abs(f); 9137bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen } 9147bfc979594b083c36f6a08e49273ef7c9ad7b13aAdam Cohen 915e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); 91668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (amount < 0) { 91768d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = overScrollAmount; 91868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 91968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScrollX = mMaxScrollX + overScrollAmount; 92068d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 92168d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 92268d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } 92368d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 924c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka protected float maxOverScroll() { 925c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not 926c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka // exceed). Used to find out how much extra wallpaper we need for the overscroll effect 927c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka float f = 1.0f; 928c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); 929c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka return OVERSCROLL_DAMP_FACTOR * f; 930c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka } 931c5b262ccf639fedac2aff5bb8238342f95396338Michael Jurka 932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 93445e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 93545e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onTouchEvent(ev); 93645e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 937b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka acquireVelocityTrackerAndAddMovement(ev); 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 945321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 948321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 953c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 954aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX = 0; 955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 9560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 9570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 9580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 966c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung final float deltaX = mLastMotionX + mLastMotionXRemainder - x; 967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 968aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen mTotalMotionX += Math.abs(deltaX); 969aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 970c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // Only scroll and update mLastMotionX if we have moved some discrete amount. We 971c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // keep the remainder because we are actually testing if we've moved from the last 972c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung // scrolled position (which is discrete). 973c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung if (Math.abs(deltaX) >= 1.0f) { 97468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mTouchX += deltaX; 97568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 97668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen if (!mDeferScrollUpdate) { 977c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung scrollBy((int) deltaX, 0); 97868d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen } else { 97968d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen invalidate(); 980321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 981c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionX = x; 982c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = deltaX - (int) deltaX; 983321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 984321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 986564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen } else { 987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 989321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 990321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 992321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 993321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 994321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 995321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 996321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 998321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 9999cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung final int deltaX = (int) (x - mDownMotionX); 1000b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE; 10010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 1002aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1003b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); 1004b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen 1005aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // In the case that the page is moved far to one direction and then is flung 1006aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // in the opposite direction, we use a threshold to determine whether we should 1007aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // just return to the starting page, or if we should skip one further. 1008aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen boolean returnToOriginalPage = false; 1009aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen final int pageWidth = getScaledMeasuredWidth(getChildAt(mCurrentPage)); 1010b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && 1011aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen Math.signum(velocityX) != Math.signum(deltaX)) { 1012aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen returnToOriginalPage = true; 1013aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } 1014aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1015b64cb5a44bedcff0ea4b09cf8f1f5b6f95b0244eAdam Cohen boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING && 1016aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen Math.abs(velocityX) > snapVelocity; 1017aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen 1018aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen int finalPage; 1019aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // We give flings precedence over large moves, which is why we short-circuit our 1020aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // test for a large move if a fling has been registered. That is, a large 1021aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen // move to the left and fling to the right will register as a fling to the right. 1022aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen if (((isSignificantMove && deltaX > 0 && !isFling) || 1023aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX > 0)) && mCurrentPage > 0) { 1024aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; 1025aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1026aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen } else if (((isSignificantMove && deltaX < 0 && !isFling) || 1027aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen (isFling && velocityX < 0)) && 102886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 1029aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1; 1030aefd4e1f519838852d9e870031a85c9ea55a05f3Adam Cohen snapToPageWithVelocity(finalPage, velocityX); 1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1033321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1034d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 1035321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1036321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1037321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 103886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 103986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 104086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1041321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1042321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1043321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1044d0ce1ec2e21ce3bb0ba3549a01d1d06b440a8e45Patrick Dubroy } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 1045321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 1046321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 1047321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 104886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 104986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 105086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 1051321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 1052321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 1053321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 10541d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } else { 10551d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 1056321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1058321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1059b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1060321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1061321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1062321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 1063b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 1064b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka snapToDestination(); 1065b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 1067321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 1068b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 1069321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1070321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1071321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 1072321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1075321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1076321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 1077321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1078321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1079b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { 1080b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker == null) { 1081b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = VelocityTracker.obtain(); 1082b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1083b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.addMovement(ev); 1084b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1085b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1086b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void releaseVelocityTracker() { 1087b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker != null) { 1088b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.recycle(); 1089b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = null; 1090b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1091b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 1092b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 1093321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 1094321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 1095321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 1096321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 1097321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 1098321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 1099321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 1100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 1101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 1103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 1104c0844aa8a135bbf56aad2f95f57d1354a1260078Winson Chung mLastMotionXRemainder = 0; 1105321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 1106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 1107321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 1108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 11101d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown if (mTouchState == TOUCH_STATE_REST) { 11111d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown onWallpaperTap(ev); 11121d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 11131d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown } 11141d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown 11151d0867c8de6a889bccde9b7e768daef182a25e5cJeff Brown protected void onWallpaperTap(MotionEvent ev) { 1116321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1117321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1118321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1119321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 1120321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 112186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 112286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page >= 0 && !isInTouchMode()) { 112386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1127e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getChildIndexForRelativeOffset(int relativeOffset) { 1128e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int childCount = getChildCount(); 11299c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int left; 11309c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int right; 1131e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung for (int i = 0; i < childCount; ++i) { 11329c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen left = getRelativeChildOffset(i); 1133d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka right = (left + getScaledMeasuredWidth(getChildAt(i))); 1134e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung if (left <= relativeOffset && relativeOffset <= right) { 1135e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return i; 1136e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1137e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1138e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return -1; 1139e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1140e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 1141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getRelativeChildOffset(int index) { 1142321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2; 1143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1144321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getChildOffset(int index) { 1146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) 1147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return 0; 1148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int offset = getRelativeChildOffset(0); 1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < index; ++i) { 1151d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka offset += getScaledMeasuredWidth(getChildAt(i)) + mPageSpacing; 1152321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1153321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return offset; 1154321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1156d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka protected int getScaledMeasuredWidth(View child) { 1157d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka return (int) (child.getMeasuredWidth() * mLayoutScale + 0.5f); 1158d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka } 1159d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka 1160d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen int getPageNearestToCenterOfScreen() { 1161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenter = getMeasuredWidth(); 1162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 1163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 1164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 1165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 11660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 1167d3ef3065ab0941567c45e9aec98783138b623c68Michael Jurka int childWidth = getScaledMeasuredWidth(layout); 1168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 1169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 1170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 1171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 1172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 1173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 1174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1176d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen return minDistanceFromScreenCenterIndex; 1177d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen } 1178d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen 1179d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen protected void snapToDestination() { 1180d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); 1181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1183e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen private static class ScrollInterpolator implements Interpolator { 1184e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public ScrollInterpolator() { 1185e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1186e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1187e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen public float getInterpolation(float t) { 1188e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen t -= 1.0f; 1189e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return t*t*t*t*t + 1; 1190e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1191e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1192e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1193e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // We want the duration of the page snap animation to be influenced by the distance that 1194e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // the screen has to travel, however, we don't want this duration to be effected in a 1195e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // purely linear fashion. Instead, we use this method to moderate the effect that the distance 1196e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // of travel has on the overall snap duration. 1197e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceInfluenceForSnapDuration(float f) { 1198e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f -= 0.5f; // center the values about 0. 1199e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen f *= 0.3f * Math.PI / 2.0f; 1200e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return (float) Math.sin(f); 1201e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1202e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 12030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 1204e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); 1205e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int halfScreenSize = getMeasuredWidth() / 2; 1206e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1207e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 1208e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int delta = newX - mUnboundedScrollX; 1209e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int duration = 0; 1210e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1211e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (Math.abs(velocity) < MIN_FLING_VELOCITY) { 1212e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // If the velocity is low enough, then treat this more as an automatic page advance 1213e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // as opposed to an apparent physical response to flinging 1214e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1215e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen return; 1216e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1217e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1218e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // Here we compute a "distance" that will be used in the computation of the overall 1219e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // snap duration. This is a function of the actual distance that needs to be traveled; 1220e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we keep this value close to half screen size in order to reduce the variance in snap 1221e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // duration as a function of the distance the page needs to travel. 1222e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distanceRatio = 1.0f * Math.abs(delta) / 2 * halfScreenSize; 1223e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen float distance = halfScreenSize + halfScreenSize * 1224e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen distanceInfluenceForSnapDuration(distanceRatio); 1225e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1226e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.abs(velocity); 1227e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); 1228e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1229e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // we want the page's snap velocity to approximately match the velocity at which the 1230e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // user flings, so we scale the duration by a value near to the derivative of the scroll 1231e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // interpolator at zero, ie. 5. We use 6 to make it a little slower. 1232e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen duration = 6 * Math.round(1000 * Math.abs(distance / velocity)); 1233e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen 1234e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPage(whichPage, delta, duration); 12350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 12360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 12385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 12410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 124286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 1243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 124486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 124568d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen final int sX = mUnboundedScrollX; 1246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 12470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 12480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 12490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 12510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 12520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 12540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 12550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild == getChildAt(mCurrentPage)) { 12560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 12570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 12580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 1261321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 1262321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 1263321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1265321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 126668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); 126780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 126880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // only load some associated pages 126986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung loadAssociatedPages(mNextPage); 12700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 1271321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 1272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 1275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 127686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 1277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 127886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 1279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 1283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 128486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 1285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 128686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 1287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 129086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 1292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 1293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 1294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 1295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 1296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (vp == getChildAt(i)) { 1297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 1298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 1302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 1305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 1306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 1308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 1309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 13110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 13120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 13130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 13140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 13150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 13160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 13170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1319321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 132086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 1321321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 1323321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 1324321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1326321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 1327321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 132886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 1329321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1330321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1331321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1332321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 1333321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 133486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 1335321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1336321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 1338321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 1339321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 1340321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 1341321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1342321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 1344321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 1345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 1347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1348321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 134986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void loadAssociatedPages(int page) { 13500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 13510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 13520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 1353e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int lowerPageBound = getAssociatedLowerPageBound(page); 1354e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int upperPageBound = getAssociatedUpperPageBound(page); 13550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 13568245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 13578245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int childCount = layout.getPageChildCount(); 13580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 13590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 13600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPageItems(i); 13610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 13620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 13640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (childCount > 0) { 13658245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka layout.removeAllViewsOnPage(); 13660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 13670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, true); 136880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 136980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 137080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 137180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 137280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 137380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1374e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedLowerPageBound(int page) { 1375e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.max(0, page - 1); 1376e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1377e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedUpperPageBound(int page) { 1378e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int count = getChildCount(); 1379e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.min(page + 1, count - 1); 1380e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1381e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 13825f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void startChoiceMode(int mode, ActionMode.Callback callback) { 1383430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy if (isChoiceMode(CHOICE_MODE_NONE)) { 1384430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mChoiceMode = mode; 1385430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mActionMode = startActionMode(callback); 1386430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy } 13875f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13885f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13892b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public void endChoiceMode() { 13905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (!isChoiceMode(CHOICE_MODE_NONE)) { 13915f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 13925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung resetCheckedGrandchildren(); 1393e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka if (mActionMode != null) mActionMode.finish(); 13949f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy mActionMode = null; 13955f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13965f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 13975f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 13985f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected boolean isChoiceMode(int mode) { 13995f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return mChoiceMode == mode; 14005f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14015f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 14025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected ArrayList<Checkable> getCheckedGrandchildren() { 14035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung ArrayList<Checkable> checked = new ArrayList<Checkable>(); 14045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int childCount = getChildCount(); 14055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < childCount; ++i) { 14068245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 14078245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int grandChildCount = layout.getPageChildCount(); 14085f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int j = 0; j < grandChildCount; ++j) { 14098245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final View v = layout.getChildOnPageAt(j); 14109f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 14115f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung checked.add((Checkable) v); 14125f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14135f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14145f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14155f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return checked; 14165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 14189f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy /** 14199f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * If in CHOICE_MODE_SINGLE and an item is checked, returns that item. 14209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * Otherwise, returns null. 14219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy */ 14229f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy protected Checkable getSingleCheckedGrandchild() { 14236f13342ffd3f968de9ff86b988621cc91d94adffPatrick Dubroy if (mChoiceMode != CHOICE_MODE_MULTIPLE) { 14249f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int childCount = getChildCount(); 14259f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int i = 0; i < childCount; ++i) { 14268245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka Page layout = (Page) getChildAt(i); 14278245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final int grandChildCount = layout.getPageChildCount(); 14289f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int j = 0; j < grandChildCount; ++j) { 14298245a8685fc9f4802c9fa29a989854b2522fc588Michael Jurka final View v = layout.getChildOnPageAt(j); 14309f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 14319f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return (Checkable) v; 14329f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 14339f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 14349f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 14359f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 14369f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return null; 14379f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 14389f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 14395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void resetCheckedGrandchildren() { 14405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // loop through children, and set all of their children to _not_ be checked 14415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final ArrayList<Checkable> checked = getCheckedGrandchildren(); 14425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < checked.size(); ++i) { 14435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final Checkable c = checked.get(i); 14445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung c.setChecked(false); 14455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 14475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 1448a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung public void setRestorePage(int restorePage) { 1449a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung mRestorePage = restorePage; 1450a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung } 1451a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung 145286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 145386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 145486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 145586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 145686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 145786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1458321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 145986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 146086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 146186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 146286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 146386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1464321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPageItems(int page); 146586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void invalidatePageData() { 14670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 14680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 14690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 147086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 14710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 14720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 14730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 14740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 14750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 14760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 147786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1478a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung // Use the restore page if necessary 1479a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung if (mRestorePage > -1) { 1480a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung mCurrentPage = mRestorePage; 1481a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung mRestorePage = -1; 1482a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung } 1483a12a2502e6a448d36ab7b8de46de0c1afe40b34fWinson Chung 14840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 14850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka loadAssociatedPages(mCurrentPage); 14860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = true; 1487b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung updateAdjacentPagesAlpha(); 14880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 14890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1492