diff options
author | Hyunyoung Song <hyunyoungs@google.com> | 2016-06-21 23:55:53 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2016-06-21 23:55:54 +0000 |
commit | 4efaed56372085c5fa473b9e83bb497940ffbaaf (patch) | |
tree | 9142e81323821051049f08612c78d0506ad8c6fa | |
parent | a649faa1b5cedf5c9338341b51fb9e689ed9057c (diff) | |
parent | eac1dac23944ea5127dc12a161f016f18a5599cb (diff) | |
download | android_packages_apps_Trebuchet-4efaed56372085c5fa473b9e83bb497940ffbaaf.tar.gz android_packages_apps_Trebuchet-4efaed56372085c5fa473b9e83bb497940ffbaaf.tar.bz2 android_packages_apps_Trebuchet-4efaed56372085c5fa473b9e83bb497940ffbaaf.zip |
Merge "All apps pull up work b/28917826 b/29469966 b/29542376" into ub-launcher3-calgary
5 files changed, 247 insertions, 171 deletions
diff --git a/res/values/dimens.xml b/res/values/dimens.xml index cda8c0584..02c6c704d 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -88,6 +88,7 @@ <dimen name="all_apps_search_bar_bg_overflow">-6dp</dimen> <dimen name="all_apps_search_bar_divider_width">1dp</dimen> + <dimen name="all_apps_bezel_swipe_height">24dp</dimen> <!-- Widget tray --> <dimen name="widget_preview_label_vertical_padding">8dp</dimen> <dimen name="widget_preview_label_horizontal_padding">8dp</dimen> diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index fd4aff98e..e94153d68 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -420,7 +420,7 @@ public class LauncherStateTransitionAnimation { pCb.onTransitionComplete(); } }); - mAllAppsController.animateToAllApps(animation, revealDuration); + mAllAppsController.animateToAllApps(animation, revealDuration, false); dispatchOnLauncherTransitionPrepare(fromView, animated, false); dispatchOnLauncherTransitionPrepare(toView, animated, false); @@ -861,37 +861,31 @@ public class LauncherStateTransitionAnimation { return animation; } else if (animType == PULLUP) { animation.addListener(new AnimatorListenerAdapter() { + boolean canceled = false; @Override - public void onAnimationEnd(Animator animation) { - dispatchOnLauncherTransitionEnd(fromView, animated, false); - dispatchOnLauncherTransitionEnd(toView, animated, false); - cleanupAnimation(); - pCb.onTransitionComplete(); + public void onAnimationCancel(Animator animation) { + canceled = true; } - }); - mAllAppsController.animateToWorkspace(animation, revealDuration); - - // Dispatch the prepare transition signal - dispatchOnLauncherTransitionPrepare(fromView, animated, false); - dispatchOnLauncherTransitionPrepare(toView, animated, false); - - animation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - dispatchOnLauncherTransitionEnd(fromView, animated, true); - dispatchOnLauncherTransitionEnd(toView, animated, true); - + if (canceled) return; + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); // Run any queued runnables if (onCompleteRunnable != null) { onCompleteRunnable.run(); } - - // This can hold unnecessary references to views. cleanupAnimation(); pCb.onTransitionComplete(); } + }); + mAllAppsController.animateToWorkspace(animation, revealDuration, false); + + // Dispatch the prepare transition signal + dispatchOnLauncherTransitionPrepare(fromView, animated, false); + dispatchOnLauncherTransitionPrepare(toView, animated, false); final AnimatorSet stateAnimation = animation; final Runnable startAnimRunnable = new Runnable() { diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index bd718084e..888cc5783 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -30,6 +30,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.util.Thunk; @@ -43,6 +44,7 @@ class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimat private View mView; private boolean mAccessibilityEnabled; + private boolean mCanceled = false; public AlphaUpdateListener(View v, boolean accessibilityEnabled) { mView = v; @@ -67,7 +69,13 @@ class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimat } @Override + public void onAnimationCancel(Animator animation) { + mCanceled = true; + } + + @Override public void onAnimationEnd(Animator arg0) { + if (mCanceled) return; updateVisibility(mView, mAccessibilityEnabled); } @@ -322,8 +330,10 @@ public class WorkspaceStateTransitionAnimation { boolean isCurrentPage = (i == toPage); float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); float finalAlpha; - if (states.stateIsNormalHidden || states.stateIsOverviewHidden) { + if (states.stateIsOverviewHidden) { finalAlpha = 0f; + } else if(states.stateIsNormalHidden) { + finalAlpha = FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP ? 1 : 0; } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) { finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f; } else { @@ -447,10 +457,16 @@ public class WorkspaceStateTransitionAnimation { mStateAnimator.play(hotseatAlpha); mStateAnimator.play(pageIndicatorAlpha); mStateAnimator.addListener(new AnimatorListenerAdapter() { + boolean canceled = false; + @Override + public void onAnimationCancel(Animator animation) { + canceled = true; + } + @Override public void onAnimationEnd(Animator animation) { mStateAnimator = null; - + if (canceled) return; if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) { overviewPanel.getChildAt(0).performAccessibilityAction( AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index f30e08e65..3157c133f 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -17,6 +17,7 @@ import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.PagedView; +import com.android.launcher3.R; import com.android.launcher3.Workspace; import com.android.launcher3.Workspace.Direction; import com.android.launcher3.util.TouchController; @@ -39,8 +40,8 @@ public class AllAppsTransitionController implements TouchController, VerticalPul private final Interpolator mAccelInterpolator = new AccelerateInterpolator(2f); private final Interpolator mDecelInterpolator = new DecelerateInterpolator(1f); - private static final float ANIMATION_DURATION = 2000; - public static final float ALL_APPS_FINAL_ALPHA = .8f; + private static final float ANIMATION_DURATION = 1200; + public static final float ALL_APPS_FINAL_ALPHA = .9f; private static final float PARALLAX_COEFFICIENT = .125f; @@ -54,33 +55,34 @@ public class AllAppsTransitionController implements TouchController, VerticalPul private final Launcher mLauncher; private final VerticalPullDetector mDetector; - // Animation in this class is controlled by a single variable {@link mProgressTransY}. + // Animation in this class is controlled by a single variable {@link mShiftCurrent}. // Visually, it represents top y coordinate of the all apps container. Using the - // {@link mTranslation} as the denominator, this fraction value ranges in [0, 1]. - private float mProgressTransY; // numerator - private float mTranslation = -1; // denominator + // {@link mShiftRange} as the denominator, this fraction value ranges in [0, 1]. + // + // When {@link mShiftCurrent} is 0, all apps container is pulled up. + // When {@link mShiftCurrent} is {@link mShirtRange}, all apps container is pulled down. + private float mShiftStart; // [0, mShiftRange] + private float mShiftCurrent; // [0, mShiftRange] + private float mShiftRange; // changes depending on the orientation - private static final float RECATCH_REJECTION_FRACTION = .0875f; - // Used in landscape. - private static final float BAZEL_PULL_UP_HEIGHT = 60; + private static final float RECATCH_REJECTION_FRACTION = .0875f; + private int mBezelSwipeUpHeight; private long mAnimationDuration; - private float mCurY; + private AnimatorSet mCurrentAnimation; private boolean mNoIntercept; private boolean mLightStatusBar; - // At the end of scroll settling, this class also sets the state of the launcher. - // If it's already set,do not call the #mLauncher.setXXX method. - private boolean mStateAlreadyChanged; - public AllAppsTransitionController(Launcher launcher) { mLauncher = launcher; mDetector = new VerticalPullDetector(launcher); mDetector.setListener(this); + mBezelSwipeUpHeight = launcher.getResources().getDimensionPixelSize( + R.dimen.all_apps_bezel_swipe_height); } @Override @@ -95,26 +97,49 @@ public class AllAppsTransitionController implements TouchController, VerticalPul } else if (!mLauncher.isAllAppsVisible() && !shouldPossiblyIntercept(ev)) { mNoIntercept = true; } else { - mDetector.setDetectableScrollConditions(mLauncher.isAllAppsVisible() /* down */, - isInDisallowRecatchTopZone(), isInDisallowRecatchBottomZone()); + // Now figure out which direction scroll events the controller will start + // calling the callbacks. + int conditionsToReportScroll = 0; + + if (mDetector.isRestingState()) { + if (mLauncher.isAllAppsVisible()) { + conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_DOWN; + } else { + conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_UP; + } + } else { + if (isInDisallowRecatchBottomZone()) { + conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_UP; + } else if (isInDisallowRecatchTopZone()) { + conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_DOWN; + } else { + conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_ONLY; + } + } + mDetector.setDetectableScrollConditions(conditionsToReportScroll); } } if (mNoIntercept) { return false; } mDetector.onTouchEvent(ev); + if (mDetector.isScrollingState() && (isInDisallowRecatchBottomZone() || isInDisallowRecatchTopZone())) { + return false; + } return mDetector.shouldIntercept(); } private boolean shouldPossiblyIntercept(MotionEvent ev) { DeviceProfile grid = mLauncher.getDeviceProfile(); if (mDetector.isRestingState()) { - if (mLauncher.getDragLayer().isEventOverHotseat(ev) && !grid.isLandscape) { - return true; - } - if (ev.getY() > mLauncher.getDeviceProfile().heightPx - BAZEL_PULL_UP_HEIGHT && - grid.isLandscape) { - return true; + if (grid.isVerticalBarLayout()) { + if (ev.getY() > mLauncher.getDeviceProfile().heightPx - mBezelSwipeUpHeight) { + return true; + } + } else { + if (mLauncher.getDragLayer().isEventOverHotseat(ev) && !grid.isVerticalBarLayout()) { + return true; + } } return false; } else { @@ -128,32 +153,92 @@ public class AllAppsTransitionController implements TouchController, VerticalPul } private boolean isInDisallowRecatchTopZone() { - return mProgressTransY / mTranslation < RECATCH_REJECTION_FRACTION; + return mShiftCurrent / mShiftRange < RECATCH_REJECTION_FRACTION; } private boolean isInDisallowRecatchBottomZone() { - return mProgressTransY / mTranslation > 1 - RECATCH_REJECTION_FRACTION; + return mShiftCurrent / mShiftRange > 1 - RECATCH_REJECTION_FRACTION; } @Override public void onScrollStart(boolean start) { cancelAnimation(); mCurrentAnimation = LauncherAnimUtils.createAnimatorSet(); + mShiftStart = mAppsView.getTranslationY(); preparePull(start); } + @Override + public boolean onScroll(float displacement, float velocity) { + if (mAppsView == null) { + return false; // early termination. + } + if (0 <= mShiftStart + displacement && mShiftStart + displacement < mShiftRange) { + setProgress(mShiftStart + displacement); + } + return true; + } + + @Override + public void onScrollEnd(float velocity, boolean fling) { + if (mAppsView == null) { + return; // early termination. + } + + if (fling) { + if (velocity < 0) { + calculateDuration(velocity, mAppsView.getTranslationY()); + + if (!mLauncher.isAllAppsVisible()) { + mLauncher.showAppsView(true, true, false, false); + } else { + animateToAllApps(mCurrentAnimation, mAnimationDuration, true); + } + } else { + calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY())); + if (mLauncher.isAllAppsVisible()) { + mLauncher.showWorkspace(true); + } else { + animateToWorkspace(mCurrentAnimation, mAnimationDuration, true); + } + } + // snap to top or bottom using the release velocity + } else { + if (mAppsView.getTranslationY() > mShiftRange / 2) { + calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY())); + if (mLauncher.isAllAppsVisible()) { + mLauncher.showWorkspace(true); + } else { + animateToWorkspace(mCurrentAnimation, mAnimationDuration, true); + } + } else { + calculateDuration(velocity, Math.abs(mAppsView.getTranslationY())); + if (!mLauncher.isAllAppsVisible()) { + mLauncher.showAppsView(true, true, false, false); + } else { + animateToAllApps(mCurrentAnimation, mAnimationDuration, true); + } + + } + } + } /** * @param start {@code true} if start of new drag. */ public void preparePull(boolean start) { - // Initialize values that should not change until #onScrollEnd - mCurY = mAppsView.getTranslationY(); - mStatusBarHeight = mLauncher.getDragLayer().getInsets().top; - mHotseat.setVisibility(View.VISIBLE); - mHotseat.bringToFront(); if (start) { + // Initialize values that should not change until #onScrollEnd + mStatusBarHeight = mLauncher.getDragLayer().getInsets().top; + mHotseat.setVisibility(View.VISIBLE); + mHotseat.bringToFront(); + if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) { + mShiftRange = mHotseat.getTop(); + } else { + mShiftRange = mHotseat.getBottom(); + } if (!mLauncher.isAllAppsVisible()) { mLauncher.tryAndUpdatePredictedApps(); + mHotseatBackgroundAlpha = mHotseat.getBackground().getAlpha() / 255f; mHotseat.setBackgroundTransparent(true /* transparent */); mAppsView.setVisibility(View.VISIBLE); @@ -163,12 +248,13 @@ public class AllAppsTransitionController implements TouchController, VerticalPul mAppsView.getRevealView().setAlpha(mHotseatBackgroundAlpha); DeviceProfile grid= mLauncher.getDeviceProfile(); - if (!grid.isLandscape) { - mTranslation = mHotseat.getTop(); + if (!grid.isVerticalBarLayout()) { + mShiftRange = mHotseat.getTop(); } else { - mTranslation = mHotseat.getBottom(); + mShiftRange = mHotseat.getBottom(); } - setProgress(mTranslation); + mAppsView.getRevealView().setAlpha(mHotseatBackgroundAlpha); + setProgress(mShiftRange); } else { // TODO: get rid of this workaround to override state change by workspace transition mWorkspace.onLauncherTransitionPrepare(mLauncher, false, false); @@ -177,6 +263,8 @@ public class AllAppsTransitionController implements TouchController, VerticalPul child.setVisibility(View.VISIBLE); child.setAlpha(1f); } + } else { + setProgress(mShiftCurrent); } } @@ -199,23 +287,12 @@ public class AllAppsTransitionController implements TouchController, VerticalPul mLightStatusBar = enable; } - @Override - public boolean onScroll(float displacement, float velocity) { - if (mAppsView == null) { - return false; // early termination. - } - if (0 <= mCurY + displacement && mCurY + displacement < mTranslation) { - setProgress(mCurY + displacement); - } - return true; - } - /** * @param progress y value of the border between hotseat and all apps */ public void setProgress(float progress) { updateLightStatusBar(progress); - mProgressTransY = progress; + mShiftCurrent = progress; float alpha = calcAlphaAllApps(progress); float workspaceHotseatAlpha = 1 - alpha; @@ -224,67 +301,45 @@ public class AllAppsTransitionController implements TouchController, VerticalPul mAppsView.getContentView().setAlpha(alpha); mAppsView.setTranslationY(progress); mWorkspace.setWorkspaceTranslation(Direction.Y, - PARALLAX_COEFFICIENT *(-mTranslation + progress), - mAccelInterpolator.getInterpolation(workspaceHotseatAlpha)); - mWorkspace.setHotseatTranslation(Direction.Y, -mTranslation + progress, + PARALLAX_COEFFICIENT * (-mShiftRange + progress), mAccelInterpolator.getInterpolation(workspaceHotseatAlpha)); + if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) { + mWorkspace.setHotseatTranslation(Direction.Y, -mShiftRange + progress, + mAccelInterpolator.getInterpolation(workspaceHotseatAlpha)); + } else { + mWorkspace.setHotseatTranslation(Direction.Y, + PARALLAX_COEFFICIENT * (-mShiftRange + progress), + mAccelInterpolator.getInterpolation(workspaceHotseatAlpha)); + } } public float getProgress() { - return mProgressTransY; + return mShiftCurrent; } private float calcAlphaAllApps(float progress) { - return ((mTranslation - progress)/mTranslation); - } - - @Override - public void onScrollEnd(float velocity, boolean fling) { - if (mAppsView == null) { - return; // early termination. - } - - if (fling) { - if (velocity < 0) { - calculateDuration(velocity, mAppsView.getTranslationY()); - animateToAllApps(mCurrentAnimation, mAnimationDuration); - } else { - calculateDuration(velocity, Math.abs(mTranslation - mAppsView.getTranslationY())); - animateToWorkspace(mCurrentAnimation, mAnimationDuration); - } - // snap to top or bottom using the release velocity - } else { - if (mAppsView.getTranslationY() > mTranslation / 2) { - calculateDuration(velocity, Math.abs(mTranslation - mAppsView.getTranslationY())); - animateToWorkspace(mCurrentAnimation, mAnimationDuration); - } else { - calculateDuration(velocity, Math.abs(mAppsView.getTranslationY())); - animateToAllApps(mCurrentAnimation, mAnimationDuration); - } - } - mCurrentAnimation.start(); + return ((mShiftRange - progress)/ mShiftRange); } private void calculateDuration(float velocity, float disp) { // TODO: make these values constants after tuning. float velocityDivisor = Math.max(1.5f, Math.abs(0.5f * velocity)); - float travelDistance = Math.max(0.2f, disp / mTranslation); + float travelDistance = Math.max(0.2f, disp / mShiftRange); mAnimationDuration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance); if (DBG) { Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", mAnimationDuration, velocity, disp)); } } - public void animateToAllApps(AnimatorSet animationOut, long duration) { + public void animateToAllApps(AnimatorSet animationOut, long duration, boolean start) { if (animationOut == null){ return; } if (mDetector.isRestingState()) { preparePull(true); mAnimationDuration = duration; - mStateAlreadyChanged = true; + mShiftStart = mAppsView.getTranslationY(); } - mCurY = mAppsView.getTranslationY(); final float fromAllAppsTop = mAppsView.getTranslationY(); final float toAllAppsTop = 0; @@ -311,19 +366,22 @@ public class AllAppsTransitionController implements TouchController, VerticalPul } }}); mCurrentAnimation = animationOut; + if (start) { + mCurrentAnimation.start(); + } } - public void animateToWorkspace(AnimatorSet animationOut, long duration) { + public void animateToWorkspace(AnimatorSet animationOut, long duration, boolean start) { if (animationOut == null){ return; } if(mDetector.isRestingState()) { preparePull(true); mAnimationDuration = duration; - mStateAlreadyChanged = true; + mShiftStart = mAppsView.getTranslationY(); } final float fromAllAppsTop = mAppsView.getTranslationY(); - final float toAllAppsTop = mTranslation; + final float toAllAppsTop = mShiftRange; ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", fromAllAppsTop, toAllAppsTop); @@ -336,6 +394,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul @Override public void onAnimationCancel(Animator animation) { canceled = true; + setProgress(mShiftCurrent); } @Override @@ -349,16 +408,14 @@ public class AllAppsTransitionController implements TouchController, VerticalPul } }}); mCurrentAnimation = animationOut; + if (start) { + mCurrentAnimation.start(); + } } private void finishPullUp() { mHotseat.setVisibility(View.INVISIBLE); setProgress(0f); - if (!mStateAlreadyChanged) { - mLauncher.showAppsView(false /* animated */, true /* resetListToTop */, - false /* updatePredictedApps */, false /* focusSearchBar */); - } - mStateAlreadyChanged = false; } public void finishPullDown() { @@ -368,15 +425,12 @@ public class AllAppsTransitionController implements TouchController, VerticalPul mAppsView.setVisibility(View.INVISIBLE); mHotseat.setBackgroundTransparent(false /* transparent */); mHotseat.setVisibility(View.VISIBLE); - setProgress(mTranslation); - if (!mStateAlreadyChanged) { - mLauncher.showWorkspace(false); - } - mStateAlreadyChanged = false; + setProgress(mShiftRange); } private void cancelAnimation() { if (mCurrentAnimation != null) { + mCurrentAnimation.setDuration(0); mCurrentAnimation.cancel(); mCurrentAnimation = null; } diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java index 9304aac41..b54cb000d 100644 --- a/src/com/android/launcher3/allapps/VerticalPullDetector.java +++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java @@ -7,16 +7,22 @@ import android.view.ViewConfiguration; /** * One dimensional scroll gesture detector for all apps container pull up interaction. + * Client (e.g., AllAppsTransitionController) of this class can register a listener. + * + * Features that this gesture detector can support. */ public class VerticalPullDetector { - private static final String TAG = "ScrollGesture"; private static final boolean DBG = false; + private static final String TAG = "VerticalPullDetector"; private float mTouchSlop; - private boolean mScrollDown; // if false, only scroll up will be reported. - private boolean mDisallowRecatchFromTop; - private boolean mDisallowRecatchFromBottom; + + private int mScrollDirections; + public static final int THRESHOLD_UP = 1 << 0; + public static final int THRESHOLD_DOWN = 1 << 1; + public static final int THRESHOLD_ONLY = THRESHOLD_DOWN | THRESHOLD_UP; + /** * The minimum release velocity in pixels per millisecond that triggers fling.. @@ -31,23 +37,43 @@ public class VerticalPullDetector { /* Scroll state, this is set to true during dragging and animation. */ private State mState = State.NONE; - enum State {NONE, DRAG, SCROLLING}; + + enum State { + NONE, + CATCH, // onScrollStart + DRAG, // onScrollStart, onScroll + SCROLLING // onScrollEnd + }; + + //------------------- State transition diagram ----------------------------------- + // + // NONE -> (mDisplacement > mTouchSlop) -> DRAG + // DRAG -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SCROLLING + // SCROLLING -> (MotionEvent#ACTION_DOWN) && (mDisplacement > mTouchSlop) -> CATCH + // SCROLLING -> (View settled) -> NONE private void setState(State newState) { if (DBG) { - Log.d(TAG, mState + "->" + newState); + Log.d(TAG, "setState:" + mState + "->" + newState); } mState = newState; } public boolean shouldIntercept() { - return mState == State.DRAG; + return mState == State.DRAG || mState == State.SCROLLING || mState == State.CATCH; } + /** + * There's no touch and there's no animation. + */ public boolean isRestingState() { return mState == State.NONE; } + public boolean isScrollingState() { + return mState == State.SCROLLING; + } + private float mDownX; private float mDownY; private float mDownMillis; @@ -60,8 +86,7 @@ public class VerticalPullDetector { private float mDisplacementY; private float mDisplacementX; - /* scroll started during previous animation */ - private boolean mSubtractSlop = true; + private float mSubtractDisplacement; /* Client of this gesture detector can register a callback. */ Listener mListener; @@ -80,39 +105,25 @@ public class VerticalPullDetector { mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } - public void setDetectableScrollConditions(boolean scrollDown, boolean disallowRecatchFromTop, - boolean disallowRecatchFromBottom) { - mScrollDown = scrollDown; - mDisallowRecatchFromTop = disallowRecatchFromTop; - mDisallowRecatchFromBottom = disallowRecatchFromBottom; + public void setDetectableScrollConditions(int scrollDirectionFlags) { + mScrollDirections = scrollDirectionFlags; } private boolean shouldScrollStart() { - float deltaY = Math.abs(mDisplacementY); - float deltaX = Math.max(Math.abs(mDisplacementX), 1); - if (mScrollDown && mDisplacementY > mTouchSlop) { - if (deltaY > deltaX) { - return true; - } + // reject cases where the slop condition is not met. + if (Math.abs(mDisplacementY) < mTouchSlop) { + return false; } - if (!mScrollDown && mDisplacementY < -mTouchSlop) { - if (deltaY > deltaX) { - return true; - } - } - return false; - } - private boolean shouldRecatchScrollStart() { - if (!mDisallowRecatchFromBottom && !mDisallowRecatchFromTop) { - return true; - } - if (mDisallowRecatchFromTop && mDisplacementY > mTouchSlop) { - mDisallowRecatchFromTop = false; - return true; + // reject cases where the angle condition is not met. + float deltaY = Math.abs(mDisplacementY); + float deltaX = Math.max(Math.abs(mDisplacementX), 1); + if (deltaX > deltaY) { + return false; } - if (mDisallowRecatchFromBottom && mDisplacementY < -mTouchSlop) { - mDisallowRecatchFromBottom = false; + // Check if the client is interested in scroll in current direction. + if (((mScrollDirections & THRESHOLD_DOWN) > 0 && mDisplacementY > 0) || + ((mScrollDirections & THRESHOLD_UP) > 0 && mDisplacementY < 0)) { return true; } return false; @@ -126,8 +137,11 @@ public class VerticalPullDetector { mDownY = ev.getY(); mLastDisplacement = 0; mVelocity = 0; - if (mState == State.SCROLLING && shouldRecatchScrollStart()){ + + // handle state and listener calls. + if (mState == State.SCROLLING && shouldScrollStart()){ reportScrollStart(true /* recatch */); + setState(State.CATCH); } break; case MotionEvent.ACTION_MOVE: @@ -135,23 +149,23 @@ public class VerticalPullDetector { mDisplacementY = ev.getY() - mDownY; mVelocity = computeVelocity(ev, mVelocity); - if (mState == State.SCROLLING && Math.abs(mDisplacementY) > mTouchSlop ){ - setState(State.DRAG); - reportScrollStart(true /* recatch */); - } - if (mState == State.NONE && shouldScrollStart()) { + // handle state and listener calls. + if (shouldScrollStart() && mState != State.DRAG) { + if (mState == State.NONE) { + reportScrollStart(false /* recatch */); + } setState(State.DRAG); - reportScrollStart(false /* recatch */); } - if (mState == State.DRAG && mListener != null) { + if (mState == State.DRAG) { reportScroll(); } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // These are synthetic events and there is no need to update internal values. - if (mState == State.DRAG && mListener != null) { + if (mState == State.DRAG || mState == State.CATCH) { reportScrollEnd(); + setState(State.SCROLLING); } break; default: @@ -173,7 +187,11 @@ public class VerticalPullDetector { private boolean reportScrollStart(boolean recatch) { mListener.onScrollStart(!recatch); - mSubtractSlop = !recatch; + if (mDisplacementY > 0) { + mSubtractDisplacement = mTouchSlop; + } else { + mSubtractDisplacement = -mTouchSlop; + } if (DBG) { Log.d(TAG, "onScrollStart recatch:" + recatch); } @@ -187,15 +205,8 @@ public class VerticalPullDetector { Log.d(TAG, String.format("onScroll disp=%.1f, velocity=%.1f", mDisplacementY, mVelocity)); } - float subtractDisplacement = 0f; - if (mSubtractSlop) { - if (mDisplacementY > 0) { - subtractDisplacement = mTouchSlop; - } else { - subtractDisplacement = -mTouchSlop; - } - } - return mListener.onScroll(mDisplacementY - subtractDisplacement, mVelocity); + + return mListener.onScroll(mDisplacementY - mSubtractDisplacement, mVelocity); } return true; } @@ -206,7 +217,7 @@ public class VerticalPullDetector { mDisplacementY, mVelocity)); } mListener.onScrollEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS); - setState(State.SCROLLING); + } /** * Computes the damped velocity using the two motion events and the previous velocity. |