diff options
| -rw-r--r-- | core/api/current.txt | 3 | ||||
| -rw-r--r-- | core/java/android/view/HapticScrollFeedbackProvider.java | 26 | ||||
| -rw-r--r-- | core/java/android/view/ScrollFeedbackProvider.java | 147 | ||||
| -rw-r--r-- | core/java/android/view/ViewConfiguration.java | 48 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 6 | ||||
| -rw-r--r-- | core/java/android/widget/ScrollView.java | 6 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java | 395 |
7 files changed, 139 insertions, 492 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 1879da534377..a6b77ea1be06 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -51231,11 +51231,8 @@ package android.view { @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public interface ScrollFeedbackProvider { method public void onScrollLimit(int, int, int, boolean); - method public default void onScrollLimit(@NonNull android.view.MotionEvent, int, boolean); method public void onScrollProgress(int, int, int, int); - method public default void onScrollProgress(@NonNull android.view.MotionEvent, int, int); method public void onSnapToItem(int, int, int); - method public default void onSnapToItem(@NonNull android.view.MotionEvent, int); } public class SearchEvent { diff --git a/core/java/android/view/HapticScrollFeedbackProvider.java b/core/java/android/view/HapticScrollFeedbackProvider.java index fba23ba8369f..a2f1d37c2c11 100644 --- a/core/java/android/view/HapticScrollFeedbackProvider.java +++ b/core/java/android/view/HapticScrollFeedbackProvider.java @@ -17,21 +17,35 @@ package android.view; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.view.flags.Flags; import com.android.internal.annotations.VisibleForTesting; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * {@link ScrollFeedbackProvider} that performs haptic feedback when scrolling. * * <p>Each scrolling widget should have its own instance of this class to ensure that scroll state * is isolated. + * + * <p>Check {@link ScrollFeedbackProvider} for details on the arguments that should be passed to the + * methods in this class. To check if your input device ID, source, and motion axis are valid for + * haptic feedback, you can use the + * {@link ViewConfiguration#isHapticScrollFeedbackEnabled(int, int, int)} API. */ @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider { private static final String TAG = "HapticScrollFeedbackProvider"; + /** @hide */ + @IntDef(value = {MotionEvent.AXIS_SCROLL}) + @Retention(RetentionPolicy.SOURCE) + public @interface HapticScrollFeedbackAxis {} + private static final int TICK_INTERVAL_NO_TICK = 0; private final View mView; @@ -46,10 +60,6 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider { /** The {@link InputDevice} source from which the latest scroll event happened. */ private int mSource = -1; - /** - * Cache for tick interval for scroll tick caused by a {@link InputDevice#SOURCE_ROTARY_ENCODER} - * on {@link MotionEvent#AXIS_SCROLL}. Set to -1 if the value has not been fetched and cached. - */ /** The tick interval corresponding to the current InputDevice/source/axis. */ private int mTickIntervalPixels = TICK_INTERVAL_NO_TICK; private int mTotalScrollPixels = 0; @@ -68,7 +78,8 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider { } @Override - public void onScrollProgress(int inputDeviceId, int source, int axis, int deltaInPixels) { + public void onScrollProgress( + int inputDeviceId, int source, @HapticScrollFeedbackAxis int axis, int deltaInPixels) { maybeUpdateCurrentConfig(inputDeviceId, source, axis); if (!mHapticScrollFeedbackEnabled) { return; @@ -95,7 +106,8 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider { } @Override - public void onScrollLimit(int inputDeviceId, int source, int axis, boolean isStart) { + public void onScrollLimit( + int inputDeviceId, int source, @HapticScrollFeedbackAxis int axis, boolean isStart) { maybeUpdateCurrentConfig(inputDeviceId, source, axis); if (!mHapticScrollFeedbackEnabled) { return; @@ -112,7 +124,7 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider { } @Override - public void onSnapToItem(int inputDeviceId, int source, int axis) { + public void onSnapToItem(int inputDeviceId, int source, @HapticScrollFeedbackAxis int axis) { maybeUpdateCurrentConfig(inputDeviceId, source, axis); if (!mHapticScrollFeedbackEnabled) { return; diff --git a/core/java/android/view/ScrollFeedbackProvider.java b/core/java/android/view/ScrollFeedbackProvider.java index 6f760c56e11a..78716f5646f6 100644 --- a/core/java/android/view/ScrollFeedbackProvider.java +++ b/core/java/android/view/ScrollFeedbackProvider.java @@ -24,120 +24,103 @@ import android.view.flags.Flags; * Interface to represent an entity giving consistent feedback for different events surrounding view * scroll. * - * <p>When you have access to the {@link MotionEvent}s that triggered the different scroll events, - * use the {@link MotionEvent} based APIs in this class. If you do not have access to the motion - * events, you can use the methods that accept the {@link InputDevice} ID (which can be obtained by - * APIs like {@link MotionEvent#getDeviceId()} and {@link InputDevice#getId()}) and source (which - * can be obtained by APIs like {@link MotionEvent#getSource()}) of the motion that caused the - * scroll events. + * <p>The interface provides methods for the client to report different scroll events. The client + * should report all scroll events that they want to be considered for scroll feedback using the + * respective methods. The interface will process these events and provide scroll feedback based on + * its specific feedback implementation. + * + * <h3>Obtaining the correct arguments for methods in this interface</h3> + * + * <p>Methods in this interface rely on the provision of valid {@link InputDevice} ID and source, as + * well as the {@link MotionEvent} axis that generated a specific scroll event. The + * {@link InputDevice} represented by the provided ID must have a {@link InputDevice.MotionRange} + * with the provided source and axis. See below for more details on obtaining the right arguments + * for your method call. + * + * <ul> + * + * <li><p><b>inputDeviceId</b>: should always be the ID of the {@link InputDevice} that generated + * the scroll event. If calling this method in response to a {@link MotionEvent}, use the device ID + * that is reported by the event, which can be obtained using {@link MotionEvent#getDeviceId()}. + * Otherwise, use a valid ID that is obtained from {@link InputDevice#getId()}, or from an + * {@link InputManager} instance ({@link InputManager#getInputDeviceIds()} gives all the valid input + * device IDs). + * + * <li><p><b>source</b>: should always be the {@link InputDevice} source that generated the scroll + * event. Use {@link MotionEvent#getSource()} if calling this method in response to a + * {@link MotionEvent}. Otherwise, use a valid source for the {@link InputDevice}. You can use + * {@link InputDevice#getMotionRanges()} to get all the {@link InputDevice.MotionRange}s for the + * {@link InputDevice}, from which you can derive all the valid sources for the device. + * + * <li><p><b>axis</b>: should always be the axis whose axis value produced the scroll event. + * A {@link MotionEvent} may report data for multiple axes, and each axis may have multiple data + * points for different pointers. Use the axis whose movement produced the specific scroll event. + * The motion value for an axis can be obtained using {@link MotionEvent#getAxisValue(int)}. + * You can use {@link InputDevice#getMotionRanges()} to get all the {@link InputDevice.MotionRange}s + * for the {@link InputDevice}, from which you can derive all the valid axes for the device. + * + * </ul> + * + * <b>Note</b> that not all valid input device source and motion axis inputs are necessarily + * supported for scroll feedback. If you are implementing this interface, provide clear + * documentation in your implementation class about which input device source and motion axis are + * supported for your specific implementation. If you are using one of the implementations of this + * interface, please refer to the documentation of the implementation for details on which input + * device source and axis are supported. */ @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public interface ScrollFeedbackProvider { /** - * Call this when the view has snapped to an item, with a motion generated by an - * {@link InputDevice} with an id of {@code inputDeviceId}, from an input {@code source} and on - * a given motion event {@code axis}. + * Call this when the view has snapped to an item. * - * <p>This method has the same purpose as {@link #onSnapToItem(MotionEvent, int)}. When a scroll - * snap happens, call either this method or {@link #onSnapToItem(MotionEvent, int)}, not both. - * This method is useful when you have no direct access to the {@link MotionEvent} that - * caused the snap event. * * @param inputDeviceId the ID of the {@link InputDevice} that generated the motion triggering * the snap. * @param source the input source of the motion causing the snap. * @param axis the axis of {@code event} that caused the item to snap. - * - * @see #onSnapToItem(MotionEvent, int) */ void onSnapToItem(int inputDeviceId, int source, int axis); /** - * Call this when the view has snapped to an item, with a motion from a given - * {@link MotionEvent} on a given {@code axis}. + * Call this when the view has reached the scroll limit. * - * <p>The interface is not aware of the internal scroll states of the view for which scroll - * feedback is played. As such, the client should call - * {@link #onScrollLimit(MotionEvent, int, int)} when scrolling has reached limit. - * - * @param event the {@link MotionEvent} that caused the item to snap. - * @param axis the axis of {@code event} that caused the item to snap. - * - * @see #onSnapToItem(int, int, int) - */ - default void onSnapToItem(@NonNull MotionEvent event, int axis) { - onSnapToItem(event.getDeviceId(), event.getSource(), axis); - } - - /** - * Call this when the view has reached the scroll limit when scrolled by a motion generated by - * an {@link InputDevice} with an id of {@code inputDeviceId}, from an input {@code source} and - * on a given motion event {@code axis}. - * - * <p>This method has the same purpose as {@link #onScrollLimit(MotionEvent, int, boolean)}. - * When a scroll limit happens, call either this method or - * {@link #onScrollLimit(MotionEvent, int, boolean)}, not both. This method is useful when you - * have no direct access to the {@link MotionEvent} that caused the scroll limit. + * <p>Note that a feedback may not be provided on every call to this method. This interface, for + * instance, may provide feedback on every `N`th scroll limit event. For the interface to + * properly provide feedback when needed, call this method for each scroll limit event that you + * want to be accounted to scroll limit feedback. * * @param inputDeviceId the ID of the {@link InputDevice} that caused scrolling to hit limit. * @param source the input source of the motion that caused scrolling to hit the limit. * @param axis the axis of {@code event} that caused scrolling to hit the limit. * @param isStart {@code true} if scrolling hit limit at the start of the scrolling list, and * {@code false} if the scrolling hit limit at the end of the scrolling list. - * - * @see #onScrollLimit(MotionEvent, int, boolean) + * <i>start</i> and <i>end<i> in this context are not geometrical references. + * Instead, they refer to the start and end of a scrolling experience. As such, + * "start" for some views may be at the bottom of a scrolling list, while it may + * be at the top of scrolling list for others. */ void onScrollLimit(int inputDeviceId, int source, int axis, boolean isStart); /** - * Call this when the view has reached the scroll limit when scrolled by the motion from a given - * {@link MotionEvent} on a given {@code axis}. + * Call this when the view has scrolled. * - * @param event the {@link MotionEvent} that caused scrolling to hit the limit. - * @param axis the axis of {@code event} that caused scrolling to hit the limit. - * @param isStart {@code true} if scrolling hit limit at the start of the scrolling list, and - * {@code false} if the scrolling hit limit at the end of the scrolling list. - * - * @see #onScrollLimit(int, int, int, boolean) - */ - default void onScrollLimit(@NonNull MotionEvent event, int axis, boolean isStart) { - onScrollLimit(event.getDeviceId(), event.getSource(), axis, isStart); - } - - /** - * Call this when the view has scrolled by {@code deltaInPixels} due to the motion generated by - * an {@link InputDevice} with an id of {@code inputDeviceId}, from an input {@code source} and - * on a given motion event {@code axis}. + * <p>Different axes have different ways to map their raw axis values to pixels for scrolling. + * When calling this method, use the scroll values in pixels by which the view was scrolled; do + * not use the raw axis values. That is, use whatever value is passed to one of View's scrolling + * methods (example: {@link View#scrollBy(int, int)}). For example, for vertical scrolling on + * {@link MotionEvent#AXIS_SCROLL}, convert the raw axis value to the equivalent pixels by using + * {@link ViewConfiguration#getScaledVerticalScrollFactor()}, and use that value for this method + * call. * - * <p>This method has the same purpose as {@link #onScrollProgress(MotionEvent, int, int)}. - * When a scroll progress happens, call either this method or - * {@link #onScrollProgress(MotionEvent, int, int)}, not both. This method is useful when you - * have no direct access to the {@link MotionEvent} that caused the scroll progress. + * <p>Note that a feedback may not be provided on every call to this method. This interface, for + * instance, may provide feedback for every `x` pixels scrolled. For the interface to properly + * track scroll progress and provide feedback when needed, call this method for each scroll + * event that you want to be accounted to scroll feedback. * * @param inputDeviceId the ID of the {@link InputDevice} that caused scroll progress. * @param source the input source of the motion that caused scroll progress. * @param axis the axis of {@code event} that caused scroll progress. * @param deltaInPixels the amount of scroll progress, in pixels. - * - * @see #onScrollProgress(MotionEvent, int, int) */ void onScrollProgress(int inputDeviceId, int source, int axis, int deltaInPixels); - - /** - * Call this when the view has scrolled by {@code deltaInPixels} due to the motion from a given - * {@link MotionEvent} on a given {@code axis}. - * - * <p>The interface is not aware of the internal scroll states of the view for which scroll - * feedback is played. As such, the client should call - * {@link #onScrollLimit(MotionEvent, int, int)} when scrolling has reached limit. - * - * @param event the {@link MotionEvent} that caused scroll progress. - * @param axis the axis of {@code event} that caused scroll progress. - * @param deltaInPixels the amount of scroll progress, in pixels. - * - * @see #onScrollProgress(int, int, int, int) - */ - default void onScrollProgress(@NonNull MotionEvent event, int axis, int deltaInPixels) { - onScrollProgress(event.getDeviceId(), event.getSource(), axis, deltaInPixels); - } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 0244d462473b..a3ae6cf20725 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -1220,36 +1220,39 @@ public class ViewConfiguration { * Checks if any kind of scroll haptic feedback is enabled for a motion generated by a specific * input device configuration and motion axis. * - * <h3>Obtaining the correct arguments for this method call</h3> - * <p><b>inputDeviceId</b>: if calling this method in response to a {@link MotionEvent}, use - * the device ID that is reported by the event, which can be obtained using - * {@link MotionEvent#getDeviceId()}. Otherwise, use a valid ID that is obtained from - * {@link InputDevice#getId()}, or from an {@link InputManager} instance - * ({@link InputManager#getInputDeviceIds()} gives all the valid input device IDs). + * <p>See {@link ScrollFeedbackProvider} for details on the arguments that should be passed to + * the methods in this class. * - * <p><b>axis</b>: a {@link MotionEvent} may report data for multiple axes, and each axis may - * have multiple data points for different pointers. Use the axis whose movement produced the - * scrolls that would generate the scroll haptics. You can use - * {@link InputDevice#getMotionRanges()} to get all the {@link InputDevice.MotionRange}s for the - * {@link InputDevice}, from which you can derive all the valid axes for the device. + * <p>If the provided input device ID, source, and motion axis are not supported by this Android + * device, this method returns {@code false}. In other words, if the {@link InputDevice} + * represented by the provided {code inputDeviceId} does not have a + * {@link InputDevice.MotionRange} with the provided {@code axis} and {@code source}, the method + * returns {@code false}. * - * <p><b>source</b>: use {@link MotionEvent#getSource()} if calling this method in response to a - * {@link MotionEvent}. Otherwise, use a valid source for the {@link InputDevice}. You can use - * {@link InputDevice#getMotionRanges()} to get all the {@link InputDevice.MotionRange}s for the - * {@link InputDevice}, from which you can derive all the valid sources for the device. + * <p>If the provided input device ID, source, and motion axis are supported by this Android + * device, this method returns {@code true} only if the provided arguments are supported for + * scroll haptics. Otherwise, this method returns {@code false}. * * @param inputDeviceId the ID of the {@link InputDevice} that generated the motion that may * produce scroll haptics. * @param source the input source of the motion that may produce scroll haptics. * @param axis the axis of the motion that may produce scroll haptics. * @return {@code true} if motions generated by the provided input and motion configuration - * should produce scroll haptics. {@code false} otherwise. + * can produce scroll haptics. {@code false} otherwise. + * + * @see #getHapticScrollFeedbackTickInterval(int, int, int) + * @see InputDevice#getMotionRanges() + * @see InputDevice#getMotionRange(int) + * @see InputDevice#getMotionRange(int, int) */ @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) - public boolean isHapticScrollFeedbackEnabled(int inputDeviceId, int axis, int source) { + public boolean isHapticScrollFeedbackEnabled( + int inputDeviceId, + @HapticScrollFeedbackProvider.HapticScrollFeedbackAxis int axis, + int source) { if (!isInputDeviceInfoValid(inputDeviceId, axis, source)) return false; - if (source == InputDevice.SOURCE_ROTARY_ENCODER) { + if (source == InputDevice.SOURCE_ROTARY_ENCODER && axis == MotionEvent.AXIS_SCROLL) { return mRotaryEncoderHapticScrollFeedbackEnabled; } @@ -1285,9 +1288,14 @@ public class ViewConfiguration { * configuration. If scroll haptics is disabled for the given configuration, or if the * device does not support scroll tick haptics for the given configuration, this method * returns {@code Integer.MAX_VALUE}. + * + * @see #isHapticScrollFeedbackEnabled(int, int, int) */ @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) - public int getHapticScrollFeedbackTickInterval(int inputDeviceId, int axis, int source) { + public int getHapticScrollFeedbackTickInterval( + int inputDeviceId, + @HapticScrollFeedbackProvider.HapticScrollFeedbackAxis int axis, + int source) { if (!mRotaryEncoderHapticScrollFeedbackEnabled) { return NO_HAPTIC_SCROLL_TICK_INTERVAL; } @@ -1296,7 +1304,7 @@ public class ViewConfiguration { return NO_HAPTIC_SCROLL_TICK_INTERVAL; } - if (source == InputDevice.SOURCE_ROTARY_ENCODER) { + if (source == InputDevice.SOURCE_ROTARY_ENCODER && axis == MotionEvent.AXIS_SCROLL) { return mRotaryEncoderHapticScrollFeedbackTickIntervalPixels; } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index a1165423be8e..26ceea6d1e4c 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -4520,7 +4520,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (!trackMotionScroll(delta, delta)) { if (Flags.platformWidgetHapticScrollFeedback()) { initHapticScrollFeedbackProviderIfNotExists(); - mHapticScrollFeedbackProvider.onScrollProgress(event, axis, delta); + mHapticScrollFeedbackProvider.onScrollProgress( + event.getDeviceId(), event.getSource(), axis, delta); } initDifferentialFlingHelperIfNotExists(); mDifferentialMotionFlingHelper.onMotionEvent(event, axis); @@ -4536,7 +4537,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (Flags.platformWidgetHapticScrollFeedback()) { initHapticScrollFeedbackProviderIfNotExists(); mHapticScrollFeedbackProvider.onScrollLimit( - event, axis, /* isStart= */ hitTopLimit); + event.getDeviceId(), event.getSource(), axis, + /* isStart= */ hitTopLimit); } if (hitTopLimit) { mEdgeGlowTop.onPullDistance(overscroll, 0.5f); diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 90b077b21d85..e0e72ba1b9db 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -1014,12 +1014,14 @@ public class ScrollView extends FrameLayout { if (Flags.platformWidgetHapticScrollFeedback()) { initHapticScrollFeedbackProviderIfNotExists(); mHapticScrollFeedbackProvider.onScrollLimit( - event, axis, /* isStart= */ newScrollY == 0); + event.getDeviceId(), event.getSource(), axis, + /* isStart= */ newScrollY == 0); } } else { if (Flags.platformWidgetHapticScrollFeedback()) { initHapticScrollFeedbackProviderIfNotExists(); - mHapticScrollFeedbackProvider.onScrollProgress(event, axis, delta); + mHapticScrollFeedbackProvider.onScrollProgress( + event.getDeviceId(), event.getSource(), axis, delta); } initDifferentialFlingHelperIfNotExists(); mDifferentialMotionFlingHelper.onMotionEvent(event, axis); diff --git a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java index a2c41e440107..d2af2a734330 100644 --- a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java +++ b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java @@ -47,7 +47,6 @@ public final class HapticScrollFeedbackProviderTest { private static final int INPUT_DEVICE_2 = 2; private TestView mView; - private long mCurrentTimeMillis = 1000; // arbitrary starting time value @Mock ViewConfiguration mMockViewConfig; @@ -67,24 +66,15 @@ public final class HapticScrollFeedbackProviderTest { setHapticScrollFeedbackEnabled(false); // Call different types scroll feedback methods; non of them should produce feedback because // feedback has been disabled. - mProvider.onSnapToItem(createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL); mProvider.onSnapToItem( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL); mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ true); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ true); mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 10); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ -9); - mProvider.onScrollProgress( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 300); mProvider.onScrollProgress( @@ -95,14 +85,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testSnapToItem_withMotionEvent() { - mProvider.onSnapToItem(createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_ITEM_FOCUS); - } - - @Test - public void testSnapToItem_withDeviceIdAndSource() { + public void testSnapToItem() { mProvider.onSnapToItem( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL); @@ -110,15 +93,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_start_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ true); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT); - } - - @Test - public void testScrollLimit_start_withDeviceIdAndSource() { + public void testScrollLimit_start() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ true); @@ -127,15 +102,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_stop_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT); - } - - @Test - public void testScrollLimit_stop_withDeviceIdAndSource() { + public void testScrollLimit_stop() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -144,21 +111,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollProgress_zeroTickInterval_withMotionEvent() { - setHapticScrollTickInterval(0); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 10); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 30); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 30); - - assertNoFeedback(mView); - } - - @Test - public void testScrollProgress_zeroTickInterval_withDeviceIdAndSource() { + public void testScrollProgress_zeroTickInterval() { setHapticScrollTickInterval(0); mProvider.onScrollProgress( @@ -172,30 +125,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollProgress_progressEqualsOrExceedsPositiveThreshold_withMotionEvent() { - setHapticScrollTickInterval(100); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 20); - - assertNoFeedback(mView); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 80); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 1); - - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 80); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 40); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 2); - } - - @Test - public void testScrollProgress_progressEqualsOrExceedsPositiveThreshold_withDeviceIdAndSrc() { + public void testScrollProgress_progressEqualsOrExceedsPositiveThreshold() { setHapticScrollTickInterval(100); mProvider.onScrollProgress( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, @@ -217,37 +147,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollProgress_progressEqualsOrExceedsNegativeThreshold_withMotionEvent() { - setHapticScrollTickInterval(100); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), - MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ -20); - - assertNoFeedback(mView); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), - MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ -80); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 1); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), - MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ -70); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), - MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ -40); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 2); - } - - @Test - public void testScrollProgress_progressEqualsOrExceedsNegativeThreshold_withDeviceIdAndSrc() { + public void testScrollProgress_progressEqualsOrExceedsNegativeThreshold() { setHapticScrollTickInterval(100); mProvider.onScrollProgress( @@ -273,42 +173,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollProgress_positiveAndNegativeProgresses_withMotionEvent() { - setHapticScrollTickInterval(100); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 20); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), - MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ -90); - - assertNoFeedback(mView); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 10); - - assertNoFeedback(mView); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), - MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ -50); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 1); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 40); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 50); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 60); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 2); - } - - @Test - public void testScrollProgress_positiveAndNegativeProgresses_withDeviceIdAndSource() { + public void testScrollProgress_positiveAndNegativeProgresses() { setHapticScrollTickInterval(100); mProvider.onScrollProgress( @@ -348,19 +213,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollProgress_singleProgressExceedsThreshold_withMotionEvent() { - setHapticScrollTickInterval(100); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), - MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ 1000); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 1); - } - - @Test - public void testScrollProgress_singleProgressExceedsThreshold_withDeviceIdAndSource() { + public void testScrollProgress_singleProgressExceedsThreshold() { setHapticScrollTickInterval(100); mProvider.onScrollProgress( @@ -371,17 +224,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_startAndEndLimit_playsOnlyOneFeedback_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ true); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT); - } - - @Test - public void testScrollLimit_startAndEndLimit_playsOnlyOneFeedback_withDeviceIdAndSource() { + public void testScrollLimit_startAndEndLimit_playsOnlyOneFeedback() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -393,17 +236,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_doubleStartLimit_playsOnlyOneFeedback_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ true); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ true); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT); - } - - @Test - public void testScrollLimit_doubleStartLimit_playsOnlyOneFeedback_withDeviceIdAndSource() { + public void testScrollLimit_doubleStartLimit_playsOnlyOneFeedback() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ true); @@ -415,17 +248,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_doubleEndLimit_playsOnlyOneFeedback_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT); - } - - @Test - public void testScrollLimit_doubleEndLimit_playsOnlyOneFeedback_withDeviceIdAndSource() { + public void testScrollLimit_doubleEndLimit_playsOnlyOneFeedback() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -439,14 +262,15 @@ public final class HapticScrollFeedbackProviderTest { @Test public void testScrollLimit_notEnabledWithZeroProgress() { mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(INPUT_DEVICE_1), MotionEvent.AXIS_SCROLL, + INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); mProvider.onScrollProgress( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 0); mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ true); + INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, + /* isStart= */ true); mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -455,20 +279,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_enabledWithProgress_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 80); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2); - } - - @Test - public void testScrollLimit_enabledWithProgress_withDeviceIdAndSource() { + public void testScrollLimit_enabledWithProgress() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -484,19 +295,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_enabledWithSnap_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - mProvider.onSnapToItem(createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2); - } - - @Test - public void testScrollLimit_enabledWithSnap_withDeviceIdAndSource() { + public void testScrollLimit_enabledWithSnap() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -511,19 +310,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_enabledWithDissimilarSnap_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - mProvider.onSnapToItem(createTouchMoveEvent(), MotionEvent.AXIS_X); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2); - } - - @Test - public void testScrollLimit_enabledWithDissimilarSnap_withDeviceIdAndSource() { + public void testScrollLimit_enabledWithDissimilarSnap() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -538,20 +325,7 @@ public final class HapticScrollFeedbackProviderTest { } @Test - public void testScrollLimit_enabledWithDissimilarProgress_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - mProvider.onScrollProgress( - createTouchMoveEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 80); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2); - } - - @Test - public void testScrollLimit_enabledWithDissimilarProgress_withDeviceIdAndSource() { + public void testScrollLimit_enabledWithDissimilarProgress() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -566,54 +340,9 @@ public final class HapticScrollFeedbackProviderTest { assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2); } - @Test - public void testScrollLimit_enabledWithDissimilarLimit_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - mProvider.onScrollLimit(createTouchMoveEvent(), MotionEvent.AXIS_SCROLL, false); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* isStart= */ false); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 3); - } @Test - public void testScrollLimit_enabledWithDissimilarLimit_withDeviceIdAndSource() { - mProvider.onScrollLimit( - INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, - /* isStart= */ false); - - mProvider.onScrollLimit(INPUT_DEVICE_2, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.AXIS_X, - /* isStart= */ false); - mProvider.onScrollLimit( - INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, - /* isStart= */ false); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 3); - } - - @Test - public void testScrollLimit_enabledWithMotionFromDifferentDeviceId_withMotionEvent() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(INPUT_DEVICE_1), - MotionEvent.AXIS_SCROLL, - /* isStart= */ false); - - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(INPUT_DEVICE_2), - MotionEvent.AXIS_SCROLL, - /* isStart= */ false); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(INPUT_DEVICE_1), - MotionEvent.AXIS_SCROLL, - /* isStart= */ false); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 3); - } - - @Test - public void testScrollLimit_enabledWithMotionFromDifferentDeviceId_withDeviceIdAndSource() { + public void testScrollLimit_enabledWithMotionFromDifferentDeviceId() { mProvider.onScrollLimit( INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, /* isStart= */ false); @@ -632,57 +361,6 @@ public final class HapticScrollFeedbackProviderTest { assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 3); } - @Test - public void testSnapToItem_differentApis() { - mProvider.onSnapToItem( - INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL); - mProvider.onSnapToItem(createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_ITEM_FOCUS, 2); - } - - @Test - public void testScrollLimit_differentApis() { - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(INPUT_DEVICE_1), - MotionEvent.AXIS_SCROLL, - /* isStart= */ false); - mProvider.onScrollLimit( - INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, - /* isStart= */ false); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1); - - mProvider.onScrollLimit( - INPUT_DEVICE_2, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, - /* isStart= */ true); - mProvider.onScrollLimit( - createRotaryEncoderScrollEvent(INPUT_DEVICE_2), - MotionEvent.AXIS_SCROLL, - /* isStart= */ true); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2); - } - - @Test - public void testScrollProgress_differentApis() { - setHapticScrollTickInterval(100); - - // Neither types of APIs independently excceeds the "100" tick interval. - // But the combined deltas pass 100. - mProvider.onScrollProgress( - INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ 20); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 40); - mProvider.onScrollProgress( - INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, - /* deltaInPixels= */ 30); - mProvider.onScrollProgress( - createRotaryEncoderScrollEvent(), MotionEvent.AXIS_SCROLL, /* deltaInPixels= */ 30); - - assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 1); - } private void assertNoFeedback(TestView view) { for (int feedback : new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK}) { @@ -715,41 +393,6 @@ public final class HapticScrollFeedbackProviderTest { .thenReturn(enabled); } - private MotionEvent createTouchMoveEvent() { - long downTime = mCurrentTimeMillis; - long eventTime = mCurrentTimeMillis + 2; // arbitrary increment from the down time. - ++mCurrentTimeMillis; - return MotionEvent.obtain( - downTime , eventTime, MotionEvent.ACTION_MOVE, /* x= */ 3, /* y= */ 5, 0); - } - - private MotionEvent createRotaryEncoderScrollEvent() { - return createRotaryEncoderScrollEvent(INPUT_DEVICE_1); - } - - private MotionEvent createRotaryEncoderScrollEvent(int deviceId) { - MotionEvent.PointerProperties props = new MotionEvent.PointerProperties(); - props.id = 0; - - MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords(); - coords.setAxisValue(MotionEvent.AXIS_SCROLL, 20); - - return MotionEvent.obtain(0 /* downTime */, - ++mCurrentTimeMillis, - MotionEvent.ACTION_SCROLL, - /* pointerCount= */ 1, - new MotionEvent.PointerProperties[] {props}, - new MotionEvent.PointerCoords[] {coords}, - /* metaState= */ 0, - /* buttonState= */ 0, - /* xPrecision= */ 0, - /* yPrecision= */ 0, - deviceId, - /* edgeFlags= */ 0, - InputDevice.SOURCE_ROTARY_ENCODER, - /* flags= */ 0); - } - private static class TestView extends View { final Map<Integer, Integer> mFeedbackCount = new HashMap<>(); |