PagedView.java revision bbc60d8e79416e37cbede55c159bf6aaa6c171d5
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 19e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport java.util.ArrayList; 20e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport java.util.HashMap; 219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 22321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.content.Context; 239c4949e12c909d5e01d24386147b1c528015b31bAdam Cohenimport android.content.res.TypedArray; 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; 30e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.util.Log; 315f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.view.ActionMode; 32321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.MotionEvent; 33321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.VelocityTracker; 34321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.View; 35321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewConfiguration; 36321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewGroup; 37321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.view.ViewParent; 3880baf5a6b3c62a62265f626d43d1167783c94131Winson Chungimport android.view.animation.Animation; 395f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.view.animation.Animation.AnimationListener; 40e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.view.animation.AnimationUtils; 415f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chungimport android.widget.Checkable; 42e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport android.widget.LinearLayout; 43321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungimport android.widget.Scroller; 44321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 45e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chungimport com.android.launcher.R; 4680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 47321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung/** 48321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * An abstraction of the original Workspace which supports browsing through a 490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * sequential list of "pages" 50321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 51321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chungpublic abstract class PagedView extends ViewGroup { 52321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private static final String TAG = "PagedView"; 530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final int INVALID_PAGE = -1; 54321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 5586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // the min drag distance for a fling to register, to prevent random page shifts 569cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_FLING = 25; 579cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung // The min drag distance to trigger a page shift (regardless of velocity) 589cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung private static final int MIN_LENGTH_FOR_MOVE = 200; 59321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 605f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private static final int PAGE_SNAP_ANIMATION_DURATION = 1000; 610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected static final float NANOTIME_DIV = 1000000000.0f; 620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // the velocity at which a fling gesture will cause us to snap to the next page 640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mSnapVelocity = 500; 650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mSmoothingTime; 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected float mTouchX; 68321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFirstLayout = true; 700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mCurrentPage; 720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mNextPage = INVALID_PAGE; 730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected Scroller mScroller; 74321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private VelocityTracker mVelocityTracker; 75321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 76321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mDownMotionX; 77321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mLastMotionX; 78321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private float mLastMotionY; 79f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private int mLastScreenCenter = -1; 80321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_REST = 0; 820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_SCROLLING = 1; 830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_PREV_PAGE = 2; 840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected final static int TOUCH_STATE_NEXT_PAGE = 3; 85e45440ef0eb9edcde30767b38099b093c6a0d6b0Adam Cohen protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; 86321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected int mTouchState = TOUCH_STATE_REST; 88321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected OnLongClickListener mLongClickListener; 90321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 91321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private boolean mAllowLongPress = true; 92321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 93321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mTouchSlop; 94321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mPagingTouchSlop; 95321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private int mMaximumVelocity; 969c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageSpacing; 979c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingTop; 989c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingBottom; 999c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingLeft; 1009c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mPageLayoutPaddingRight; 101df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutWidthGap; 102df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung protected int mPageLayoutHeightGap; 1039c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mCellCountX; 1049c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen protected int mCellCountY; 1057da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung protected boolean mCenterPagesVertically; 106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected static final int INVALID_POINTER = -1; 108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1095f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka protected int mActivePointerId = INVALID_POINTER; 110321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 11186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private PageSwitchListener mPageSwitchListener; 112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 11386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private ArrayList<Boolean> mDirtyPageContent; 11486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung private boolean mDirtyPageAlpha; 115321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // choice modes 1175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_NONE = 0; 1185f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_SINGLE = 1; 1195f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // Multiple selection mode is not supported by all Launcher actions atm 1205f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected static final int CHOICE_MODE_MULTIPLE = 2; 1219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 122e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka protected int mChoiceMode; 1235f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung private ActionMode mActionMode; 1245f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 125241c3b451d7841ba08247beea784953eca4e8582Winson Chung protected PagedViewIconCache mPageViewIconCache; 126241c3b451d7841ba08247beea784953eca4e8582Winson Chung 1270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, syncPages and syncPageItems will be called to refresh pages 1280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mContentIsRefreshable = true; 1290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, modify alpha of neighboring pages as user scrolls left/right 1310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mFadeInAdjacentScreens = true; 1320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding 1340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // to switch to a new page 1350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mUsePagingTouchSlop = true; 1360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // If true, the subclass should directly update mScrollX itself in its computeScroll method 1380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // (SmoothPagedView does this) 1390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean mDeferScrollUpdate = false; 1400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1411262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected boolean mIsPageMoving = false; 1421262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 143241c3b451d7841ba08247beea784953eca4e8582Winson Chung /** 144241c3b451d7841ba08247beea784953eca4e8582Winson Chung * Simple cache mechanism for PagedViewIcon outlines. 145241c3b451d7841ba08247beea784953eca4e8582Winson Chung */ 146241c3b451d7841ba08247beea784953eca4e8582Winson Chung class PagedViewIconCache { 147241c3b451d7841ba08247beea784953eca4e8582Winson Chung private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>(); 148241c3b451d7841ba08247beea784953eca4e8582Winson Chung 149241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void clear() { 150241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.clear(); 151241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 152241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void addOutline(Object key, Bitmap b) { 153241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.put(key, b); 154241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 155241c3b451d7841ba08247beea784953eca4e8582Winson Chung public void removeOutline(Object key) { 156241c3b451d7841ba08247beea784953eca4e8582Winson Chung if (iconOutlineCache.containsKey(key)) { 157241c3b451d7841ba08247beea784953eca4e8582Winson Chung iconOutlineCache.remove(key); 158241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 159241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 160241c3b451d7841ba08247beea784953eca4e8582Winson Chung public Bitmap getOutline(Object key) { 161241c3b451d7841ba08247beea784953eca4e8582Winson Chung return iconOutlineCache.get(key); 162241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 163241c3b451d7841ba08247beea784953eca4e8582Winson Chung } 164241c3b451d7841ba08247beea784953eca4e8582Winson Chung 16586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public interface PageSwitchListener { 16686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void onPageSwitch(View newPage, int newPageIndex); 167321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 168321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 169321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context) { 170321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, null); 171321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 172321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 173321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs) { 174321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung this(context, attrs, 0); 175321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 176321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 177321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public PagedView(Context context, AttributeSet attrs, int defStyle) { 178321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(context, attrs, defStyle); 1795f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 180321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1819c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 1829c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView, defStyle, 0); 1839c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageSpacing = a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0); 1849c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingTop = a.getDimensionPixelSize( 1859c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingTop, 10); 1869c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingBottom = a.getDimensionPixelSize( 1879c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingBottom, 10); 1889c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingLeft = a.getDimensionPixelSize( 1899c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingLeft, 10); 1909c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen mPageLayoutPaddingRight = a.getDimensionPixelSize( 1919c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen R.styleable.PagedView_pageLayoutPaddingRight, 10); 192df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutWidthGap = a.getDimensionPixelSize( 193df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutWidthGap, -1); 194df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung mPageLayoutHeightGap = a.getDimensionPixelSize( 195df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung R.styleable.PagedView_pageLayoutHeightGap, -1); 1969c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen a.recycle(); 1979c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen 198321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHapticFeedbackEnabled(false); 1990142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka init(); 200321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 201321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 202321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 203321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Initializes various states for this workspace. 204321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 2050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 20686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent = new ArrayList<Boolean>(); 20786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mDirtyPageContent.ensureCapacity(32); 208241c3b451d7841ba08247beea784953eca4e8582Winson Chung mPageViewIconCache = new PagedViewIconCache(); 209321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller = new Scroller(getContext()); 21086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = 0; 2117da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung mCenterPagesVertically = true; 212321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 213321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 214321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchSlop = configuration.getScaledTouchSlop(); 215321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 216321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 217321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 218321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 21986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { 22086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener = pageSwitchListener; 22186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 22286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 223321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 224321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 225321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 226321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 22786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Returns the index of the currently displayed page. 228321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 22986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * @return The index of the currently displayed page. 230321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 23186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getCurrentPage() { 23286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung return mCurrentPage; 233321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 234321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 23586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int getPageCount() { 236321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildCount(); 237321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 238321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 23986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View getPageAt(int index) { 240321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getChildAt(index); 241321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 242321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 243321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int getScrollWidth() { 244321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return getWidth(); 245321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 246321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 247321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 248bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * Updates the scroll of the current page immediately to its final scroll position. We use this 249bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of 250bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung * the previous tab page. 251bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung */ 252bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung protected void updateCurrentPageScroll() { 253bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 254bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung scrollTo(newX, 0); 255bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung mScroller.setFinalX(newX); 256bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung } 257bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung 258bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung /** 25986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Sets the current page. 260321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 26186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung void setCurrentPage(int currentPage) { 26272e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (!mScroller.isFinished()) { 26372e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy mScroller.abortAnimation(); 26472e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 26572e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy if (getChildCount() == 0 || currentPage == mCurrentPage) { 26672e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy return; 26772e0d34fc5d86260a7bd173aed44acf8b66b1c1dPatrick Dubroy } 268321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 26986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); 270bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung updateCurrentPageScroll(); 27180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 27386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung notifyPageSwitchListener(); 274321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 275321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void notifyPageSwitchListener() { 27786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mPageSwitchListener != null) { 27886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); 279321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 280321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 281321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 2821262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy private void pageBeginMoving() { 2831262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = true; 2841262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageBeginMoving(); 2851262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 2861262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 2871262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy private void pageEndMoving() { 2881262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy onPageEndMoving(); 2891262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy mIsPageMoving = false; 2901262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy } 2911262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy 2920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2931262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageBeginMoving() { 2940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 2960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // a method that subclasses can override to add behavior 2971262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy protected void onPageEndMoving() { 2980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 2990142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 300321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 30186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * Registers the specified listener on each page contained in this workspace. 302321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 303321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @param l The listener used to respond to long clicks. 304321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 305321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 306321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void setOnLongClickListener(OnLongClickListener l) { 307321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLongClickListener = l; 30886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final int count = getPageCount(); 309321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 31086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(i).setOnLongClickListener(l); 311321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 312321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 313321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 314321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 3150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void scrollTo(int x, int y) { 3160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka super.scrollTo(x, y); 3170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = x; 3180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 3190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // we moved this functionality to a helper function so SmoothPagedView can reuse it 3220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected boolean computeScrollHelper() { 323321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.computeScrollOffset()) { 3245f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 325321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 3260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 3270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 32886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung } else if (mNextPage != INVALID_PAGE) { 3295f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka mDirtyPageAlpha = true; 33086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); 33186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mNextPage = INVALID_PAGE; 3320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 3330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageEndMoving(); 3340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return true; 335321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 3360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return false; 3370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 3380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 3390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 3400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka computeScrollHelper(); 342321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 343321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 344321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 345321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 346321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 347321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 348321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (widthMode != MeasureSpec.EXACTLY) { 349321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 350321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 351321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 352321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 353321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 354321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (heightMode != MeasureSpec.EXACTLY) { 355321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 356321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 357321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 358321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // The children are given the same width and height as the workspace 3595f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // unless they were set to WRAP_CONTENT 360321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 361321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 3625f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka // disallowing padding in paged view (just pass 0) 3635f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final View child = getChildAt(i); 3645f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 3655f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 3665f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childWidthMode; 3675f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.width == LayoutParams.WRAP_CONTENT) { 3685f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.AT_MOST; 3695f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 3705f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childWidthMode = MeasureSpec.EXACTLY; 3715f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 3725f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 3735f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka int childHeightMode; 3745f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (lp.height == LayoutParams.WRAP_CONTENT) { 3755f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.AT_MOST; 3765f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } else { 3775f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childHeightMode = MeasureSpec.EXACTLY; 3785f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka } 3795f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 3805f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childWidthMeasureSpec = 3815f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka MeasureSpec.makeMeasureSpec(widthSize, childWidthMode); 3825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int childHeightMeasureSpec = 3835f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka MeasureSpec.makeMeasureSpec(heightSize, childHeightMode); 3845f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka 3855f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 386321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 387321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 388321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setMeasuredDimension(widthSize, heightSize); 389cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka } 390321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 391cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka @Override 39228750fba6a2d141eb9a1e566718c17236030b815Michael Jurka protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 3935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 394321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(false); 395cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); 396cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka scrollTo(newX, 0); 397cfc629446ca86366665263dba6520a7b978b7c3eMichael Jurka mScroller.setFinalX(newX); 398321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung setHorizontalScrollBarEnabled(true); 399321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mFirstLayout = false; 400321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 401321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 402321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 403321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childLeft = 0; 404321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (childCount > 0) { 405e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung childLeft = getRelativeChildOffset(0); 406321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 407321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 408321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; i++) { 409321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final View child = getChildAt(i); 410321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (child.getVisibility() != View.GONE) { 411321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childWidth = child.getMeasuredWidth(); 4127da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung final int childHeight = (mCenterPagesVertically ? 4137da1025bd7f15b04cf55c79b73e94e5e1bc959d9Winson Chung (getMeasuredHeight() - child.getMeasuredHeight()) / 2 : 0); 4145f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka child.layout(childLeft, childHeight, 4155f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka childLeft + childWidth, childHeight + child.getMeasuredHeight()); 4169c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen childLeft += childWidth + mPageSpacing; 417321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 418321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 419321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 420321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 421e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void updateAdjacentPagesAlpha() { 4220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mFadeInAdjacentScreens) { 4230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { 424e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int halfScreenSize = getMeasuredWidth() / 2; 425e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int screenCenter = mScrollX + halfScreenSize; 4260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = getChildCount(); 4270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < childCount; ++i) { 4280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 4290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childWidth = layout.getMeasuredWidth(); 4300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int halfChildWidth = (childWidth / 2); 4310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka int childCenter = getChildOffset(i) + halfChildWidth; 432e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 433b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // On the first layout, we may not have a width nor a proper offset, so for now 434b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // we should just assume full page width (and calculate the offset according to 435b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // that). 436b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung if (childWidth <= 0) { 437b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childWidth = getMeasuredWidth(); 438b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung childCenter = (i * childWidth) + (childWidth / 2); 439b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung } 440b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 441e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int d = halfChildWidth; 442e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung int distanceFromScreenCenter = childCenter - screenCenter; 443e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (distanceFromScreenCenter > 0) { 444e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i > 0) { 445e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung d += getChildAt(i - 1).getMeasuredWidth() / 2; 446e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 4470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 448e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung if (i < childCount - 1) { 449e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung d += getChildAt(i + 1).getMeasuredWidth() / 2; 450e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung } 4510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 4529c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen d += mPageSpacing; 453e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 454b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung // Preventing potential divide-by-zero 455b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung d = Math.max(1, d); 456b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung 457e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d; 458e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); 459e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung float alpha = 1.0f - dimAlpha; 460e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung 461f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (alpha < ALPHA_QUANTIZE_LEVEL) { 462f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 0.0f; 463f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) { 464f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen alpha = 1.0f; 465f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 466f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 4670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (Float.compare(alpha, layout.getAlpha()) != 0) { 4680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.setAlpha(alpha); 4690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 470affd7b4d23cecb4ed74133dd8bd9a5ede099c562Winson Chung } 4710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = false; 472321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 473321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 474e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 4750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 476f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen protected void screenScrolled(int screenCenter) { 477f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 478f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 479e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung @Override 480e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected void dispatchDraw(Canvas canvas) { 481f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int halfScreenSize = getMeasuredWidth() / 2; 482f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int screenCenter = mScrollX + halfScreenSize; 483f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 484f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen if (screenCenter != mLastScreenCenter) { 485f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen screenScrolled(screenCenter); 486f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen updateAdjacentPagesAlpha(); 487f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mLastScreenCenter = screenCenter; 488f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 4890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 490e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung // Find out which screens are visible; as an optimization we only call draw on them 4910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // As an optimization, this code assumes that all pages have the same width as the 0th 4920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // page. 4930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int pageCount = getChildCount(); 494c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka if (pageCount > 0) { 495c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int pageWidth = getChildAt(0).getMeasuredWidth(); 496c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final int screenWidth = getMeasuredWidth(); 497c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int x = getRelativeChildOffset(0) + pageWidth; 498c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int leftScreen = 0; 499c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka int rightScreen = 0; 500c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka while (x <= mScrollX) { 501c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka leftScreen++; 5029c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen x += pageWidth + mPageSpacing; 503c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka // replace above line with this if you don't assume all pages have same width as 0th 504c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka // page: 505c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka // x += getChildAt(leftScreen).getMeasuredWidth(); 506c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 507c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = leftScreen; 508c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka while (x < mScrollX + screenWidth) { 509c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen++; 5109c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen x += pageWidth + mPageSpacing; 511c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka // replace above line with this if you don't assume all pages have same width as 0th 512c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka // page: 513c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka //if (rightScreen < pageCount) { 514c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka // x += getChildAt(rightScreen).getMeasuredWidth(); 515c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka //} 516c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 517c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka rightScreen = Math.min(getChildCount() - 1, rightScreen); 5180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 519c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka final long drawingTime = getDrawingTime(); 520c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka for (int i = leftScreen; i <= rightScreen; i++) { 521c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka drawChild(canvas, getChildAt(i), drawingTime); 522c4fb9173e73c0092a089512734c0d7df13189014Michael Jurka } 5230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 524321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 525321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 526321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 527321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 52886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 52986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page != mCurrentPage || !mScroller.isFinished()) { 53086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 531321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 532321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 533321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 534321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 535321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 536321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 537321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 53886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int focusablePage; 53986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage != INVALID_PAGE) { 54086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mNextPage; 541321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 54286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung focusablePage = mCurrentPage; 543321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 54486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View v = getPageAt(focusablePage); 545321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 546321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v.requestFocus(direction, previouslyFocusedRect); 547321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 548321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return false; 549321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 550321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 551321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 552321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean dispatchUnhandledMove(View focused, int direction) { 553321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 55486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() > 0) { 55586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() - 1); 556321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 557321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 558321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT) { 55986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (getCurrentPage() < getPageCount() - 1) { 56086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(getCurrentPage() + 1); 561321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 562321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 563321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 564321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return super.dispatchUnhandledMove(focused, direction); 565321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 566321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 567321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 568321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 56986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { 57086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage).addFocusables(views, direction); 571321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 572321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (direction == View.FOCUS_LEFT) { 57386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) { 57486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage - 1).addFocusables(views, direction); 575321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 576321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (direction == View.FOCUS_RIGHT){ 57786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getPageCount() - 1) { 57886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung getPageAt(mCurrentPage + 1).addFocusables(views, direction); 579321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 580321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 581321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 582321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 583321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 584321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If one of our descendant views decides that it could be focused now, only 58586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * pass that along if it's on the current page. 586321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * 58786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This happens when live folders requery, and if they're off page, they 58886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * end up calling requestFocus, which pulls it on page. 589321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 590321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 591321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void focusableViewAvailable(View focused) { 59286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung View current = getPageAt(mCurrentPage); 593321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung View v = focused; 594321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung while (true) { 595321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == current) { 596321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.focusableViewAvailable(focused); 597321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 598321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 599321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v == this) { 600321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 601321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 602321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent parent = v.getParent(); 603321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (parent instanceof View) { 604321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung v = (View)v.getParent(); 605321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 606321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return; 607321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 608321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 609321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 610321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 611321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 612321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * {@inheritDoc} 613321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 614321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 615321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 616321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (disallowIntercept) { 617321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // We need to make sure to cancel our long press if 618321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // a scrollable widget takes over touch events 61986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getChildAt(mCurrentPage); 62086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage.cancelLongPress(); 621321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 622321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestDisallowInterceptTouchEvent(disallowIntercept); 623321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 624321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 625321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 626321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onInterceptTouchEvent(MotionEvent ev) { 627321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 628321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * This method JUST determines whether we want to intercept the motion. 629321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If we return true, onTouchEvent will be called and we do the actual 630321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * scrolling there. 631321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 632321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 63345e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 63445e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 63545e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 636321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 637321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Shortcut the most recurring case: the user is in the dragging 638321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * state and he is moving his finger. We want to intercept this 639321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * motion. 640321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 641321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 642321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if ((action == MotionEvent.ACTION_MOVE) && 643321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState == TOUCH_STATE_SCROLLING)) { 644321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 645321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 646321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 647321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 648321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: { 649321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 650321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 651321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * whether the user has moved far enough from his original down touch. 652321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 6531ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka if (mActivePointerId != INVALID_POINTER) { 6541ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka determineScrollingStart(ev); 6551ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka break; 6561ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka } 6571ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 6581ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 6591ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // i.e. fall through to the next case (don't break) 6601ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 6611ff706b8c03063740ca74868fea46fa47d71bb27Michael Jurka // while it's small- this was causing a crash before we checked for INVALID_POINTER) 662321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 663321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 664321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: { 665321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(); 666321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(); 667321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember location of down touch 668321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = x; 669321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 670321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = y; 671321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 672321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = true; 673321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 674321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 675321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches the screen, initiate drag; 676321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * otherwise don't. mScroller.isFinished should be false when 677321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * being flinged. 678321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 679fd177c1d10085e5e12ff7df27d956a378d1139b1Michael Jurka final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 6805f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 6815f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (finishedScrolling) { 6825f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_REST; 6835f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mScroller.abortAnimation(); 6845f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } else { 6855f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mTouchState = TOUCH_STATE_SCROLLING; 6865f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 687321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 68886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung // check if this can be the beginning of a tap on the side of the pages 689321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // to scroll the current page 69021f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen if ((mTouchState != TOUCH_STATE_PREV_PAGE) && !handlePagingClicks() && 691321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung (mTouchState != TOUCH_STATE_NEXT_PAGE)) { 692321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() > 0) { 693e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int width = getMeasuredWidth(); 694e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int offset = getRelativeChildOffset(mCurrentPage); 69521f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen if (x < offset - mPageSpacing) { 696321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_PREV_PAGE; 69721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen } else if (x > (width - offset + mPageSpacing)) { 698321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_NEXT_PAGE; 699321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 700321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 701321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 702321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 703321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 704321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 705321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 706321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 707321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 708321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 709321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 710321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 711321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 712321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 713321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 714321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 715321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 716321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 717321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 718321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * The only time we want to intercept motion events is if we are in the 719321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * drag mode. 720321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 721321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mTouchState != TOUCH_STATE_REST; 722321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 723321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 72480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung protected void animateClickFeedback(View v, final Runnable r) { 72580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // animate the view slightly to show click feedback running some logic after it is "pressed" 72680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung Animation anim = AnimationUtils.loadAnimation(getContext(), 72780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung R.anim.paged_view_click_feedback); 72880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung anim.setAnimationListener(new AnimationListener() { 72980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 73080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationStart(Animation animation) {} 73180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 73280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationRepeat(Animation animation) { 73380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung r.run(); 73480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 73580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung @Override 73680baf5a6b3c62a62265f626d43d1167783c94131Winson Chung public void onAnimationEnd(Animation animation) {} 73780baf5a6b3c62a62265f626d43d1167783c94131Winson Chung }); 73880baf5a6b3c62a62265f626d43d1167783c94131Winson Chung v.startAnimation(anim); 73980baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 74080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 741321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 742321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Determines if we should change the touch state to start scrolling after the 743321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * user moves their touch point too far. 744321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 7451adf5391a3a3d215b226adf3702019c22a99e3b1Michael Jurka protected void determineScrollingStart(MotionEvent ev) { 746321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 747321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * Locally do absolute value. mLastMotionX is set to the y value 748321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * of the down event. 749321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 750321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 751321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 752321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float y = ev.getY(pointerIndex); 753321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int xDiff = (int) Math.abs(x - mLastMotionX); 754321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int yDiff = (int) Math.abs(y - mLastMotionY); 755321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 756321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int touchSlop = mTouchSlop; 757321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xPaged = xDiff > mPagingTouchSlop; 758321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean xMoved = xDiff > touchSlop; 759321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung boolean yMoved = yDiff > touchSlop; 760321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 761321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (xMoved || yMoved) { 7620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mUsePagingTouchSlop ? xPaged : xMoved) { 763321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll if the user moved far enough along the X axis 764321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_SCROLLING; 765321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 7660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX = mScrollX; 7670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 7680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 769321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 770321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Either way, cancel any pending longpress 771321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mAllowLongPress) { 772321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mAllowLongPress = false; 773321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Try canceling the long press. It could also have been scheduled 774321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // by a distant descendant, so use the mAllowLongPress flag to block 775321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // everything 77686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung final View currentPage = getPageAt(mCurrentPage); 77710fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung if (currentPage != null) { 77810fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung currentPage.cancelLongPress(); 77910fefb18c4f227f6a08fc24966800e49ce743be8Winson Chung } 780321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 781321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 782321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 783321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 78421f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen protected boolean handlePagingClicks() { 78521f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen return false; 78621f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen } 78721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen 788321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 789321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean onTouchEvent(MotionEvent ev) { 79045e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung // Skip touch handling if there are no pages to swipe 79145e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung if (getChildCount() <= 0) return super.onTouchEvent(ev); 79245e1d6ec0a213a444d01466c3d4f1ded5508ed63Winson Chung 793b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka acquireVelocityTrackerAndAddMovement(ev); 794321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 795321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int action = ev.getAction(); 796321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 797321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung switch (action & MotionEvent.ACTION_MASK) { 798321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_DOWN: 799321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /* 800321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * If being flinged and user touches, stop the fling. isFinished 801321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * will be false if being flinged. 802321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 803321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) { 804321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mScroller.abortAnimation(); 805321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 806321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 807321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Remember where the motion event started 808321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mDownMotionX = mLastMotionX = ev.getX(); 809321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(0); 8100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 8110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 8120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 813321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 814321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 815321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_MOVE: 816321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 817321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // Scroll to follow the motion event 818321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(mActivePointerId); 819321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 820321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int deltaX = (int) (mLastMotionX - x); 821321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = x; 822321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 823321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int sx = getScrollX(); 824321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (deltaX < 0) { 825321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (sx > 0) { 8260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX += Math.max(-mTouchX, deltaX); 8270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 8280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (!mDeferScrollUpdate) { 8290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka scrollBy(Math.max(-sx, deltaX), 0); 8300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 8310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // This will trigger a call to computeScroll() on next drawChild() call 8320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 8330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 834321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 835321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else if (deltaX > 0) { 836321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int lastChildIndex = getChildCount() - 1; 837321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int availableToScroll = getChildOffset(lastChildIndex) - 838321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung getRelativeChildOffset(lastChildIndex) - sx; 839321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (availableToScroll > 0) { 8400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTouchX += Math.min(availableToScroll, deltaX); 8410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 8420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (!mDeferScrollUpdate) { 8430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka scrollBy(Math.min(availableToScroll, deltaX), 0); 8440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 8450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // This will trigger a call to computeScroll() on next drawChild() call 8460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka invalidate(); 8470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 848321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 849321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 850321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(); 851321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 852564976a46ef02d665aa0e455ad7867746a0b5325Adam Cohen } else { 853321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung determineScrollingStart(ev); 854321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 855321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 856321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 857321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_UP: 858321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mTouchState == TOUCH_STATE_SCROLLING) { 859321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int activePointerId = mActivePointerId; 860321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = ev.findPointerIndex(activePointerId); 861321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final float x = ev.getX(pointerIndex); 862321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final VelocityTracker velocityTracker = mVelocityTracker; 863321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 864321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 8659cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung final int deltaX = (int) (x - mDownMotionX); 8669cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung boolean isfling = Math.abs(deltaX) > MIN_LENGTH_FOR_FLING; 8679cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE; 868321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 8690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int snapVelocity = mSnapVelocity; 8709cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung if ((isSignificantMove && deltaX > 0 || 8719cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung (isfling && velocityX > snapVelocity)) && 8729cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung mCurrentPage > 0) { 8730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage - 1, velocityX); 8749cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung } else if ((isSignificantMove && deltaX < 0 || 8759cfd25f16739548111ba8fc6ba8cd83010eccef6Winson Chung (isfling && velocityX < -snapVelocity)) && 87686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage < getChildCount() - 1) { 8770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPageWithVelocity(mCurrentPage + 1, velocityX); 878321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 879321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 880321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 88121f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen } else if (mTouchState == TOUCH_STATE_PREV_PAGE && !handlePagingClicks()) { 882321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 883321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 884321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 88586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.max(0, mCurrentPage - 1); 88686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 88786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 888321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 889321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 890321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 89121f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen } else if (mTouchState == TOUCH_STATE_NEXT_PAGE && !handlePagingClicks()) { 892321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // at this point we have not moved beyond the touch slop 893321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 894321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // we can just page 89586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 89686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (nextPage != mCurrentPage) { 89786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(nextPage); 898321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 899321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung snapToDestination(); 900321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 901321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 902321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 903321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 904b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 905321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 906321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 907321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_CANCEL: 908b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mTouchState == TOUCH_STATE_SCROLLING) { 909b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka snapToDestination(); 910b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 911321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mTouchState = TOUCH_STATE_REST; 912321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = INVALID_POINTER; 913b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka releaseVelocityTracker(); 914321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 915321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 916321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung case MotionEvent.ACTION_POINTER_UP: 917321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung onSecondaryPointerUp(ev); 918321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung break; 919321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 920321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 921321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return true; 922321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 923321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 924b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { 925b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker == null) { 926b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = VelocityTracker.obtain(); 927b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 928b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.addMovement(ev); 929b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 930b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 931b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka private void releaseVelocityTracker() { 932b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka if (mVelocityTracker != null) { 933b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker.recycle(); 934b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka mVelocityTracker = null; 935b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 936b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka } 937b8f0672d7c4b9836f90c1a7517a9e47fec4c7144Michael Jurka 938321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private void onSecondaryPointerUp(MotionEvent ev) { 939321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 940321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung MotionEvent.ACTION_POINTER_INDEX_SHIFT; 941321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int pointerId = ev.getPointerId(pointerIndex); 942321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (pointerId == mActivePointerId) { 943321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // This was our active pointer going up. Choose a new 944321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // active pointer and adjust accordingly. 945321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung // TODO: Make this decision more intelligent. 946321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 947321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); 948321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mLastMotionY = ev.getY(newPointerIndex); 949321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mActivePointerId = ev.getPointerId(newPointerIndex); 950321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mVelocityTracker != null) { 951321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung mVelocityTracker.clear(); 952321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 953321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 954321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 955321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 956321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 957321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void requestChildFocus(View child, View focused) { 958321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.requestChildFocus(child, focused); 95986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int page = indexOfChild(child); 96086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (page >= 0 && !isInTouchMode()) { 96186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung snapToPage(page); 962321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 963321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 964321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 965e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getChildIndexForRelativeOffset(int relativeOffset) { 966e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int childCount = getChildCount(); 9679c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int left; 9689c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen int right; 969e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung for (int i = 0; i < childCount; ++i) { 9709c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen left = getRelativeChildOffset(i); 9719c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen right = (left + getChildAt(i).getMeasuredWidth()); 972e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung if (left <= relativeOffset && relativeOffset <= right) { 973e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return i; 974e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 975e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 976e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return -1; 977e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 978e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 979321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getRelativeChildOffset(int index) { 980321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2; 981321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 982321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 983321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected int getChildOffset(int index) { 984321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (getChildCount() == 0) 985321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return 0; 986321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 987321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int offset = getRelativeChildOffset(0); 988321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < index; ++i) { 9899c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen offset += getChildAt(i).getMeasuredWidth() + mPageSpacing; 990321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 991321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return offset; 992321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 993321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 994d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen int getPageNearestToCenterOfScreen() { 995321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenter = getMeasuredWidth(); 996321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int minDistanceFromScreenCenterIndex = -1; 997321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int screenCenter = mScrollX + (getMeasuredWidth() / 2); 998321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int childCount = getChildCount(); 999321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < childCount; ++i) { 10000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View layout = (View) getChildAt(i); 1001321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childWidth = layout.getMeasuredWidth(); 1002321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int halfChildWidth = (childWidth / 2); 1003321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int childCenter = getChildOffset(i) + halfChildWidth; 1004321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); 1005321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (distanceFromScreenCenter < minDistanceFromScreenCenter) { 1006321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenter = distanceFromScreenCenter; 1007321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung minDistanceFromScreenCenterIndex = i; 1008321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1009321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1010d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen return minDistanceFromScreenCenterIndex; 1011d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen } 1012d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen 1013d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen protected void snapToDestination() { 1014d19d3ca3ec22aeec48b8e555e9764b98ff8cae5fAdam Cohen snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); 1015321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1016321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 10170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 10180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // We ignore velocity in this implementation, but children (e.g. SmoothPagedView) 10190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // can use it 10200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage); 10210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 10220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 10230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 10245f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); 1025321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1026321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 10270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int duration) { 102886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); 1029321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 103086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 1031321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int sX = getScrollX(); 1032321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final int delta = newX - sX; 10330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 10340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 10350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 10360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage, int delta, int duration) { 10370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mNextPage = whichPage; 10380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 10390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka View focusedChild = getFocusedChild(); 10400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (focusedChild != null && whichPage != mCurrentPage && 10410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild == getChildAt(mCurrentPage)) { 10420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka focusedChild.clearFocus(); 10430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 10440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 10450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka pageBeginMoving(); 1046321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung awakenScrollBars(duration); 1047321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (duration == 0) { 1048321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung duration = Math.abs(delta); 1049321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1050321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1051321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (!mScroller.isFinished()) mScroller.abortAnimation(); 10520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mScroller.startScroll(getScrollX(), 0, delta, 0, duration); 105380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 105480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung // only load some associated pages 105586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung loadAssociatedPages(mNextPage); 10560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka notifyPageSwitchListener(); 1057321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung invalidate(); 1058321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1059321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1060321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1061321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected Parcelable onSaveInstanceState() { 1062321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung final SavedState state = new SavedState(super.onSaveInstanceState()); 106386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung state.currentPage = mCurrentPage; 1064321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return state; 1065321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1066321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1067321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1068321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung protected void onRestoreInstanceState(Parcelable state) { 1069321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState savedState = (SavedState) state; 1070321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.onRestoreInstanceState(savedState.getSuperState()); 107186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (savedState.currentPage != -1) { 107286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung mCurrentPage = savedState.currentPage; 1073321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1074321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1075321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1076321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollLeft() { 1077321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 107886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); 1079321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 108086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage > 0) snapToPage(mNextPage - 1); 1081321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1082321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1083321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1084321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void scrollRight() { 1085321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (mScroller.isFinished()) { 108686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); 1087321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } else { 108886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); 1089321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1090321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1091321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 109286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public int getPageForView(View v) { 1093321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int result = -1; 1094321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (v != null) { 1095321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung ViewParent vp = v.getParent(); 1096321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung int count = getChildCount(); 1097321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung for (int i = 0; i < count; i++) { 1098321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung if (vp == getChildAt(i)) { 1099321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return i; 1100321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1101321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1102321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1103321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return result; 1104321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1105321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1106321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung /** 1107321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung * @return True is long presses are still allowed for the current touch 1108321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung */ 1109321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public boolean allowLongPress() { 1110321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return mAllowLongPress; 1111321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1112321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 11130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 11140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Set true to allow long-press events to be triggered, usually checked by 11150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * {@link Launcher} to accept or block dpad-initiated long-presses. 11160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 11170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setAllowLongPress(boolean allowLongPress) { 11180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mAllowLongPress = allowLongPress; 11190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 11200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1121321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static class SavedState extends BaseSavedState { 112286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung int currentPage = -1; 1123321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1124321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung SavedState(Parcelable superState) { 1125321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(superState); 1126321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1127321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1128321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung private SavedState(Parcel in) { 1129321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super(in); 113086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung currentPage = in.readInt(); 1131321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1132321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1133321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung @Override 1134321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void writeToParcel(Parcel out, int flags) { 1135321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung super.writeToParcel(out, flags); 113686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung out.writeInt(currentPage); 1137321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1138321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1139321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public static final Parcelable.Creator<SavedState> CREATOR = 1140321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung new Parcelable.Creator<SavedState>() { 1141321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState createFromParcel(Parcel in) { 1142321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState(in); 1143321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1144321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 1145321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public SavedState[] newArray(int size) { 1146321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung return new SavedState[size]; 1147321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1148321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung }; 1149321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1150321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung 115186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung public void loadAssociatedPages(int page) { 11520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 11530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 11540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (page < count) { 1155e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int lowerPageBound = getAssociatedLowerPageBound(page); 1156e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung int upperPageBound = getAssociatedUpperPageBound(page); 11570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 11580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final ViewGroup layout = (ViewGroup) getChildAt(i); 11590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int childCount = layout.getChildCount(); 11600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (lowerPageBound <= i && i <= upperPageBound) { 11610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mDirtyPageContent.get(i)) { 11620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPageItems(i); 11630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, false); 11640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 11650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 11660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (childCount > 0) { 11670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka layout.removeAllViews(); 11680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 11690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.set(i, true); 117080baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 117180baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 117280baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 117380baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 117480baf5a6b3c62a62265f626d43d1167783c94131Winson Chung } 117580baf5a6b3c62a62265f626d43d1167783c94131Winson Chung 1176e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedLowerPageBound(int page) { 1177e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.max(0, page - 1); 1178e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1179e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung protected int getAssociatedUpperPageBound(int page) { 1180e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung final int count = getChildCount(); 1181e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung return Math.min(page + 1, count - 1); 1182e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung } 1183e3193b93ad7bf33e2e45319084a99b9fc986622bWinson Chung 11845f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void startChoiceMode(int mode, ActionMode.Callback callback) { 1185430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy if (isChoiceMode(CHOICE_MODE_NONE)) { 1186430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mChoiceMode = mode; 1187430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy mActionMode = startActionMode(callback); 1188430c53bc80be01996b45e55687885d6c05314645Patrick Dubroy } 11895f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 11905f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 11912b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public void endChoiceMode() { 11925f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung if (!isChoiceMode(CHOICE_MODE_NONE)) { 11935f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung mChoiceMode = CHOICE_MODE_NONE; 11945f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung resetCheckedGrandchildren(); 1195e17e19c0bd78348b0452f5b00846b2a63a749d33Michael Jurka if (mActionMode != null) mActionMode.finish(); 11969f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy mActionMode = null; 11975f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 11985f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 11995f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 12005f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected boolean isChoiceMode(int mode) { 12015f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return mChoiceMode == mode; 12025f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 12035f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 12045f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected ArrayList<Checkable> getCheckedGrandchildren() { 12055f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung ArrayList<Checkable> checked = new ArrayList<Checkable>(); 12065f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int childCount = getChildCount(); 12075f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < childCount; ++i) { 1208d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung final ViewGroup layout = (ViewGroup) getChildAt(i); 12095f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final int grandChildCount = layout.getChildCount(); 12105f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int j = 0; j < grandChildCount; ++j) { 12115f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final View v = layout.getChildAt(j); 12129f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 12135f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung checked.add((Checkable) v); 12145f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 12155f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 12165f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 12175f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung return checked; 12185f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 12195f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 12209f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy /** 12219f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * If in CHOICE_MODE_SINGLE and an item is checked, returns that item. 12229f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy * Otherwise, returns null. 12239f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy */ 12249f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy protected Checkable getSingleCheckedGrandchild() { 12259f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (mChoiceMode == CHOICE_MODE_SINGLE) { 12269f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int childCount = getChildCount(); 12279f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int i = 0; i < childCount; ++i) { 1228d0d43010c7a091b6ade407d30e490527a8d16120Winson Chung final ViewGroup layout = (ViewGroup) getChildAt(i); 12299f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final int grandChildCount = layout.getChildCount(); 12309f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy for (int j = 0; j < grandChildCount; ++j) { 12319f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy final View v = layout.getChildAt(j); 12329f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy if (v instanceof Checkable && ((Checkable) v).isChecked()) { 12339f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return (Checkable) v; 12349f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 12359f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 12369f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 12379f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 12389f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy return null; 12399f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy } 12409f7aec8d4317d0407a41e12f236c962b225cb4c3Patrick Dubroy 12412b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy public Object getChosenItem() { 12422b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy View checkedView = (View) getSingleCheckedGrandchild(); 12432b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy if (checkedView != null) { 12442b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy return checkedView.getTag(); 12452b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy } 12462b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy return null; 12472b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy } 12482b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy 12495f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung protected void resetCheckedGrandchildren() { 12505f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung // loop through children, and set all of their children to _not_ be checked 12515f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final ArrayList<Checkable> checked = getCheckedGrandchildren(); 12525f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung for (int i = 0; i < checked.size(); ++i) { 12535f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung final Checkable c = checked.get(i); 12545f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung c.setChecked(false); 12555f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 12565f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung } 12575f2aa4efeeb8b0133d891715d71553138d9f9ca7Winson Chung 125886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 125986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called ONLY to synchronize the number of pages that the paged view has. 126086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * To actually fill the pages with information, implement syncPageItems() below. It is 126186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * guaranteed that syncPageItems() will be called for a particular page before it is shown, 126286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * and therefore, individual page items do not need to be updated in this method. 126386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1264321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPages(); 126586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 126686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung /** 126786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * This method is called to synchronize the items that are on a particular page. If views on 126886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung * the page can be reused, then they should be updated within this method. 126986f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung */ 1270321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public abstract void syncPageItems(int page); 127186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 1272321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung public void invalidatePageData() { 12730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (mContentIsRefreshable) { 12740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Update all the pages 12750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka syncPages(); 127686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 12770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Mark each of the pages as dirty 12780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int count = getChildCount(); 12790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.clear(); 12800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka for (int i = 0; i < count; ++i) { 12810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageContent.add(true); 12820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 128386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung 12840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // Load any pages that are necessary for the current window of views 12850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka loadAssociatedPages(mCurrentPage); 12860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mDirtyPageAlpha = true; 1287b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung updateAdjacentPagesAlpha(); 12880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka requestLayout(); 12890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1290321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung } 1291321e9ee68848d9e782fd557f69cc070308ffbc9cWinson Chung} 1292