diff options
Diffstat (limited to 'src/com/android/launcher3/PagedView.java')
-rw-r--r-- | src/com/android/launcher3/PagedView.java | 1131 |
1 files changed, 284 insertions, 847 deletions
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 7d65f4686..873c0684e 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -19,16 +19,16 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.PointF; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -46,20 +46,12 @@ import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; -import android.view.animation.AnimationUtils; -import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; -import java.util.ArrayList; +import com.android.launcher3.util.LauncherEdgeEffect; +import com.android.launcher3.util.Thunk; -interface Page { - public int getPageChildCount(); - public View getChildOnPageAt(int i); - public void removeAllViewsOnPage(); - public void removeViewOnPageAt(int i); - public int indexOfChildOnPage(View v); -} +import java.util.ArrayList; /** * An abstraction of the original Workspace which supports browsing through a @@ -74,28 +66,21 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private static final int MIN_LENGTH_FOR_FLING = 25; protected static final int PAGE_SNAP_ANIMATION_DURATION = 750; - protected static final int OVER_SCROLL_PAGE_SNAP_ANIMATION_DURATION = 350; protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950; protected static final float NANOTIME_DIV = 1000000000.0f; - private static final float OVERSCROLL_ACCELERATE_FACTOR = 2; - private static final float OVERSCROLL_DAMP_FACTOR = 0.07f; - private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; // The page is moved more than halfway, automatically move to the next page on touch up. private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f; + private static final float MAX_SCROLL_PROGRESS = 1.0f; + // The following constants need to be scaled based on density. The scaled versions will be // assigned to the corresponding member variables below. private static final int FLING_THRESHOLD_VELOCITY = 500; private static final int MIN_SNAP_VELOCITY = 1500; private static final int MIN_FLING_VELOCITY = 250; - // We are disabling touch interaction of the widget region for factory ROM. - private static final boolean DISABLE_TOUCH_INTERACTION = false; - private static final boolean DISABLE_TOUCH_SIDE_PAGES = true; - private static final boolean DISABLE_FLING_TO_DELETE = true; - public static final int INVALID_RESTORE_PAGE = -1001; private boolean mFreeScroll = false; @@ -124,7 +109,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected LauncherScroller mScroller; private Interpolator mDefaultInterpolator; private VelocityTracker mVelocityTracker; - private int mPageSpacing = 0; + @Thunk int mPageSpacing = 0; private float mParentDownMotionX; private float mParentDownMotionY; @@ -156,7 +141,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected OnLongClickListener mLongClickListener; protected int mTouchSlop; - private int mPagingTouchSlop; private int mMaximumVelocity; protected int mPageLayoutWidthGap; protected int mPageLayoutHeightGap; @@ -164,15 +148,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected int mCellCountY = 0; protected boolean mCenterPagesVertically; protected boolean mAllowOverScroll = true; - protected int mUnboundedScrollX; protected int[] mTempVisiblePagesRange = new int[2]; protected boolean mForceDrawAllChildrenNextFrame; - private boolean mSpacePagesAutomatically = false; - - // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise - // it is equal to the scaled overscroll position. We use a separate value so as to prevent - // the screens from continuing to translate beyond the normal bounds. - protected int mOverScrollX; protected static final int INVALID_POINTER = -1; @@ -180,37 +157,16 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private PageSwitchListener mPageSwitchListener; - protected ArrayList<Boolean> mDirtyPageContent; - - // If true, syncPages and syncPageItems will be called to refresh pages - protected boolean mContentIsRefreshable = true; - // If true, modify alpha of neighboring pages as user scrolls left/right protected boolean mFadeInAdjacentScreens = false; - // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding - // to switch to a new page - protected boolean mUsePagingTouchSlop = true; - - // If true, the subclass should directly update scrollX itself in its computeScroll method - // (SmoothPagedView does this) - protected boolean mDeferScrollUpdate = false; - protected boolean mDeferLoadAssociatedPagesUntilScrollCompletes = false; - protected boolean mIsPageMoving = false; - // All syncs and layout passes are deferred until data is ready. - protected boolean mIsDataReady = false; - - protected boolean mAllowLongPress = true; - private boolean mWasInOverscroll = false; // Page Indicator - private int mPageIndicatorViewId; - private PageIndicator mPageIndicator; - private boolean mAllowPagedViewAnimations = true; - + @Thunk int mPageIndicatorViewId; + @Thunk PageIndicator mPageIndicator; // The viewport whether the pages are to be contained (the actual view may be larger than the // viewport) private Rect mViewport = new Rect(); @@ -218,16 +174,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Reordering // We use the min scale to determine how much to expand the actually PagedView measured // dimensions such that when we are zoomed out, the view is not clipped - private int REORDERING_DROP_REPOSITION_DURATION = 200; - protected int REORDERING_REORDER_REPOSITION_DURATION = 300; - protected int REORDERING_ZOOM_IN_OUT_DURATION = 250; - private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80; + private static int REORDERING_DROP_REPOSITION_DURATION = 200; + @Thunk static int REORDERING_REORDER_REPOSITION_DURATION = 300; + private static int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80; + private float mMinScale = 1f; private boolean mUseMinScale = false; protected View mDragView; - protected AnimatorSet mZoomInOutAnim; private Runnable mSidePageHoverRunnable; - private int mSidePageHoverIndex = -1; + @Thunk int mSidePageHoverIndex = -1; // This variable's scope is only for the duration of startReordering() and endReordering() private boolean mReorderingStarted = false; // This variable's scope is for the duration of startReordering() and after the zoomIn() @@ -239,30 +194,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private Runnable mPostReorderingPreZoomInRunnable; // Convenience/caching - private Matrix mTmpInvMatrix = new Matrix(); - private float[] mTmpPoint = new float[2]; - private int[] mTmpIntPoint = new int[2]; - private Rect mTmpRect = new Rect(); - private Rect mAltTmpRect = new Rect(); - - // Fling to delete - private int FLING_TO_DELETE_FADE_OUT_DURATION = 350; - private float FLING_TO_DELETE_FRICTION = 0.035f; - // The degrees specifies how much deviation from the up vector to still consider a fling "up" - private float FLING_TO_DELETE_MAX_FLING_DEGREES = 65f; - protected int mFlingToDeleteThresholdVelocity = -1400; - // Drag to delete - private boolean mDeferringForDelete = false; - private int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250; - private int DRAG_TO_DELETE_FADE_OUT_DURATION = 350; - - // Drop to delete - private View mDeleteDropTarget; - - // Bouncer - private boolean mTopAlignPageWhenShrinkingForBouncer = false; + private static final Matrix sTmpInvMatrix = new Matrix(); + private static final float[] sTmpPoint = new float[2]; + private static final int[] sTmpIntPoint = new int[2]; + private static final Rect sTmpRect = new Rect(); protected final Rect mInsets = new Rect(); + protected final boolean mIsRtl; + + // Edge effect + private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect(); + private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect(); public interface PageSwitchListener { void onPageSwitch(View newPage, int newPageIndex); @@ -290,6 +232,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc a.recycle(); setHapticFeedbackEnabled(false); + mIsRtl = Utilities.isRtl(getResources()); init(); } @@ -297,8 +240,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc * Initializes various states for this workspace. */ protected void init() { - mDirtyPageContent = new ArrayList<Boolean>(); - mDirtyPageContent.ensureCapacity(32); mScroller = new LauncherScroller(getContext()); setDefaultInterpolator(new ScrollInterpolator()); mCurrentPage = 0; @@ -306,18 +247,19 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledPagingTouchSlop(); - mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mDensity = getResources().getDisplayMetrics().density; - // Scale the fling-to-delete threshold by the density - mFlingToDeleteThresholdVelocity = - (int) (mFlingToDeleteThresholdVelocity * mDensity); - mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity); mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity); setOnHierarchyChangeListener(this); + setWillNotDraw(false); + } + + protected void setEdgeGlowColor(int color) { + mEdgeGlowLeft.setColor(color); + mEdgeGlowRight.setColor(color); } protected void setDefaultInterpolator(Interpolator interpolator) { @@ -333,7 +275,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc ViewGroup grandParent = (ViewGroup) parent.getParent(); if (mPageIndicator == null && mPageIndicatorViewId > -1) { mPageIndicator = (PageIndicator) grandParent.findViewById(mPageIndicatorViewId); - mPageIndicator.removeAllMarkers(mAllowPagedViewAnimations); + mPageIndicator.removeAllMarkers(true); ArrayList<PageIndicator.PageMarkerResources> markers = new ArrayList<PageIndicator.PageMarkerResources>(); @@ -341,7 +283,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc markers.add(getPageIndicatorMarker(i)); } - mPageIndicator.addMarkers(markers, mAllowPagedViewAnimations); + mPageIndicator.addMarkers(markers, true); OnClickListener listener = getPageIndicatorClickListener(); if (listener != null) { @@ -359,33 +301,31 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return null; } + @Override protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); // Unhook the page indicator mPageIndicator = null; } - void setDeleteDropTarget(View v) { - mDeleteDropTarget = v; - } - // Convenience methods to map points from self to parent and vice versa - float[] mapPointFromViewToParent(View v, float x, float y) { - mTmpPoint[0] = x; - mTmpPoint[1] = y; - v.getMatrix().mapPoints(mTmpPoint); - mTmpPoint[0] += v.getLeft(); - mTmpPoint[1] += v.getTop(); - return mTmpPoint; - } - float[] mapPointFromParentToView(View v, float x, float y) { - mTmpPoint[0] = x - v.getLeft(); - mTmpPoint[1] = y - v.getTop(); - v.getMatrix().invert(mTmpInvMatrix); - mTmpInvMatrix.mapPoints(mTmpPoint); - return mTmpPoint; - } - - void updateDragViewTranslationDuringDrag() { + private float[] mapPointFromViewToParent(View v, float x, float y) { + sTmpPoint[0] = x; + sTmpPoint[1] = y; + v.getMatrix().mapPoints(sTmpPoint); + sTmpPoint[0] += v.getLeft(); + sTmpPoint[1] += v.getTop(); + return sTmpPoint; + } + private float[] mapPointFromParentToView(View v, float x, float y) { + sTmpPoint[0] = x - v.getLeft(); + sTmpPoint[1] = y - v.getTop(); + v.getMatrix().invert(sTmpInvMatrix); + sTmpInvMatrix.mapPoints(sTmpPoint); + return sTmpPoint; + } + + private void updateDragViewTranslationDuringDrag() { if (mDragView != null) { float x = (mLastMotionX - mDownMotionX) + (getScrollX() - mDownScrollX) + (mDragViewBaselineLeft - mDragView.getLeft()); @@ -453,33 +393,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } /** - * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api. - */ - public boolean isLayoutRtl() { - return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); - } - - /** - * Called by subclasses to mark that data is ready, and that we can begin loading and laying - * out pages. - */ - protected void setDataIsReady() { - mIsDataReady = true; - } - - protected boolean isDataReady() { - return mIsDataReady; - } - - /** * Returns the index of the currently displayed page. - * - * @return The index of the currently displayed page. */ - int getCurrentPage() { + public int getCurrentPage() { return mCurrentPage; } + /** + * Returns the index of page to be shown immediately afterwards. + */ int getNextPage() { return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; } @@ -488,7 +410,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return getChildCount(); } - View getPageAt(int index) { + public View getPageAt(int index) { return getChildAt(index); } @@ -512,17 +434,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc forceFinishScroller(); } - /** - * Called during AllApps/Home transitions to avoid unnecessary work. When that other animation - * {@link #updateCurrentPageScroll()} should be called, to correctly set the final state and - * re-enable scrolling. - */ - void stopScrolling() { - mCurrentPage = getNextPage(); - notifyPageSwitchListener(); - forceFinishScroller(); - } - private void abortScrollerAnimation(boolean resetNextPage) { mScroller.abortAnimation(); // We need to clean up the next page here to avoid computeScrollHelper from @@ -555,7 +466,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc /** * Sets the current page. */ - void setCurrentPage(int currentPage) { + public void setCurrentPage(int currentPage) { if (!mScroller.isFinished()) { abortScrollerAnimation(true); } @@ -647,37 +558,41 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override public void scrollBy(int x, int y) { - scrollTo(mUnboundedScrollX + x, getScrollY() + y); + scrollTo(getScrollX() + x, getScrollY() + y); } @Override public void scrollTo(int x, int y) { // In free scroll mode, we clamp the scrollX if (mFreeScroll) { + // If the scroller is trying to move to a location beyond the maximum allowed + // in the free scroll mode, we make sure to end the scroll operation. + if (!mScroller.isFinished() && + (x > mFreeScrollMaxScrollX || x < mFreeScrollMinScrollX)) { + forceFinishScroller(); + } + x = Math.min(x, mFreeScrollMaxScrollX); x = Math.max(x, mFreeScrollMinScrollX); } - final boolean isRtl = isLayoutRtl(); - mUnboundedScrollX = x; - - boolean isXBeforeFirstPage = isRtl ? (x > mMaxScrollX) : (x < 0); - boolean isXAfterLastPage = isRtl ? (x < 0) : (x > mMaxScrollX); + boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0); + boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX); if (isXBeforeFirstPage) { - super.scrollTo(0, y); + super.scrollTo(mIsRtl ? mMaxScrollX : 0, y); if (mAllowOverScroll) { mWasInOverscroll = true; - if (isRtl) { + if (mIsRtl) { overScroll(x - mMaxScrollX); } else { overScroll(x); } } } else if (isXAfterLastPage) { - super.scrollTo(mMaxScrollX, y); + super.scrollTo(mIsRtl ? 0 : mMaxScrollX, y); if (mAllowOverScroll) { mWasInOverscroll = true; - if (isRtl) { + if (mIsRtl) { overScroll(x); } else { overScroll(x - mMaxScrollX); @@ -688,7 +603,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc overScroll(0); mWasInOverscroll = false; } - mOverScrollX = x; super.scrollTo(x, y); } @@ -708,21 +622,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc AccessibilityManager am = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); if (am.isEnabled()) { - AccessibilityEvent ev = - AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); - ev.setItemCount(getChildCount()); - ev.setFromIndex(mCurrentPage); - ev.setToIndex(getNextPage()); - - final int action; - if (getNextPage() >= mCurrentPage) { - action = AccessibilityNodeInfo.ACTION_SCROLL_FORWARD; - } else { - action = AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD; + if (mCurrentPage != getNextPage()) { + AccessibilityEvent ev = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); + ev.setScrollable(true); + ev.setScrollX(getScrollX()); + ev.setScrollY(getScrollY()); + ev.setMaxScrollX(mMaxScrollX); + ev.setMaxScrollY(0); + + sendAccessibilityEventUnchecked(ev); } - - ev.setAction(action); - sendAccessibilityEventUnchecked(ev); } } @@ -731,8 +641,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (mScroller.computeScrollOffset()) { // Don't bother scrolling if the page does not need to be moved if (getScrollX() != mScroller.getCurrX() - || getScrollY() != mScroller.getCurrY() - || mOverScrollX != mScroller.getCurrX()) { + || getScrollY() != mScroller.getCurrY()) { float scaleX = mFreeScroll ? getScaleX() : 1f; int scrollX = (int) (mScroller.getCurrX() * (1 / scaleX)); scrollTo(scrollX, mScroller.getCurrY()); @@ -746,12 +655,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mNextPage = INVALID_PAGE; notifyPageSwitchListener(); - // Load the associated pages if necessary - if (mDeferLoadAssociatedPagesUntilScrollCompletes) { - loadAssociatedPages(mCurrentPage); - mDeferLoadAssociatedPagesUntilScrollCompletes = false; - } - // We don't want to trigger a page end moving unless the page has settled // and the user has stopped scrolling if (mTouchState == TOUCH_STATE_REST) { @@ -775,10 +678,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc computeScrollHelper(); } - protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) { - return mTopAlignPageWhenShrinkingForBouncer; - } - public static class LayoutParams extends ViewGroup.LayoutParams { public boolean isFullScreenPage = false; @@ -789,15 +688,35 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc super(width, height); } + public LayoutParams(Context context, AttributeSet attrs) { + super(context, attrs); + } + public LayoutParams(ViewGroup.LayoutParams source) { super(source); } } + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + public void addFullScreenPage(View page) { LayoutParams lp = generateDefaultLayoutParams(); lp.isFullScreenPage = true; @@ -810,7 +729,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (!mIsDataReady || getChildCount() == 0) { + if (getChildCount() == 0) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } @@ -914,27 +833,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } - if (mSpacePagesAutomatically) { - int spacing = (getViewportWidth() - mInsets.left - mInsets.right - - referenceChildWidth) / 2; - if (spacing >= 0) { - setPageSpacing(spacing); - } - mSpacePagesAutomatically = false; - } setMeasuredDimension(scaledWidthSize, scaledHeightSize); } - /** - * This method should be called once before first layout / measure pass. - */ - protected void setSinglePageInViewport() { - mSpacePagesAutomatically = true; - } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (!mIsDataReady || getChildCount() == 0) { + if (getChildCount() == 0) { return; } @@ -945,13 +849,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int offsetY = getViewportOffsetY(); // Update the viewport offsets - mViewport.offset(offsetX, offsetY); + mViewport.offset(offsetX, offsetY); - final boolean isRtl = isLayoutRtl(); - - final int startIndex = isRtl ? childCount - 1 : 0; - final int endIndex = isRtl ? -1 : childCount; - final int delta = isRtl ? -1 : 1; + final int startIndex = mIsRtl ? childCount - 1 : 0; + final int endIndex = mIsRtl ? -1 : childCount; + final int delta = mIsRtl ? -1 : 1; int verticalPadding = getPaddingTop() + getPaddingBottom(); @@ -959,8 +861,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc LayoutParams nextLp; int childLeft = offsetX + (lp.isFullScreenPage ? 0 : getPaddingLeft()); - if (mPageScrolls == null || getChildCount() != mChildCountOnLastLayout) { - mPageScrolls = new int[getChildCount()]; + if (mPageScrolls == null || childCount != mChildCountOnLastLayout) { + mPageScrolls = new int[childCount]; } for (int i = startIndex; i != endIndex; i += delta) { @@ -1003,24 +905,40 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc pageGap = getPaddingRight(); } - childLeft += childWidth + pageGap; + childLeft += childWidth + pageGap + getChildGap(); } } - if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { + if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) { updateCurrentPageScroll(); mFirstLayout = false; } - if (childCount > 0) { - final int index = isLayoutRtl() ? 0 : childCount - 1; - mMaxScrollX = getScrollForPage(index); + final LayoutTransition transition = getLayoutTransition(); + // If the transition is running defer updating max scroll, as some empty pages could + // still be present, and a max scroll change could cause sudden jumps in scroll. + if (transition != null && transition.isRunning()) { + transition.addTransitionListener(new LayoutTransition.TransitionListener() { + + @Override + public void startTransition(LayoutTransition transition, ViewGroup container, + View view, int transitionType) { } + + @Override + public void endTransition(LayoutTransition transition, ViewGroup container, + View view, int transitionType) { + // Wait until all transitions are complete. + if (!transition.isRunning()) { + transition.removeTransitionListener(this); + updateMaxScrollX(); + } + } + }); } else { - mMaxScrollX = 0; + updateMaxScrollX(); } - if (mScroller.isFinished() && mChildCountOnLastLayout != getChildCount() && - !mDeferringForDelete) { + if (mScroller.isFinished() && mChildCountOnLastLayout != childCount) { if (mRestorePage != INVALID_RESTORE_PAGE) { setCurrentPage(mRestorePage); mRestorePage = INVALID_RESTORE_PAGE; @@ -1028,42 +946,37 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc setCurrentPage(getNextPage()); } } - mChildCountOnLastLayout = getChildCount(); + mChildCountOnLastLayout = childCount; if (isReordering(true)) { updateDragViewTranslationDuringDrag(); } } - public void setPageSpacing(int pageSpacing) { - mPageSpacing = pageSpacing; - requestLayout(); + protected int getChildGap() { + return 0; } - protected void screenScrolled(int screenCenter) { - boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; - - if (mFadeInAdjacentScreens && !isInOverscroll) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child != null) { - float scrollProgress = getScrollProgress(screenCenter, child, i); - float alpha = 1 - Math.abs(scrollProgress); - child.setAlpha(alpha); - } - } - invalidate(); + @Thunk void updateMaxScrollX() { + int childCount = getChildCount(); + if (childCount > 0) { + final int index = mIsRtl ? 0 : childCount - 1; + mMaxScrollX = getScrollForPage(index); + } else { + mMaxScrollX = 0; } } - protected void enablePagedViewAnimations() { - mAllowPagedViewAnimations = true; - - } - protected void disablePagedViewAnimations() { - mAllowPagedViewAnimations = false; + public void setPageSpacing(int pageSpacing) { + mPageSpacing = pageSpacing; + requestLayout(); } + /** + * Called when the center screen changes during scrolling. + */ + protected void screenScrolled(int screenCenter) { } + @Override public void onChildViewAdded(View parent, View child) { // Update the page indicator, we don't update the page indicator as we @@ -1072,7 +985,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int pageIndex = indexOfChild(child); mPageIndicator.addMarker(pageIndex, getPageIndicatorMarker(pageIndex), - mAllowPagedViewAnimations); + true); } // This ensures that when children are added, they get the correct transforms / alphas @@ -1093,7 +1006,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Update the page indicator, we don't update the page indicator as we // add/remove pages if (mPageIndicator != null && !isReordering(false)) { - mPageIndicator.removeMarker(index, mAllowPagedViewAnimations); + mPageIndicator.removeMarker(index, true); } } @@ -1115,7 +1028,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public void removeViewAt(int index) { // XXX: We should find a better way to hook into this before the view // gets removed form its parent... - removeViewAt(index); + removeMarkerForView(index); super.removeViewAt(index); } @Override @@ -1123,7 +1036,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Update the page indicator, we don't update the page indicator as we // add/remove pages if (mPageIndicator != null) { - mPageIndicator.removeAllMarkers(mAllowPagedViewAnimations); + mPageIndicator.removeAllMarkers(true); } super.removeAllViewsInLayout(); @@ -1144,7 +1057,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected void getVisiblePages(int[] range) { final int pageCount = getChildCount(); - mTmpIntPoint[0] = mTmpIntPoint[1] = 0; + sTmpIntPoint[0] = sTmpIntPoint[1] = 0; range[0] = -1; range[1] = -1; @@ -1157,9 +1070,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc for (int i = 0; i < count; i++) { View currPage = getPageAt(i); - mTmpIntPoint[0] = 0; - Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false); - if (mTmpIntPoint[0] > viewportWidth) { + sTmpIntPoint[0] = 0; + Utilities.getDescendantCoordRelativeToParent(currPage, this, sTmpIntPoint, false); + if (sTmpIntPoint[0] > viewportWidth) { if (range[0] == -1) { continue; } else { @@ -1167,9 +1080,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - mTmpIntPoint[0] = currPage.getMeasuredWidth(); - Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false); - if (mTmpIntPoint[0] < 0) { + sTmpIntPoint[0] = currPage.getMeasuredWidth(); + Utilities.getDescendantCoordRelativeToParent(currPage, this, sTmpIntPoint, false); + if (sTmpIntPoint[0] < 0) { if (range[0] == -1) { continue; } else { @@ -1199,9 +1112,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final int pageCount = getChildCount(); if (pageCount > 0) { int halfScreenSize = getViewportWidth() / 2; - // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. - // Otherwise it is equal to the scaled overscroll position. - int screenCenter = mOverScrollX + halfScreenSize; + int screenCenter = getScrollX() + halfScreenSize; if (screenCenter != mLastScreenCenter || mForceScreenScrolled) { // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can @@ -1242,6 +1153,46 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (getPageCount() > 0) { + if (!mEdgeGlowLeft.isFinished()) { + final int restoreCount = canvas.save(); + Rect display = mViewport; + canvas.translate(display.left, display.top); + canvas.rotate(270); + + getEdgeVerticalPostion(sTmpIntPoint); + canvas.translate(display.top - sTmpIntPoint[1], 0); + mEdgeGlowLeft.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width()); + if (mEdgeGlowLeft.draw(canvas)) { + postInvalidateOnAnimation(); + } + canvas.restoreToCount(restoreCount); + } + if (!mEdgeGlowRight.isFinished()) { + final int restoreCount = canvas.save(); + Rect display = mViewport; + canvas.translate(display.left + mPageScrolls[getChildCount() - 1], display.top); + canvas.rotate(90); + + getEdgeVerticalPostion(sTmpIntPoint); + canvas.translate(sTmpIntPoint[0] - display.top, -display.width()); + mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width()); + if (mEdgeGlowRight.draw(canvas)) { + postInvalidateOnAnimation(); + } + canvas.restoreToCount(restoreCount); + } + } + } + + /** + * Returns the top and bottom position for the edge effect. + */ + protected abstract void getEdgeVerticalPostion(int[] pos); + + @Override public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { int page = indexToPage(indexOfChild(child)); if (page != mCurrentPage || !mScroller.isFinished()) { @@ -1346,7 +1297,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc * Return true if a tap at (x, y) should trigger a flip to the previous page. */ protected boolean hitsPreviousPage(float x, float y) { - if (isLayoutRtl()) { + if (mIsRtl) { return (x > (getViewportOffsetX() + getViewportWidth() - getPaddingRight() - mPageSpacing)); } @@ -1357,7 +1308,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc * Return true if a tap at (x, y) should trigger a flip to the next page. */ protected boolean hitsNextPage(float x, float y) { - if (isLayoutRtl()) { + if (mIsRtl) { return (x < getViewportOffsetX() + getPaddingLeft() + mPageSpacing); } return (x > (getViewportOffsetX() + getViewportWidth() - @@ -1366,17 +1317,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc /** Returns whether x and y originated within the buffered viewport */ private boolean isTouchPointInViewportWithBuffer(int x, int y) { - mTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top, + sTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top, mViewport.right + mViewport.width() / 2, mViewport.bottom); - return mTmpRect.contains(x, y); + return sTmpRect.contains(x, y); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (DISABLE_TOUCH_INTERACTION) { - return false; - } - /* * This method JUST determines whether we want to intercept the motion. * If we return true, onTouchEvent will be called and we do the actual @@ -1453,19 +1400,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - // check if this can be the beginning of a tap on the side of the pages - // to scroll the current page - if (!DISABLE_TOUCH_SIDE_PAGES) { - if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { - if (getChildCount() > 0) { - if (hitsPreviousPage(x, y)) { - mTouchState = TOUCH_STATE_PREV_PAGE; - } else if (hitsNextPage(x, y)) { - mTouchState = TOUCH_STATE_NEXT_PAGE; - } - } - } - } break; } @@ -1506,54 +1440,33 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return; final int xDiff = (int) Math.abs(x - mLastMotionX); - final int yDiff = (int) Math.abs(y - mLastMotionY); final int touchSlop = Math.round(touchSlopScale * mTouchSlop); - boolean xPaged = xDiff > mPagingTouchSlop; boolean xMoved = xDiff > touchSlop; - boolean yMoved = yDiff > touchSlop; - if (xMoved || xPaged || yMoved) { - if (mUsePagingTouchSlop ? xPaged : xMoved) { - // Scroll if the user moved far enough along the X axis - mTouchState = TOUCH_STATE_SCROLLING; - mTotalMotionX += Math.abs(mLastMotionX - x); - mLastMotionX = x; - mLastMotionXRemainder = 0; - mTouchX = getViewportOffsetX() + getScrollX(); - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - onScrollInteractionBegin(); - pageBeginMoving(); - } + if (xMoved) { + // Scroll if the user moved far enough along the X axis + mTouchState = TOUCH_STATE_SCROLLING; + mTotalMotionX += Math.abs(mLastMotionX - x); + mLastMotionX = x; + mLastMotionXRemainder = 0; + mTouchX = getViewportOffsetX() + getScrollX(); + mSmoothingTime = System.nanoTime() / NANOTIME_DIV; + onScrollInteractionBegin(); + pageBeginMoving(); } } - protected float getMaxScrollProgress() { - return 1.0f; - } - protected void cancelCurrentPageLongPress() { - if (mAllowLongPress) { - //mAllowLongPress = false; - // Try canceling the long press. It could also have been scheduled - // by a distant descendant, so use the mAllowLongPress flag to block - // everything - final View currentPage = getPageAt(mCurrentPage); - if (currentPage != null) { - currentPage.cancelLongPress(); - } + // Try canceling the long press. It could also have been scheduled + // by a distant descendant, so use the mAllowLongPress flag to block + // everything + final View currentPage = getPageAt(mCurrentPage); + if (currentPage != null) { + currentPage.cancelLongPress(); } } - protected float getBoundedScrollProgress(int screenCenter, View v, int page) { - final int halfScreenSize = getViewportWidth() / 2; - - screenCenter = Math.min(getScrollX() + halfScreenSize, screenCenter); - screenCenter = Math.max(halfScreenSize, screenCenter); - - return getScrollProgress(screenCenter, v, page); - } - protected float getScrollProgress(int screenCenter, View v, int page) { final int halfScreenSize = getViewportWidth() / 2; @@ -1563,7 +1476,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final int totalDistance; int adjacentPage = page + 1; - if ((delta < 0 && !isLayoutRtl()) || (delta > 0 && isLayoutRtl())) { + if ((delta < 0 && !mIsRtl) || (delta > 0 && mIsRtl)) { adjacentPage = page - 1; } @@ -1574,8 +1487,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } float scrollProgress = delta / (totalDistance * 1.0f); - scrollProgress = Math.min(scrollProgress, getMaxScrollProgress()); - scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress()); + scrollProgress = Math.min(scrollProgress, MAX_SCROLL_PROGRESS); + scrollProgress = Math.max(scrollProgress, - MAX_SCROLL_PROGRESS); return scrollProgress; } @@ -1598,7 +1511,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int scrollOffset = 0; LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.isFullScreenPage) { - scrollOffset = isLayoutRtl() ? getPaddingRight() : getPaddingLeft(); + scrollOffset = mIsRtl ? getPaddingRight() : getPaddingLeft(); } int baselineX = mPageScrolls[index] + scrollOffset + getViewportOffsetX(); @@ -1606,49 +1519,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - // This curve determines how the effect of scrolling over the limits of the page dimishes - // as the user pulls further and further from the bounds - private float overScrollInfluenceCurve(float f) { - f -= 1.0f; - return f * f * f + 1.0f; - } - - protected float acceleratedOverFactor(float amount) { - int screenSize = getViewportWidth(); - - // We want to reach the max over scroll effect when the user has - // over scrolled half the size of the screen - float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize); - - if (f == 0) return 0; - - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - return f; - } - protected void dampedOverScroll(float amount) { int screenSize = getViewportWidth(); - float f = (amount / screenSize); - - if (f == 0) return; - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); - - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - - int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); - if (amount < 0) { - mOverScrollX = overScrollAmount; - super.scrollTo(mOverScrollX, getScrollY()); + if (f < 0) { + mEdgeGlowLeft.onPull(-f); + } else if (f > 0) { + mEdgeGlowRight.onPull(f); } else { - mOverScrollX = mMaxScrollX + overScrollAmount; - super.scrollTo(mOverScrollX, getScrollY()); + return; } invalidate(); } @@ -1657,25 +1536,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc dampedOverScroll(amount); } - protected float maxOverScroll() { - // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not - // exceed). Used to find out how much extra wallpaper we need for the over scroll effect - float f = 1.0f; - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); - return OVERSCROLL_DAMP_FACTOR * f; - } - - protected void enableFreeScroll() { + public void enableFreeScroll() { setEnableFreeScroll(true); } - protected void disableFreeScroll() { + public void disableFreeScroll() { setEnableFreeScroll(false); } void updateFreescrollBounds() { getFreeScrollPageRange(mTempVisiblePagesRange); - if (isLayoutRtl()) { + if (mIsRtl) { mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[1]); mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[0]); } else { @@ -1700,11 +1571,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc setEnableOverscroll(!freeScroll); } - private void setEnableOverscroll(boolean enable) { + protected void setEnableOverscroll(boolean enable) { mAllowOverScroll = enable; } - int getNearestHoverOverPageIndex() { + private int getNearestHoverOverPageIndex() { if (mDragView != null) { int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2) + mDragView.getTranslationX()); @@ -1727,10 +1598,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override public boolean onTouchEvent(MotionEvent ev) { - if (DISABLE_TOUCH_INTERACTION) { - return false; - } - super.onTouchEvent(ev); // Skip touch handling if there are no pages to swipe @@ -1785,12 +1652,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (Math.abs(deltaX) >= 1.0f) { mTouchX += deltaX; mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - if (!mDeferScrollUpdate) { - scrollBy((int) deltaX, 0); - if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX); - } else { - invalidate(); - } + scrollBy((int) deltaX, 0); mLastMotionX = x; mLastMotionXRemainder = deltaX - (int) deltaX; } else { @@ -1811,19 +1673,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Find the closest page to the touch point final int dragViewIndex = indexOfChild(mDragView); - // Change the drag view if we are hovering over the drop target - boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget( - (int) mParentDownMotionX, (int) mParentDownMotionY); - setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete); - if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX); if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY); if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX); if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY); final int pageUnderPointIndex = getNearestHoverOverPageIndex(); - if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView) && - !isHoveringOverDelete) { + if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView)) { mTempVisiblePagesRange[0] = 0; mTempVisiblePagesRange[1] = getPageCount() - 1; getFreeScrollPageRange(mTempVisiblePagesRange); @@ -1870,9 +1726,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } removeView(mDragView); - onRemoveView(mDragView, false); addView(mDragView, pageUnderPointIndex); - onAddView(mDragView, pageUnderPointIndex); mSidePageHoverIndex = -1; if (mPageIndicator != null) { mPageIndicator.setActiveMarker(getNextPage()); @@ -1922,9 +1776,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // We give flings precedence over large moves, which is why we short-circuit our // test for a large move if a fling has been registered. That is, a large // move to the left and fling to the right will register as a fling to the right. - final boolean isRtl = isLayoutRtl(); - boolean isDeltaXLeft = isRtl ? deltaX > 0 : deltaX < 0; - boolean isVelocityXLeft = isRtl ? velocityX > 0 : velocityX < 0; + boolean isDeltaXLeft = mIsRtl ? deltaX > 0 : deltaX < 0; + boolean isVelocityXLeft = mIsRtl ? velocityX > 0 : velocityX < 0; if (((isSignificantMove && !isDeltaXLeft && !isFling) || (isFling && !isVelocityXLeft)) && mCurrentPage > 0) { finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; @@ -1983,19 +1836,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mParentDownMotionX = pt[0]; mParentDownMotionY = pt[1]; updateDragViewTranslationDuringDrag(); - boolean handledFling = false; - if (!DISABLE_FLING_TO_DELETE) { - // Check the velocity and see if we are flinging-to-delete - PointF flingToDeleteVector = isFlingingToDelete(); - if (flingToDeleteVector != null) { - onFlingToDelete(flingToDeleteVector); - handledFling = true; - } - } - if (!handledFling && isHoveringOverDeleteDropTarget((int) mParentDownMotionX, - (int) mParentDownMotionY)) { - onDropToDelete(); - } } else { if (!mCancelTap) { onUnhandledTap(ev); @@ -2024,17 +1864,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return true; } - public void onFlingToDelete(View v) {} - public void onRemoveView(View v, boolean deletePermanently) {} - public void onRemoveViewAnimationCompleted() {} - public void onAddView(View v, int index) {} - private void resetTouchState() { releaseVelocityTracker(); endReordering(); mCancelTap = false; mTouchState = TOUCH_STATE_REST; mActivePointerId = INVALID_POINTER; + mEdgeGlowLeft.onRelease(); + mEdgeGlowRight.onRelease(); } /** @@ -2066,7 +1903,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); } if (hscroll != 0 || vscroll != 0) { - boolean isForwardScroll = isLayoutRtl() ? (hscroll < 0 || vscroll < 0) + boolean isForwardScroll = mIsRtl ? (hscroll < 0 || vscroll < 0) : (hscroll > 0 || vscroll > 0); if (isForwardScroll) { scrollRight(); @@ -2124,22 +1961,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - protected int getChildWidth(int index) { - return getPageAt(index).getMeasuredWidth(); - } - - int getPageNearestToPoint(float x) { - int index = 0; - for (int i = 0; i < getChildCount(); ++i) { - if (x < getChildAt(i).getRight() - getScrollX()) { - return index; - } else { - index++; - } - } - return Math.min(index, getChildCount() - 1); - } - int getPageNearestToCenterOfScreen() { int minDistanceFromScreenCenter = Integer.MAX_VALUE; int minDistanceFromScreenCenterIndex = -1; @@ -2159,20 +1980,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return minDistanceFromScreenCenterIndex; } - protected boolean isInOverScroll() { - return (mOverScrollX > mMaxScrollX || mOverScrollX < 0); - } - - protected int getPageSnapDuration() { - if (isInOverScroll()) { - return OVER_SCROLL_PAGE_SNAP_ANIMATION_DURATION; - } - return PAGE_SNAP_ANIMATION_DURATION; - - } - protected void snapToDestination() { - snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration()); + snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); } private static class ScrollInterpolator implements Interpolator { @@ -2189,7 +1998,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // the screen has to travel, however, we don't want this duration to be effected in a // purely linear fashion. Instead, we use this method to moderate the effect that the distance // of travel has on the overall snap duration. - float distanceInfluenceForSnapDuration(float f) { + private float distanceInfluenceForSnapDuration(float f) { f -= 0.5f; // center the values about 0. f *= 0.3f * Math.PI / 2.0f; return (float) Math.sin(f); @@ -2200,13 +2009,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int halfScreenSize = getViewportWidth() / 2; final int newX = getScrollForPage(whichPage); - int delta = newX - mUnboundedScrollX; + int delta = newX - getScrollX(); int duration = 0; - if (Math.abs(velocity) < mMinFlingVelocity || isInOverScroll()) { + if (Math.abs(velocity) < mMinFlingVelocity) { // If the velocity is low enough, then treat this more as an automatic page advance // as opposed to an apparent physical response to flinging - snapToPage(whichPage, getPageSnapDuration()); + snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); return; } @@ -2229,12 +2038,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc snapToPage(whichPage, delta, duration); } - protected void snapToPage(int whichPage) { - snapToPage(whichPage, getPageSnapDuration()); + public void snapToPage(int whichPage) { + snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); } protected void snapToPageImmediately(int whichPage) { - snapToPage(whichPage, getPageSnapDuration(), true, null); + snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null); } protected void snapToPage(int whichPage, int duration) { @@ -2250,8 +2059,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc whichPage = validateNewPage(whichPage); int newX = getScrollForPage(whichPage); - final int sX = mUnboundedScrollX; - final int delta = newX - sX; + final int delta = newX - getScrollX(); snapToPage(whichPage, delta, duration, immediate, interpolator); } @@ -2270,8 +2078,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc focusedChild.clearFocus(); } - sendScrollAccessibilityEvent(); - pageBeginMoving(); awakenScrollBars(duration); if (immediate) { @@ -2290,7 +2096,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mScroller.setInterpolator(mDefaultInterpolator); } - mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); + mScroller.startScroll(getScrollX(), 0, delta, 0, duration); updatePageIndicator(); @@ -2299,9 +2105,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc computeScroll(); } - // Defer loading associated pages until the scroll settles - mDeferLoadAssociatedPagesUntilScrollCompletes = true; - mForceScreenScrolled = true; invalidate(); } @@ -2328,27 +2131,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return result; } - /** - * @return True is long presses are still allowed for the current touch - */ - public boolean allowLongPress() { - return mAllowLongPress; - } - @Override public boolean performLongClick() { mCancelTap = true; return super.performLongClick(); } - /** - * Set true to allow long-press events to be triggered, usually checked by - * {@link Launcher} to accept or block dpad-initiated long-presses. - */ - public void setAllowLongPress(boolean allowLongPress) { - mAllowLongPress = allowLongPress; - } - public static class SavedState extends BaseSavedState { int currentPage = -1; @@ -2356,7 +2144,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc super(superState); } - private SavedState(Parcel in) { + @Thunk SavedState(Parcel in) { super(in); currentPage = in.readInt(); } @@ -2379,113 +2167,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc }; } - protected void loadAssociatedPages(int page) { - loadAssociatedPages(page, false); - } - protected void loadAssociatedPages(int page, boolean immediateAndOnly) { - if (mContentIsRefreshable) { - final int count = getChildCount(); - if (page < count) { - int lowerPageBound = getAssociatedLowerPageBound(page); - int upperPageBound = getAssociatedUpperPageBound(page); - if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/" - + upperPageBound); - // First, clear any pages that should no longer be loaded - for (int i = 0; i < count; ++i) { - Page layout = (Page) getPageAt(i); - if ((i < lowerPageBound) || (i > upperPageBound)) { - if (layout.getPageChildCount() > 0) { - layout.removeAllViewsOnPage(); - } - mDirtyPageContent.set(i, true); - } - } - // Next, load any new pages - for (int i = 0; i < count; ++i) { - if ((i != page) && immediateAndOnly) { - continue; - } - if (lowerPageBound <= i && i <= upperPageBound) { - if (mDirtyPageContent.get(i)) { - syncPageItems(i, (i == page) && immediateAndOnly); - mDirtyPageContent.set(i, false); - } - } - } - } - } - } - - protected int getAssociatedLowerPageBound(int page) { - return Math.max(0, page - 1); - } - protected int getAssociatedUpperPageBound(int page) { - final int count = getChildCount(); - return Math.min(page + 1, count - 1); - } - - /** - * This method is called ONLY to synchronize the number of pages that the paged view has. - * To actually fill the pages with information, implement syncPageItems() below. It is - * guaranteed that syncPageItems() will be called for a particular page before it is shown, - * and therefore, individual page items do not need to be updated in this method. - */ - public abstract void syncPages(); - - /** - * This method is called to synchronize the items that are on a particular page. If views on - * the page can be reused, then they should be updated within this method. - */ - public abstract void syncPageItems(int page, boolean immediate); - - protected void invalidatePageData() { - invalidatePageData(-1, false); - } - protected void invalidatePageData(int currentPage) { - invalidatePageData(currentPage, false); - } - protected void invalidatePageData(int currentPage, boolean immediateAndOnly) { - if (!mIsDataReady) { - return; - } - - if (mContentIsRefreshable) { - // Force all scrolling-related behavior to end - forceFinishScroller(); - - // Update all the pages - syncPages(); - - // We must force a measure after we've loaded the pages to update the content width and - // to determine the full scroll width - measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - - // Set a new page as the current page if necessary - if (currentPage > -1) { - setCurrentPage(Math.min(getPageCount() - 1, currentPage)); - } - - // Mark each of the pages as dirty - final int count = getChildCount(); - mDirtyPageContent.clear(); - for (int i = 0; i < count; ++i) { - mDirtyPageContent.add(true); - } - - // Load any pages that are necessary for the current window of views - loadAssociatedPages(mCurrentPage, immediateAndOnly); - requestLayout(); - } - if (isPageMoving()) { - // If the page is moving, then snap it to the final position to ensure we don't get - // stuck between pages - snapToDestination(); - } - } - // Animate the drag view back to the original position - void animateDragViewToOriginalPosition() { + private void animateDragViewToOriginalPosition() { if (mDragView != null) { AnimatorSet anim = new AnimatorSet(); anim.setDuration(REORDERING_DROP_REPOSITION_DURATION); @@ -2504,7 +2187,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - protected void onStartReordering() { + public void onStartReordering() { // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.) mTouchState = TOUCH_STATE_REORDERING; mIsReordering = true; @@ -2514,7 +2197,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc invalidate(); } - private void onPostReorderingAnimationCompleted() { + @Thunk void onPostReorderingAnimationCompleted() { // Trigger the callback when reordering has settled --mPostReorderingPreZoomInRemainingAnimationCount; if (mPostReorderingPreZoomInRunnable != null && @@ -2524,7 +2207,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - protected void onEndReordering() { + public void onEndReordering() { mIsReordering = false; } @@ -2574,281 +2257,26 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc onEndReordering(); } }; - if (!mDeferringForDelete) { - mPostReorderingPreZoomInRunnable = new Runnable() { - public void run() { - onCompleteRunnable.run(); - enableFreeScroll(); - }; - }; - - mPostReorderingPreZoomInRemainingAnimationCount = - NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT; - // Snap to the current page - snapToPage(indexOfChild(mDragView), 0); - // Animate the drag view back to the front position - animateDragViewToOriginalPosition(); - } else { - // Handled in post-delete-animation-callbacks - } - } - - /* - * Flinging to delete - IN PROGRESS - */ - private PointF isFlingingToDelete() { - ViewConfiguration config = ViewConfiguration.get(getContext()); - mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity()); - - if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) { - // Do a quick dot product test to ensure that we are flinging upwards - PointF vel = new PointF(mVelocityTracker.getXVelocity(), - mVelocityTracker.getYVelocity()); - PointF upVec = new PointF(0f, -1f); - float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) / - (vel.length() * upVec.length())); - if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) { - return vel; - } - } - return null; - } - - /** - * Creates an animation from the current drag view along its current velocity vector. - * For this animation, the alpha runs for a fixed duration and we update the position - * progressively. - */ - private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener { - private View mDragView; - private PointF mVelocity; - private Rect mFrom; - private long mPrevTime; - private float mFriction; - - private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); - - public FlingAlongVectorAnimatorUpdateListener(View dragView, PointF vel, Rect from, - long startTime, float friction) { - mDragView = dragView; - mVelocity = vel; - mFrom = from; - mPrevTime = startTime; - mFriction = 1f - (mDragView.getResources().getDisplayMetrics().density * friction); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = ((Float) animation.getAnimatedValue()).floatValue(); - long curTime = AnimationUtils.currentAnimationTimeMillis(); - - mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f); - mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f); - - mDragView.setTranslationX(mFrom.left); - mDragView.setTranslationY(mFrom.top); - mDragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t)); - mVelocity.x *= mFriction; - mVelocity.y *= mFriction; - mPrevTime = curTime; - } - }; - - private static final int ANIM_TAG_KEY = 100; - - private Runnable createPostDeleteAnimationRunnable(final View dragView) { - return new Runnable() { - @Override + mPostReorderingPreZoomInRunnable = new Runnable() { public void run() { - int dragViewIndex = indexOfChild(dragView); - - // For each of the pages around the drag view, animate them from the previous - // position to the new position in the layout (as a result of the drag view moving - // in the layout) - // NOTE: We can make an assumption here because we have side-bound pages that we - // will always have pages to animate in from the left - getFreeScrollPageRange(mTempVisiblePagesRange); - boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]); - boolean slideFromLeft = (isLastWidgetPage || - dragViewIndex > mTempVisiblePagesRange[0]); - - // Setup the scroll to the correct page before we swap the views - if (slideFromLeft) { - snapToPageImmediately(dragViewIndex - 1); - } - - int firstIndex = (isLastWidgetPage ? 0 : mTempVisiblePagesRange[0]); - int lastIndex = Math.min(mTempVisiblePagesRange[1], getPageCount() - 1); - int lowerIndex = (slideFromLeft ? firstIndex : dragViewIndex + 1 ); - int upperIndex = (slideFromLeft ? dragViewIndex - 1 : lastIndex); - ArrayList<Animator> animations = new ArrayList<Animator>(); - for (int i = lowerIndex; i <= upperIndex; ++i) { - View v = getChildAt(i); - // dragViewIndex < pageUnderPointIndex, so after we remove the - // drag view all subsequent views to pageUnderPointIndex will - // shift down. - int oldX = 0; - int newX = 0; - if (slideFromLeft) { - if (i == 0) { - // Simulate the page being offscreen with the page spacing - oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i) - - mPageSpacing; - } else { - oldX = getViewportOffsetX() + getChildOffset(i - 1); - } - newX = getViewportOffsetX() + getChildOffset(i); - } else { - oldX = getChildOffset(i) - getChildOffset(i - 1); - newX = 0; - } - - // Animate the view translation from its old position to its new - // position - AnimatorSet anim = (AnimatorSet) v.getTag(); - if (anim != null) { - anim.cancel(); - } - - // Note: Hacky, but we want to skip any optimizations to not draw completely - // hidden views - v.setAlpha(Math.max(v.getAlpha(), 0.01f)); - v.setTranslationX(oldX - newX); - anim = new AnimatorSet(); - anim.playTogether( - ObjectAnimator.ofFloat(v, "translationX", 0f), - ObjectAnimator.ofFloat(v, "alpha", 1f)); - animations.add(anim); - v.setTag(ANIM_TAG_KEY, anim); - } - - AnimatorSet slideAnimations = new AnimatorSet(); - slideAnimations.playTogether(animations); - slideAnimations.setDuration(DELETE_SLIDE_IN_SIDE_PAGE_DURATION); - slideAnimations.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mDeferringForDelete = false; - onEndReordering(); - onRemoveViewAnimationCompleted(); - } - }); - slideAnimations.start(); - - removeView(dragView); - onRemoveView(dragView, true); - } - }; - } - - public void onFlingToDelete(PointF vel) { - final long startTime = AnimationUtils.currentAnimationTimeMillis(); - - // NOTE: Because it takes time for the first frame of animation to actually be - // called and we expect the animation to be a continuation of the fling, we have - // to account for the time that has elapsed since the fling finished. And since - // we don't have a startDelay, we will always get call to update when we call - // start() (which we want to ignore). - final TimeInterpolator tInterpolator = new TimeInterpolator() { - private int mCount = -1; - private long mStartTime; - private float mOffset; - /* Anonymous inner class ctor */ { - mStartTime = startTime; - } - - @Override - public float getInterpolation(float t) { - if (mCount < 0) { - mCount++; - } else if (mCount == 0) { - mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() - - mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION); - mCount++; - } - return Math.min(1f, mOffset + t); - } + onCompleteRunnable.run(); + enableFreeScroll(); + }; }; - final Rect from = new Rect(); - final View dragView = mDragView; - from.left = (int) dragView.getTranslationX(); - from.top = (int) dragView.getTranslationY(); - AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel, - from, startTime, FLING_TO_DELETE_FRICTION); - - final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView); - - // Create and start the animation - ValueAnimator mDropAnim = new ValueAnimator(); - mDropAnim.setInterpolator(tInterpolator); - mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION); - mDropAnim.setFloatValues(0f, 1f); - mDropAnim.addUpdateListener(updateCb); - mDropAnim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - onAnimationEndRunnable.run(); - } - }); - mDropAnim.start(); - mDeferringForDelete = true; - } - - /* Drag to delete */ - private boolean isHoveringOverDeleteDropTarget(int x, int y) { - if (mDeleteDropTarget != null) { - mAltTmpRect.set(0, 0, 0, 0); - View parent = (View) mDeleteDropTarget.getParent(); - if (parent != null) { - parent.getGlobalVisibleRect(mAltTmpRect); - } - mDeleteDropTarget.getGlobalVisibleRect(mTmpRect); - mTmpRect.offset(-mAltTmpRect.left, -mAltTmpRect.top); - return mTmpRect.contains(x, y); - } - return false; + mPostReorderingPreZoomInRemainingAnimationCount = + NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT; + // Snap to the current page + snapToPage(indexOfChild(mDragView), 0); + // Animate the drag view back to the front position + animateDragViewToOriginalPosition(); } - protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {} - - private void onDropToDelete() { - final View dragView = mDragView; - - final float toScale = 0f; - final float toAlpha = 0f; - - // Create and start the complex animation - ArrayList<Animator> animations = new ArrayList<Animator>(); - AnimatorSet motionAnim = new AnimatorSet(); - motionAnim.setInterpolator(new DecelerateInterpolator(2)); - motionAnim.playTogether( - ObjectAnimator.ofFloat(dragView, "scaleX", toScale), - ObjectAnimator.ofFloat(dragView, "scaleY", toScale)); - animations.add(motionAnim); - - AnimatorSet alphaAnim = new AnimatorSet(); - alphaAnim.setInterpolator(new LinearInterpolator()); - alphaAnim.playTogether( - ObjectAnimator.ofFloat(dragView, "alpha", toAlpha)); - animations.add(alphaAnim); - - final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView); - - AnimatorSet anim = new AnimatorSet(); - anim.playTogether(animations); - anim.setDuration(DRAG_TO_DELETE_FADE_OUT_DURATION); - anim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - onAnimationEndRunnable.run(); - } - }); - anim.start(); - - mDeferringForDelete = true; - } + private static final int ANIM_TAG_KEY = 100; /* Accessibility */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); @@ -2859,6 +2287,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (getCurrentPage() > 0) { info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); } + info.setClassName(getClass().getName()); + + // Accessibility-wise, PagedView doesn't support long click, so disabling it. + // Besides disabling the accessibility long-click, this also prevents this view from getting + // accessibility focus. + info.setLongClickable(false); + if (Utilities.isLmpOrAbove()) { + info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK); + } } @Override @@ -2872,7 +2309,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); - event.setScrollable(true); + event.setScrollable(getPageCount() > 1); } @Override |