PagedView.java revision 5f2aa4efeeb8b0133d891715d71553138d9f9ca7
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 Chungimport android.content.Context; 23241c3b451d7841ba08247beea784953eca4e8582Winson Chungimport android.graphics.Bitmap; 24321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Canvas; 25321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.graphics.Rect; 26321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcel; 27321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.os.Parcelable; 28321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.util.AttributeSet; 295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.util.Log; 305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode; 31321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 32321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 3780baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation; 3880baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation.AnimationListener; 3986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chungimport android.view.animation.AnimationUtils; 405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable; 41321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 42321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 4380baf5a6b3c62a62265f626d43d1167783c94131Winson 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 54321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final int MIN_LENGTH_FOR_FLING = 50; 55321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private static final int PAGE_SNAP_ANIMATION_DURATION = 1000; 570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 64321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 70321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 71321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 72321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 73321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mLastMotionX; 74321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mLastMotionY; 75321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 80321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 82321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 84321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 85321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private boolean mAllowLongPress = true; 86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 87321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mTouchSlop; 88321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 89321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 91321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final int INVALID_POINTER = -1; 92321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 93321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mActivePointerId = INVALID_POINTER; 94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka private enum PageMovingState { PAGE_BEGIN_MOVING, PAGE_END_MOVING }; 9686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka private PageMovingListener mPageMovingListener; 98321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 9986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private ArrayList<Boolean> mDirtyPageContent; 10086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private boolean mDirtyPageAlpha; 101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // choice modes 1035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_NONE = 0; 1045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_SINGLE = 1; 1055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // Multiple selection mode is not supported by all Launcher actions atm 1065f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_MULTIPLE = 2; 1075f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private int mChoiceMode; 1085f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private ActionMode mActionMode; 1095f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 110241c3b451d7841ba08247beea784953eca4e8582Winson Chung protected PagedViewIconCache mPageViewIconCache; 111241c3b451d7841ba08247beea784953eca4e8582Winson Chung 1120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 126241c3b451d7841ba08247beea784953eca4e8582Winson Chung /** 127241c3b451d7841ba08247beea784953eca4e8582Winson Chung * Simple cache mechanism for PagedViewIcon outlines. 128241c3b451d7841ba08247beea784953eca4e8582Winson Chung */ 129241c3b451d7841ba08247beea784953eca4e8582Winson Chung class PagedViewIconCache { 130241c3b451d7841ba08247beea784953eca4e8582Winson Chung private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>(); 131241c3b451d7841ba08247beea784953eca4e8582Winson Chung 132241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void clear() { 133241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.clear(); 134241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 135241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void addOutline(Object key, Bitmap b) { 136241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.put(key, b); 137241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 138241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void removeOutline(Object key) { 139241c3b451d7841ba08247beea784953eca4e8582Winson Chung if (iconOutlineCache.containsKey(key)) { 140241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.remove(key); 141241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 142241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 143241c3b451d7841ba08247beea784953eca4e8582Winson Chung public Bitmap getOutline(Object key) { 144241c3b451d7841ba08247beea784953eca4e8582Winson Chung return iconOutlineCache.get(key); 145241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 146241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 147241c3b451d7841ba08247beea784953eca4e8582Winson Chung 14886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 14986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 151321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public interface PageMovingListener { 1530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka void onPageBeginMoving(); 1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka void onPageEndMoving(); 1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 157321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 158321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 159321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 160321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 161321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 162321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 163321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 164321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 165321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 166321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 1675f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 1700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 17786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 17886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 179241c3b451d7841ba08247beea784953eca4e8582Winson Chung mPageViewIconCache = new PagedViewIconCache(); 180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller = new Scroller(getContext()); 18186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 182321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 183321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 184321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 185321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 186321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 187321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 188321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 18986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 19086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 19186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 19286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 193321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 194321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 195321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 196321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 19786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 19986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 20186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 20286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 20586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 206321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 207321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 208321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 20986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 210321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 211321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int getScrollWidth() { 214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getWidth(); 215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 21886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 219321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 22086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 221321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 222321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) return; 223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 22486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 22586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung scrollTo(getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage), 0); 22680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 227321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 22886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 229321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 23286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 23386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 235321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void pageBeginMoving() { 2390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void pageEndMoving() { 2430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 24686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 248321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 249321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 250321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 251321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 252321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 25386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 254321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 25586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 256321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 257321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 258321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 259321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 2600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 2610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka super.scrollTo(x, y); 2620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 2630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 2640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 2670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 269321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 2700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 2710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 27286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 27386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 27486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 2750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 2760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageEndMoving(); 2770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 278321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 2790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 2800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 2830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 2840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 285321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 286321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 287321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 288321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 289321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 292321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 293321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 294321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 295321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 296321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 297321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (heightMode != MeasureSpec.EXACTLY) { 298321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 299321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 301321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setMeasuredDimension(widthSize, heightSize); 308321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mFirstLayout) { 310321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(false); 31186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung scrollTo(mCurrentPage * widthSize, 0); 3120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mScroller.setFinalX(mCurrentPage * widthSize); 313321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(true); 314321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mFirstLayout = false; 315321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 316321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 317321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 318321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 319321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 320321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 321321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 322321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 323321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung childLeft = (getMeasuredWidth() - getChildAt(0).getMeasuredWidth()) / 2; 324321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 326321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 327321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final View child = getChildAt(i); 328321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 329321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childWidth = child.getMeasuredWidth(); 330321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); 331321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung childLeft += childWidth; 332321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 333321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 334321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 335321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 336321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 337321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void dispatchDraw(Canvas canvas) { 3380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 3390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { 3400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int screenCenter = mScrollX + (getMeasuredWidth() / 2); 3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = getChildCount(); 3420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < childCount; ++i) { 3430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 3440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childWidth = layout.getMeasuredWidth(); 3450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int halfChildWidth = (childWidth / 2); 3460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childCenter = getChildOffset(i) + halfChildWidth; 3470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 3480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka float alpha = 0.0f; 3490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (distanceFromScreenCenter < halfChildWidth) { 3500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka alpha = 1.0f; 3510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else if (distanceFromScreenCenter > childWidth) { 3520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka alpha = 0.0f; 3530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 3540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka float dimAlpha = (float) (distanceFromScreenCenter - halfChildWidth) / halfChildWidth; 3550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); 3560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka alpha = 1.0f - dimAlpha; 3570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (Float.compare(alpha, layout.getAlpha()) != 0) { 3590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.setAlpha(alpha); 3600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 361affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 3620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = false; 363321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 364321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 3650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Find out which screens are visible; as an optimization we only call draw on them 3670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // As an optimization, this code assumes that all pages have the same width as the 0th 3690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page. 3700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageWidth = getChildAt(0).getMeasuredWidth(); 3710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 3720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int screenWidth = getMeasuredWidth(); 3730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int x = getRelativeChildOffset(0) + pageWidth; 3740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int leftScreen = 0; 3750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int rightScreen = 0; 3760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka while (x <= mScrollX) { 3770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka leftScreen++; 3780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka x += pageWidth; 3790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // replace above line with this if you don't assume all pages have same width as 0th 3800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page: 3810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // x += getChildAt(leftScreen).getMeasuredWidth(); 3820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka rightScreen = leftScreen; 3840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka while (x < mScrollX + screenWidth) { 3850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka rightScreen++; 3860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka x += pageWidth; 3870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // replace above line with this if you don't assume all pages have same width as 0th 3880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page: 3890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka //if (rightScreen < pageCount) { 3900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // x += getChildAt(rightScreen).getMeasuredWidth(); 3910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka //} 3920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka rightScreen = Math.min(getChildCount() - 1, rightScreen); 3940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final long drawingTime = getDrawingTime(); 3960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = leftScreen; i <= rightScreen; i++) { 3970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka drawChild(canvas, getChildAt(i), drawingTime); 3980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 402321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 40386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 40486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 40586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 407321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 408321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 409321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 411321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 412321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 41386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 41486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 41586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 416321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 41786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 418321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 41986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 421321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v.requestFocus(direction, previouslyFocusedRect); 422321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 423321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 424321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 425321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 426321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 427321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 428321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 42986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 43086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 431321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 432321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 433321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 43486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 43586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 436321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 437321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 438321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 439321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 440321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 441321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 442321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 443321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 44486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 44586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 446321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 447321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 44886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 44986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 450321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 451321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 45286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 45386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 454321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 455321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 456321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 457321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 458321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 459321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 46086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 461321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 46286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 46386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 464321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 465321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 466321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 46786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 468321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 469321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 470321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 471321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 474321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 475321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 476321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 477321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 478321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 479321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 480321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 481321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 482321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 483321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 484321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 485321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 486321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 487321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 488321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 489321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 490321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 491321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 492321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 493321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 49486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getChildAt(mCurrentPage); 49586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 496321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 497321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 498321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 499321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 500321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 501321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 502321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 503321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 504321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 505321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 506321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 507321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 508321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 509321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 510321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 511321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 512321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 513321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 514321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 515321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 516321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 517321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 518321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 519321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 520321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 521321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 522321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 523321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 524321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 528321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 529321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 530321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 536321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 537321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 538321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 539321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 540321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 542321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 544321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 5455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int xDist = (mScroller.getFinalX() - mScroller.getCurrX()); 5465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 5475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 5485f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 5495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 5505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 5515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 5525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 55486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 555321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 556321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((mTouchState != TOUCH_STATE_PREV_PAGE) && 557321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState != TOUCH_STATE_NEXT_PAGE)) { 558321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 559321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int relativeChildLeft = getChildOffset(0); 560321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int relativeChildRight = relativeChildLeft + getChildAt(0).getMeasuredWidth(); 561321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (x < relativeChildLeft) { 562321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 563321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (x > relativeChildRight) { 564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 568321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 569321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 570321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 573321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Release the drag 5740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageEndMoving(); 575321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 576321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 577321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 578321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 579321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 580321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 581321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 582321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 583321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 584321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 585321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 586321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 587321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 588321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 589321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 590321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 591321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 592321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 59380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 59480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 59580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung Animation anim = AnimationUtils.loadAnimation(getContext(), 59680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung R.anim.paged_view_click_feedback); 59780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung anim.setAnimationListener(new AnimationListener() { 59880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 59980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationStart(Animation animation) {} 60080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 60180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationRepeat(Animation animation) { 60280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 60380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 60480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 60580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationEnd(Animation animation) {} 60680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 60780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung v.startAnimation(anim); 60880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 60980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 610321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 611321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 612321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 613321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 614321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void determineScrollingStart(MotionEvent ev) { 615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 617321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 618321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 619321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 620321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 621321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 622321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int touchSlop = mTouchSlop; 626321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (xMoved || yMoved) { 6310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 633321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 634321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 6350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 6360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 6370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 638321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mAllowLongPress) { 641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Try canceling the long press. It could also have been scheduled 643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // by a distant descendant, so use the mAllowLongPress flag to block 644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // everything 64586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getPageAt(mCurrentPage); 64686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 647321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 648321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 649321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 651321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 652321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 653321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker == null) { 654321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker = VelocityTracker.obtain(); 655321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 656321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.addMovement(ev); 657321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 658321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 659321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 660321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 661321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 665321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 666321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 669321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 670321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 671321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 6730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 6740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 6750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 677321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 679321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 680321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 681321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 682321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 683321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int deltaX = (int) (mLastMotionX - x); 684321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 685321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 686321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int sx = getScrollX(); 687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (deltaX < 0) { 688321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (sx > 0) { 6890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX += Math.max(-mTouchX, deltaX); 6900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 6910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (!mDeferScrollUpdate) { 6920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka scrollBy(Math.max(-sx, deltaX), 0); 6930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 6940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // This will trigger a call to computeScroll() on next drawChild() call 6950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 6960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 697321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (deltaX > 0) { 699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int lastChildIndex = getChildCount() - 1; 700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int availableToScroll = getChildOffset(lastChildIndex) - 701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung getRelativeChildOffset(lastChildIndex) - sx; 702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (availableToScroll > 0) { 7030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX += Math.min(availableToScroll, deltaX); 7040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 7050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (!mDeferScrollUpdate) { 7060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka scrollBy(Math.min(availableToScroll, deltaX), 0); 7070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 7080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // This will trigger a call to computeScroll() on next drawChild() call 7090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 7100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 712321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 714321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 715321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if ((mTouchState == TOUCH_STATE_PREV_PAGE) || 716321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_NEXT_PAGE)) { 717321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 724321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 725321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 726321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 727321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 728321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 729321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean isfling = Math.abs(mDownMotionX - x) > MIN_LENGTH_FOR_FLING; 730321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 7310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 7320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (isfling && velocityX > snapVelocity && mCurrentPage > 0) { 7330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage - 1, velocityX); 7340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else if (isfling && velocityX < -snapVelocity && 73586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 7360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage + 1, velocityX); 737321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 738321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 739321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 740321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 741321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.recycle(); 743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker = null; 744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 745321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 74986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 75086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 75186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 753321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 75986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 76086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 76186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 762321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 766321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 767321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 768321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 776321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 777321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 778321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 779321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 784321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 785321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 786321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 787321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 790321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 791321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 792321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 793321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 80486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 80586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page >= 0 && !isInTouchMode()) { 80686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 810321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getRelativeChildOffset(int index) { 811321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2; 812321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getChildOffset(int index) { 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) 816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return 0; 817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int offset = getRelativeChildOffset(0); 819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < index; ++i) { 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung offset += getChildAt(i).getMeasuredWidth(); 821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return offset; 823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void snapToDestination() { 826321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenter = getMeasuredWidth(); 827321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 828321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 829321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 830321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 8310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 832321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childWidth = layout.getMeasuredWidth(); 833321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 836321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 840321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 8415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(minDistanceFromScreenCenterIndex, PAGE_SNAP_ANIMATION_DURATION); 842321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 843321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 8440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 8450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // We ignore velocity in this implementation, but children (e.g. SmoothPagedView) 8460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // can use it 8470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage); 8480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 8490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 8500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 8515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 852321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 8540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 85586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 85886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int sX = getScrollX(); 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 8610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 8620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 8630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 8640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 8650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 8660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 8670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 8680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 8690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild == getChildAt(mCurrentPage)) { 8700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 8710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 8720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 8730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 874321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 875321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 876321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 877321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 8800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mScroller.startScroll(getScrollX(), 0, delta, 0, duration); 88180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 88280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // only load some associated pages 88386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung loadAssociatedPages(mNextPage); 8840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 885321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 886321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 887321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected Parcelable onSaveInstanceState() { 890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final SavedState state = new SavedState(super.onSaveInstanceState()); 89186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung state.currentPage = mCurrentPage; 892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return state; 893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 895321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 896321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onRestoreInstanceState(Parcelable state) { 897321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState savedState = (SavedState) state; 898321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.onRestoreInstanceState(savedState.getSuperState()); 89986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (savedState.currentPage != -1) { 90086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = savedState.currentPage; 901321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 904321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 90686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 90886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 909321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 910321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 911321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 912321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 913321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 91486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 91686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 918321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 919321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 92086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 922321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 924321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 925321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 926321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (vp == getChildAt(i)) { 927321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 928321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 929321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 930321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 931321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 932321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 933321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 934321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 935321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 936321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 937321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 9410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 9420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 9430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 9440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 9450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 9460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 9470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 9480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 95086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 954321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 956321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 95886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 959321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 960321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 961321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 96486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 965321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 966321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 967321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 968321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 969321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 970321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 971321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 972321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 973321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 974321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 975321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 976321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 977321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 978321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 97986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void loadAssociatedPages(int page) { 9800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 9810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 9820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 9830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int lowerPageBound = Math.max(0, page - 1); 9840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int upperPageBound = Math.min(page + 1, count - 1); 9850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 9860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final ViewGroup layout = (ViewGroup) getChildAt(i); 9870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = layout.getChildCount(); 9880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 9890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 9900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPageItems(i); 9910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 9920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 9930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 9940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (childCount > 0) { 9950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.removeAllViews(); 9960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 9970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, true); 99880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 99980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 100080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 100180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 100280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 100380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 10045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void startChoiceMode(int mode, ActionMode.Callback callback) { 10055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // StartActionMode may call through toendChoiceMode, so we should do this first 10065f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mActionMode = startActionMode(callback); 10075f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = mode; 10085f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10095f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 10105f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void endChoiceMode() { 10115f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (!isChoiceMode(CHOICE_MODE_NONE)) { 10125f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mActionMode.finish(); 10135f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mActionMode = null; 10145f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 10155f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung resetCheckedGrandchildren(); 10165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10185f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 10195f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected boolean isChoiceMode(int mode) { 10205f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return mChoiceMode == mode; 10215f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10225f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 10235f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected ArrayList<Checkable> getCheckedGrandchildren() { 10245f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung ArrayList<Checkable> checked = new ArrayList<Checkable>(); 10255f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int childCount = getChildCount(); 10265f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < childCount; ++i) { 10275f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i); 10285f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int grandChildCount = layout.getChildCount(); 10295f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int j = 0; j < grandChildCount; ++j) { 10305f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final View v = layout.getChildAt(j); 10315f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (v instanceof Checkable) { 10325f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung checked.add((Checkable) v); 10335f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10345f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10355f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10365f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return checked; 10375f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10385f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 10395f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void resetCheckedGrandchildren() { 10405f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // loop through children, and set all of their children to _not_ be checked 10415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final ArrayList<Checkable> checked = getCheckedGrandchildren(); 10425f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < checked.size(); ++i) { 10435f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final Checkable c = checked.get(i); 10445f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung c.setChecked(false); 10455f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10465f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 10475f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 104886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 104986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 105086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 105186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 105286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 105386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1054321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 105586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 105686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 105786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 105886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 105986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1060321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPageItems(int page); 106186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1062321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void invalidatePageData() { 10630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 10640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 10650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 106686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 10670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 10680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 10690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 10700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 10710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 10720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 107386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 10740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 10750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka loadAssociatedPages(mCurrentPage); 10760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = true; 10770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 10780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1079321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1080321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1081