PagedView.java revision 0142d49e1378a7155bcca1fb59965d9e73016dbc
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 19321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport java.util.ArrayList; 2086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chungimport java.util.Arrays; 21241c3b451d7841ba08247beea784953eca4e8582Winson Chungimport java.util.HashMap; 22321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 23321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context; 24241c3b451d7841ba08247beea784953eca4e8582Winson Chungimport android.graphics.Bitmap; 25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas; 26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect; 27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel; 28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable; 29321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet; 30321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 31321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 32321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 3680baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation; 3780baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation.AnimationListener; 3886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chungimport android.view.animation.AnimationUtils; 39321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 40321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4180baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport com.android.launcher.R; 4280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 43321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/** 44321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a 450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages" 46321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup { 48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final String TAG = "PagedView"; 490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final int INVALID_PAGE = -1; 50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 5186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // the min drag distance for a fling to register, to prevent random page shifts 52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final int MIN_LENGTH_FOR_FLING = 50; 53321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 61321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 67321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 68321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 69321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 70321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mLastMotionX; 71321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mLastMotionY; 72321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 77321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 79321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 81321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 82321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private boolean mAllowLongPress = true; 83321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 84321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mTouchSlop; 85321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 88321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final int INVALID_POINTER = -1; 89321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mActivePointerId = INVALID_POINTER; 91321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka private enum PageMovingState { PAGE_BEGIN_MOVING, PAGE_END_MOVING }; 9386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka private PageMovingListener mPageMovingListener; 95321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 9686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private ArrayList<Boolean> mDirtyPageContent; 9786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private boolean mDirtyPageAlpha; 98321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 99241c3b451d7841ba08247beea784953eca4e8582Winson Chung protected PagedViewIconCache mPageViewIconCache; 100241c3b451d7841ba08247beea784953eca4e8582Winson Chung 1010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 115241c3b451d7841ba08247beea784953eca4e8582Winson Chung /** 116241c3b451d7841ba08247beea784953eca4e8582Winson Chung * Simple cache mechanism for PagedViewIcon outlines. 117241c3b451d7841ba08247beea784953eca4e8582Winson Chung */ 118241c3b451d7841ba08247beea784953eca4e8582Winson Chung class PagedViewIconCache { 119241c3b451d7841ba08247beea784953eca4e8582Winson Chung private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>(); 120241c3b451d7841ba08247beea784953eca4e8582Winson Chung 121241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void clear() { 122241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.clear(); 123241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 124241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void addOutline(Object key, Bitmap b) { 125241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.put(key, b); 126241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 127241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void removeOutline(Object key) { 128241c3b451d7841ba08247beea784953eca4e8582Winson Chung if (iconOutlineCache.containsKey(key)) { 129241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.remove(key); 130241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 131241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 132241c3b451d7841ba08247beea784953eca4e8582Winson Chung public Bitmap getOutline(Object key) { 133241c3b451d7841ba08247beea784953eca4e8582Winson Chung return iconOutlineCache.get(key); 134241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 135241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 136241c3b451d7841ba08247beea784953eca4e8582Winson Chung 13786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 13886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public interface PageMovingListener { 1420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka void onPageBeginMoving(); 1430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka void onPageEndMoving(); 1440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 151321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 152321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 153321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 154321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 155321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 156321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 1580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 16586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 16686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 167241c3b451d7841ba08247beea784953eca4e8582Winson Chung mPageViewIconCache = new PagedViewIconCache(); 168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller = new Scroller(getContext()); 16986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 176321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 17786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 17886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 17986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 18086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 181321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 183321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 18586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 186321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 18786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 18986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 19086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 191321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 192321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 19386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 19786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 199321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int getScrollWidth() { 202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getWidth(); 203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 205321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 20686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 20886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) return; 211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 21286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 21386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung scrollTo(getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage), 0); 21480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 21686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 22086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 22186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 224321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void pageBeginMoving() { 2270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void pageEndMoving() { 2310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 23486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 239321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 24186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 24386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 2480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 2490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka super.scrollTo(x, y); 2500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 2510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 2520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 2550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 257321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 2580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 2590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 26086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 26186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 26286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 2630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 2640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageEndMoving(); 2650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 266321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 2670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 2680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 2710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 2720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 273321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 276321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 277321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 282321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 283321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 284321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (heightMode != MeasureSpec.EXACTLY) { 286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setMeasuredDimension(widthSize, heightSize); 296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mFirstLayout) { 298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(false); 29986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung scrollTo(mCurrentPage * widthSize, 0); 3000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mScroller.setFinalX(mCurrentPage * widthSize); 301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(true); 302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mFirstLayout = false; 303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 311321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung childLeft = (getMeasuredWidth() - getChildAt(0).getMeasuredWidth()) / 2; 312321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 313321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 314321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 315321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final View child = getChildAt(i); 316321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 317321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childWidth = child.getMeasuredWidth(); 318321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); 319321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung childLeft += childWidth; 320321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 321321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 323321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 324321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void dispatchDraw(Canvas canvas) { 3260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 3270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { 3280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int screenCenter = mScrollX + (getMeasuredWidth() / 2); 3290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = getChildCount(); 3300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < childCount; ++i) { 3310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 3320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childWidth = layout.getMeasuredWidth(); 3330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int halfChildWidth = (childWidth / 2); 3340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childCenter = getChildOffset(i) + halfChildWidth; 3350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 3360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka float alpha = 0.0f; 3370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (distanceFromScreenCenter < halfChildWidth) { 3380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka alpha = 1.0f; 3390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else if (distanceFromScreenCenter > childWidth) { 3400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka alpha = 0.0f; 3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 3420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka float dimAlpha = (float) (distanceFromScreenCenter - halfChildWidth) / halfChildWidth; 3430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); 3440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka alpha = 1.0f - dimAlpha; 3450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (Float.compare(alpha, layout.getAlpha()) != 0) { 3470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.setAlpha(alpha); 3480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 349affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 3500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = false; 351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 352321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 3530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Find out which screens are visible; as an optimization we only call draw on them 3550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // As an optimization, this code assumes that all pages have the same width as the 0th 3570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page. 3580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageWidth = getChildAt(0).getMeasuredWidth(); 3590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 3600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int screenWidth = getMeasuredWidth(); 3610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int x = getRelativeChildOffset(0) + pageWidth; 3620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int leftScreen = 0; 3630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int rightScreen = 0; 3640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka while (x <= mScrollX) { 3650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka leftScreen++; 3660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka x += pageWidth; 3670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // replace above line with this if you don't assume all pages have same width as 0th 3680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page: 3690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // x += getChildAt(leftScreen).getMeasuredWidth(); 3700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka rightScreen = leftScreen; 3720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka while (x < mScrollX + screenWidth) { 3730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka rightScreen++; 3740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka x += pageWidth; 3750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // replace above line with this if you don't assume all pages have same width as 0th 3760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page: 3770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka //if (rightScreen < pageCount) { 3780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // x += getChildAt(rightScreen).getMeasuredWidth(); 3790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka //} 3800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka rightScreen = Math.min(getChildCount() - 1, rightScreen); 3820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final long drawingTime = getDrawingTime(); 3840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = leftScreen; i <= rightScreen; i++) { 3850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka drawChild(canvas, getChildAt(i), drawingTime); 3860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 388321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 389321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 390321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 39186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 39286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 39386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 394321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 395321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 396321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 397321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 40186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 40286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 40386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 40586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 40786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 408321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 409321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v.requestFocus(direction, previouslyFocusedRect); 410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 411321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 412321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 413321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 414321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 415321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 416321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 41786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 41886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 42286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 42386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 424321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 425321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 428321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 429321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 430321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 431321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 43286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 43386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 434321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 435321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 43686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 43786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 438321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 439321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 44086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 44186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 442321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 444321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 445321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 446321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 447321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 44886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 449321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 45086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 45186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 452321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 453321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 45586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 456321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 457321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 458321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 459321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 460321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 461321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 462321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 463321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 464321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 467321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 471321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 476321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 477321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 480321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 48286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getChildAt(mCurrentPage); 48386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 485321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 486321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 487321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 488321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 494321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 495321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 496321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 497321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 499321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 500321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 501321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 502321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 503321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 504321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 505321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 506321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 507321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 508321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 509321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 510321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 511321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 512321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 513321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 515321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 516321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 517321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 518321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 519321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 520321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 521321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 522321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 523321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 524321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 528321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 529321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 530321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; 534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 53586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 536321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 537321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((mTouchState != TOUCH_STATE_PREV_PAGE) && 538321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState != TOUCH_STATE_NEXT_PAGE)) { 539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int relativeChildLeft = getChildOffset(0); 541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int relativeChildRight = relativeChildLeft + getChildAt(0).getMeasuredWidth(); 542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (x < relativeChildLeft) { 543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (x > relativeChildRight) { 545321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 546321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 547321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 548321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 549321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 550321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 554321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Release the drag 5550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageEndMoving(); 556321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 557321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 558321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 559321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 560321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 561321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 562321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 563321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 568321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 569321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 570321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 573321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 57480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 57580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 57680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung Animation anim = AnimationUtils.loadAnimation(getContext(), 57780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung R.anim.paged_view_click_feedback); 57880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung anim.setAnimationListener(new AnimationListener() { 57980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 58080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationStart(Animation animation) {} 58180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 58280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationRepeat(Animation animation) { 58380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 58480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 58580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 58680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationEnd(Animation animation) {} 58780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 58880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung v.startAnimation(anim); 58980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 59080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 591321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 592321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 593321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 594321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 595321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void determineScrollingStart(MotionEvent ev) { 596321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 597321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 598321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 599321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 600321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 601321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 602321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 603321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 604321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 605321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 606321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int touchSlop = mTouchSlop; 607321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 608321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 609321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 610321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 611321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (xMoved || yMoved) { 6120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 613321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 614321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 6160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 6170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 6180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 619321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 620321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 621321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mAllowLongPress) { 622321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Try canceling the long press. It could also have been scheduled 624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // by a distant descendant, so use the mAllowLongPress flag to block 625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // everything 62686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getPageAt(mCurrentPage); 62786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 631321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 633321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 634321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker == null) { 635321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker = VelocityTracker.obtain(); 636321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.addMovement(ev); 638321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 646321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 647321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 648321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 649321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 651321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 652321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 653321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 6540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 6550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 6560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 657321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 661321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int deltaX = (int) (mLastMotionX - x); 665321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 666321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int sx = getScrollX(); 668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (deltaX < 0) { 669321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (sx > 0) { 6700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX += Math.max(-mTouchX, deltaX); 6710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 6720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (!mDeferScrollUpdate) { 6730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka scrollBy(Math.max(-sx, deltaX), 0); 6740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 6750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // This will trigger a call to computeScroll() on next drawChild() call 6760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 6770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 679321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (deltaX > 0) { 680321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int lastChildIndex = getChildCount() - 1; 681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int availableToScroll = getChildOffset(lastChildIndex) - 682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung getRelativeChildOffset(lastChildIndex) - sx; 683321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (availableToScroll > 0) { 6840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX += Math.min(availableToScroll, deltaX); 6850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 6860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (!mDeferScrollUpdate) { 6870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka scrollBy(Math.min(availableToScroll, deltaX), 0); 6880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 6890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // This will trigger a call to computeScroll() on next drawChild() call 6900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 6910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 693321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 694321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 695321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if ((mTouchState == TOUCH_STATE_PREV_PAGE) || 697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_NEXT_PAGE)) { 698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 710321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean isfling = Math.abs(mDownMotionX - x) > MIN_LENGTH_FOR_FLING; 711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 7120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 7130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (isfling && velocityX > snapVelocity && mCurrentPage > 0) { 7140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage - 1, velocityX); 7150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else if (isfling && velocityX < -snapVelocity && 71686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 7170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage + 1, velocityX); 718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.recycle(); 724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker = null; 725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 726321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 727321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 728321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 729321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 73086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 73186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 73286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 733321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 734321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 735321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 736321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 74086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 74186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 74286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 753321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 777321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 778321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 779321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 78586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 78686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page >= 0 && !isInTouchMode()) { 78786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 791321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getRelativeChildOffset(int index) { 792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2; 793321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getChildOffset(int index) { 796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) 797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return 0; 798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int offset = getRelativeChildOffset(0); 800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < index; ++i) { 801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung offset += getChildAt(i).getMeasuredWidth(); 802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return offset; 804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void snapToDestination() { 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenter = getMeasuredWidth(); 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 8120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childWidth = layout.getMeasuredWidth(); 814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 82286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(minDistanceFromScreenCenterIndex, 1000); 823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 8250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 8260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // We ignore velocity in this implementation, but children (e.g. SmoothPagedView) 8270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // can use it 8280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage); 8290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 8300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 8310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 83286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(whichPage, 1000); 833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 8350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 83686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 83986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int sX = getScrollX(); 841321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 8420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 8430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 8440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 8450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 8460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 8470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 8480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 8490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 8500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild == getChildAt(mCurrentPage)) { 8510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 8520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 8530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 8540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 8610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mScroller.startScroll(getScrollX(), 0, delta, 0, duration); 86280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 86380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // only load some associated pages 86486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung loadAssociatedPages(mNextPage); 8650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 866321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 867321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 868321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 869321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 870321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected Parcelable onSaveInstanceState() { 871321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final SavedState state = new SavedState(super.onSaveInstanceState()); 87286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung state.currentPage = mCurrentPage; 873321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return state; 874321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 875321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onRestoreInstanceState(Parcelable state) { 878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState savedState = (SavedState) state; 879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.onRestoreInstanceState(savedState.getSuperState()); 88086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (savedState.currentPage != -1) { 88186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = savedState.currentPage; 882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 885321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 886321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 88786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 88986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 891321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 89586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 89786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 898321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 899321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 90186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 904321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 906321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (vp == getChildAt(i)) { 908321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 909321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 910321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 911321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 912321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 913321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 918321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 919321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 920321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 9220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 9230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 9240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 9250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 9260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 9270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 9280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 9290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 93186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 935321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 936321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 93986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 94586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 948321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 954321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 956321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 96086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void loadAssociatedPages(int page) { 9610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 9620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 9630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 9640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int lowerPageBound = Math.max(0, page - 1); 9650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int upperPageBound = Math.min(page + 1, count - 1); 9660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 9670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final ViewGroup layout = (ViewGroup) getChildAt(i); 9680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = layout.getChildCount(); 9690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 9700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 9710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPageItems(i); 9720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 9730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 9740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 9750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (childCount > 0) { 9760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.removeAllViews(); 9770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 9780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, true); 97980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 98080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 98180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 98280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 98380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 98480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 98586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 98686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 98786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 98886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 98986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 99086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 99286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 99386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 99486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 99586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 99686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPageItems(int page); 99886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void invalidatePageData() { 10000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 10010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 10020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 100386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 10040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 10050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 10060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 10070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 10080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 10090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 101086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 10110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 10120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka loadAssociatedPages(mCurrentPage); 10130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = true; 10140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 10150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1017321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1018