From cc5c8843dfebfa92057b6ce8904ac58cd05ffaef Mon Sep 17 00:00:00 2001 From: Lais Andrade Date: Fri, 4 Jun 2021 18:20:30 +0100 Subject: [PATCH] Introduces haptic feedback to launcher overview Haptics introduced at the key moments: - Task scroll in overview or quick switch, trigger when a new task comes to the center of the screen; - Task scroll in overview when overscroll animation is triggered; - Task dismissed in overview; There is also a configured min gap between two scroll haptics set to 20ms to prevent fast scrolls from creating a chain of cancelled effects. Fix: 182382085 Test: manual Change-Id: I43c0f8c879a06f317e8a660240dafb7f7abe79f7 --- quickstep/res/values/config.xml | 1 + ...ButtonNavbarToOverviewTouchController.java | 4 +- .../NoButtonQuickSwitchTouchController.java | 13 ++++++- .../TaskViewTouchController.java | 14 ++++++- .../android/quickstep/AbsSwipeUpHandler.java | 4 +- .../interaction/EdgeBackGesturePanel.java | 2 +- .../interaction/NavBarGestureHandler.java | 4 +- .../quickstep}/util/VibratorWrapper.java | 38 +++++++++++++++++-- .../android/quickstep/views/RecentsView.java | 32 ++++++++++++++++ src/com/android/launcher3/PagedView.java | 31 +++++++++++++++ 10 files changed, 130 insertions(+), 13 deletions(-) rename {src/com/android/launcher3 => quickstep/src/com/android/quickstep}/util/VibratorWrapper.java (66%) diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml index d67b23be0a..31c0f5f2ab 100644 --- a/quickstep/res/values/config.xml +++ b/quickstep/res/values/config.xml @@ -30,6 +30,7 @@ determines how many thumbnails will be fetched in the background. --> 3 12 + 20 200 diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 283743dc64..ef6f53e8f5 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -24,7 +24,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; -import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; +import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import android.animation.ObjectAnimator; @@ -38,11 +38,11 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.states.StateAnimationConfig; -import com.android.launcher3.util.VibratorWrapper; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.OverviewToHomeAnim; +import com.android.quickstep.util.VibratorWrapper; import com.android.quickstep.views.RecentsView; /** diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 40c3e02238..ff3c517949 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -41,7 +41,7 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM; import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT; import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP; import static com.android.launcher3.util.DisplayController.getSingleFrameMs; -import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; +import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; @@ -67,14 +67,15 @@ import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.BothAxesSwipeDetector; import com.android.launcher3.util.TouchController; -import com.android.launcher3.util.VibratorWrapper; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.MotionPauseDetector; +import com.android.quickstep.util.VibratorWrapper; import com.android.quickstep.util.WorkspaceRevealAnim; import com.android.quickstep.views.LauncherRecentsView; +import com.android.quickstep.views.RecentsView; /** * Handles quick switching to a recent task from the home screen. To give as much flexibility to @@ -398,6 +399,14 @@ public class NoButtonQuickSwitchTouchController implements TouchController, nonOverviewAnim.setFloatValues(startProgress, endProgress); mNonOverviewAnim.dispatchOnStart(); } + if (targetState == QUICK_SWITCH) { + // Navigating to quick switch, add scroll feedback since the first time is not + // considered a scroll by the RecentsView. + VibratorWrapper.INSTANCE.get(mLauncher).vibrate( + RecentsView.SCROLL_VIBRATION_PRIMITIVE, + RecentsView.SCROLL_VIBRATION_PRIMITIVE_SCALE, + RecentsView.SCROLL_VIBRATION_FALLBACK); + } nonOverviewAnim.setDuration(Math.max(xDuration, yDuration)); mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState)); diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index 0603ba502a..010f463438 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -22,6 +22,7 @@ import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.os.SystemClock; +import android.os.VibrationEffect; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; @@ -41,6 +42,7 @@ import com.android.launcher3.util.FlingBlockCheck; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.SysUINavigationMode; +import com.android.quickstep.util.VibratorWrapper; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -55,6 +57,12 @@ public abstract class TaskViewTouchController private static final long MIN_TASK_DISMISS_ANIMATION_DURATION = 300; private static final long MAX_TASK_DISMISS_ANIMATION_DURATION = 600; + public static final int TASK_DISMISS_VIBRATION_PRIMITIVE = + Utilities.ATLEAST_R ? VibrationEffect.Composition.PRIMITIVE_TICK : -1; + public static final float TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE = 1f; + public static final VibrationEffect TASK_DISMISS_VIBRATION_FALLBACK = + VibratorWrapper.EFFECT_TEXTURE_TICK; + protected final T mActivity; private final SingleAxisSwipeDetector mDetector; private final RecentsView mRecentsView; @@ -334,10 +342,10 @@ public abstract class TaskViewTouchController fling = false; } PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); + boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl); float progress = mCurrentAnimation.getProgressFraction(); float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress(); if (fling) { - boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl); goingToEnd = goingUp == mCurrentAnimationIsGoingUp; } else { goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS; @@ -357,6 +365,10 @@ public abstract class TaskViewTouchController mCurrentAnimation.startWithVelocity(mActivity, goingToEnd, velocity * orientationHandler.getSecondaryTranslationDirectionFactor(), mEndDisplacement, animationDuration); + if (goingUp && goingToEnd) { + VibratorWrapper.INSTANCE.get(mActivity).vibrate(TASK_DISMISS_VIBRATION_PRIMITIVE, + TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE, TASK_DISMISS_VIBRATION_FALLBACK); + } } private void clearState() { diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 42c89fd006..48a4e77f9f 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -36,7 +36,6 @@ import static com.android.launcher3.util.DisplayController.getSingleFrameMs; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; -import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.quickstep.GestureState.GestureEndTarget.HOME; import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK; import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK; @@ -46,6 +45,7 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELED; import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; +import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; @@ -90,7 +90,6 @@ import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.tracing.InputConsumerProto; import com.android.launcher3.tracing.SwipeHandlerProto; import com.android.launcher3.util.TraceHelper; -import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.WindowBounds; import com.android.quickstep.BaseActivityInterface.AnimationFactory; import com.android.quickstep.GestureState.GestureEndTarget; @@ -107,6 +106,7 @@ import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.SwipePipToHomeAnimator; import com.android.quickstep.util.TransformParams; +import com.android.quickstep.util.VibratorWrapper; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java index 0521db4d1b..063eb6a8e5 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java @@ -43,7 +43,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.util.VibratorWrapper; +import com.android.quickstep.util.VibratorWrapper; /** Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java. */ public class EdgeBackGesturePanel extends View { diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java index a9a9e2a104..5e127d45ee 100644 --- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java +++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java @@ -16,7 +16,6 @@ package com.android.quickstep.interaction; import static com.android.launcher3.Utilities.squaredHypot; -import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_COMPLETED; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_BAD_ANGLE; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT; @@ -26,6 +25,7 @@ import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestu import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_GESTURE_COMPLETED; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE; +import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC; import android.animation.ValueAnimator; import android.content.Context; @@ -47,11 +47,11 @@ import androidx.annotation.Nullable; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.util.VibratorWrapper; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.NavBarPosition; import com.android.quickstep.util.TriggerSwipeUpTouchTracker; +import com.android.quickstep.util.VibratorWrapper; import com.android.systemui.shared.system.QuickStepContract; /** Utility class to handle Home and Assistant gestures. */ diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/quickstep/src/com/android/quickstep/util/VibratorWrapper.java similarity index 66% rename from src/com/android/launcher3/util/VibratorWrapper.java rename to quickstep/src/com/android/quickstep/util/VibratorWrapper.java index b0defd445a..211bd08b33 100644 --- a/src/com/android/launcher3/util/VibratorWrapper.java +++ b/quickstep/src/com/android/quickstep/util/VibratorWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.util; +package com.android.quickstep.util; import static android.os.VibrationEffect.createPredefined; import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED; @@ -21,15 +21,20 @@ import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; +import android.media.AudioAttributes; import android.os.Build; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.MainThreadInitializedObject; + /** * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary. */ @@ -39,8 +44,15 @@ public class VibratorWrapper { public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(VibratorWrapper::new); + public static final AudioAttributes VIBRATION_ATTRS = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build(); + public static final VibrationEffect EFFECT_CLICK = createPredefined(VibrationEffect.EFFECT_CLICK); + public static final VibrationEffect EFFECT_TEXTURE_TICK = + VibrationEffect.createPredefined(VibrationEffect.EFFECT_TEXTURE_TICK); /** * Haptic when entering overview. @@ -78,7 +90,27 @@ public class VibratorWrapper { /** Vibrates with the given effect if haptic feedback is available and enabled. */ public void vibrate(VibrationEffect vibrationEffect) { if (mHasVibrator && mIsHapticFeedbackEnabled) { - UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect)); + UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect, VIBRATION_ATTRS)); + } + } + + /** + * Vibrates with a single primitive, if supported, or use a fallback effect instead. This only + * vibrates if haptic feedback is available and enabled. + */ + @SuppressLint("NewApi") + public void vibrate(int primitiveId, float primitiveScale, VibrationEffect fallbackEffect) { + if (mHasVibrator && mIsHapticFeedbackEnabled) { + UI_HELPER_EXECUTOR.execute(() -> { + if (Utilities.ATLEAST_R && primitiveId >= 0 + && mVibrator.areAllPrimitivesSupported(primitiveId)) { + mVibrator.vibrate(VibrationEffect.startComposition() + .addPrimitive(primitiveId, primitiveScale) + .compose(), VIBRATION_ATTRS); + } else { + mVibrator.vibrate(fallbackEffect, VIBRATION_ATTRS); + } + }); } } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 3aed7cc630..9ec5138a9c 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -77,7 +77,9 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.os.SystemClock; import android.os.UserHandle; +import android.os.VibrationEffect; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; @@ -154,6 +156,7 @@ import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; +import com.android.quickstep.util.VibratorWrapper; import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskKey; @@ -238,6 +241,12 @@ public abstract class RecentsView mScrollHapticMinGapMillis) { + mScrollLastHapticTimestamp = now; + VibratorWrapper.INSTANCE.get(mContext).vibrate(SCROLL_VIBRATION_PRIMITIVE, + SCROLL_VIBRATION_PRIMITIVE_SCALE, SCROLL_VIBRATION_FALLBACK); + } + } + @Override protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { // Enables swiping to the left or right only if the task overlay is not modal. diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index eb3f94ce7c..e4ee50a0b1 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -108,6 +108,8 @@ public abstract class PagedView extends ViewGrou // relative scroll position unchanged in updateCurrentPageScroll. Cleared when snapping to a // page. protected int mCurrentPageScrollDiff; + // The current page the PagedView is scrolling over on it's way to the destination page. + protected int mCurrentScrollOverPage; @ViewDebug.ExportedProperty(category = "launcher") protected int mNextPage = INVALID_PAGE; @@ -180,6 +182,7 @@ public abstract class PagedView extends ViewGrou mScroller = new OverScroller(context, SCROLL); mCurrentPage = 0; + mCurrentScrollOverPage = 0; final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); @@ -437,6 +440,7 @@ public abstract class PagedView extends ViewGrou } int prevPage = overridePrevPage != INVALID_PAGE ? overridePrevPage : mCurrentPage; mCurrentPage = validateNewPage(currentPage); + mCurrentScrollOverPage = mCurrentPage; updateCurrentPageScroll(); notifyPageSwitchListener(prevPage); invalidate(); @@ -557,9 +561,11 @@ public abstract class PagedView extends ViewGrou if (newPos < mMinScroll && oldPos >= mMinScroll) { mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity()); mScroller.abortAnimation(); + onEdgeAbsorbingScroll(); } else if (newPos > mMaxScroll && oldPos <= mMaxScroll) { mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity()); mScroller.abortAnimation(); + onEdgeAbsorbingScroll(); } } @@ -577,6 +583,7 @@ public abstract class PagedView extends ViewGrou sendScrollAccessibilityEvent(); int prevPage = mCurrentPage; mCurrentPage = validateNewPage(mNextPage); + mCurrentScrollOverPage = mCurrentPage; mNextPage = INVALID_PAGE; notifyPageSwitchListener(prevPage); @@ -837,6 +844,7 @@ public abstract class PagedView extends ViewGrou public void onViewRemoved(View child) { super.onViewRemoved(child); mCurrentPage = validateNewPage(mCurrentPage); + mCurrentScrollOverPage = mCurrentPage; dispatchPageCountChanged(); } @@ -1408,6 +1416,20 @@ public abstract class PagedView extends ViewGrou protected void onNotSnappingToPageInFreeScroll() { } + /** + * Called when the view edges absorb part of the scroll. Subclasses can override this + * to provide custom behavior during animation. + */ + protected void onEdgeAbsorbingScroll() { + } + + /** + * Called when the current page closest to the center of the screen changes as part of the + * scroll. Subclasses can override this to provide custom behavior during scroll. + */ + protected void onScrollOverPageChanged() { + } + protected boolean shouldFlingForVelocity(int velocity) { float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity; return Math.abs(velocity) > threshold; @@ -1691,6 +1713,15 @@ public abstract class PagedView extends ViewGrou return mAllowOverScroll; } + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + int newDestinationPage = getDestinationPage(); + if (newDestinationPage >= 0 && newDestinationPage != mCurrentScrollOverPage) { + mCurrentScrollOverPage = newDestinationPage; + onScrollOverPageChanged(); + } + } + @Override public CharSequence getAccessibilityClassName() { // Some accessibility services have special logic for ScrollView. Since we provide same