1 /**
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.view.flags.Flags;
22 
23 /**
24  * Provides feedback to the user for scroll events on a {@link View}. The type of feedback provided
25  * to the user may depend on the {@link InputDevice} that generated the scroll events.
26  *
27  * <p>An example of the type of feedback that this interface may provide is haptic feedback (that
28  * is, tactile feedback that provide the user physical feedback for their scroll).
29  *
30  * <p>The interface provides methods for the client to report different scroll events. The client
31  * should report all scroll events that they want to be considered for scroll feedback using the
32  * respective methods. The interface will process these events and provide scroll feedback based on
33  * its specific feedback implementation.
34  *
35  * <h3>Obtaining the correct arguments for methods in this interface</h3>
36  *
37  * <p>Methods in this interface rely on the provision of valid {@link InputDevice} ID and source, as
38  * well as the {@link MotionEvent} axis that generated a specific scroll event. The
39  * {@link InputDevice} represented by the provided ID must have a {@link InputDevice.MotionRange}
40  * with the provided source and axis. See below for more details on obtaining the right arguments
41  * for your method call.
42  *
43  * <ul>
44  *
45  * <li><p><b>inputDeviceId</b>: should always be the ID of the {@link InputDevice} that generated
46  * the scroll event. If calling this method in response to a {@link MotionEvent}, use the device ID
47  * that is reported by the event, which can be obtained using {@link MotionEvent#getDeviceId()}.
48  * Otherwise, use a valid ID that is obtained from {@link InputDevice#getId()}, or from an
49  * {@link android.hardware.input.InputManager} instance
50  * ({@link android.hardware.input.InputManager#getInputDeviceIds()} gives all the valid input
51  * device IDs).
52  *
53  * <li><p><b>source</b>: should always be the {@link InputDevice} source that generated the scroll
54  * event. Use {@link MotionEvent#getSource()} if calling this method in response to a
55  * {@link MotionEvent}. Otherwise, use a valid source for the {@link InputDevice}. You can use
56  * {@link InputDevice#getMotionRanges()} to get all the {@link InputDevice.MotionRange}s for the
57  * {@link InputDevice}, from which you can derive all the valid sources for the device.
58  *
59  * <li><p><b>axis</b>: should always be the axis whose axis value produced the scroll event.
60  * A {@link MotionEvent} may report data for multiple axes, and each axis may have multiple data
61  * points for different pointers. Use the axis whose movement produced the specific scroll event.
62  * The motion value for an axis can be obtained using {@link MotionEvent#getAxisValue(int)}.
63  * You can use {@link InputDevice#getMotionRanges()} to get all the {@link InputDevice.MotionRange}s
64  * for the {@link InputDevice}, from which you can derive all the valid axes for the device.
65  *
66  * </ul>
67  *
68  * <b>Note</b> that not all valid input device source and motion axis inputs are necessarily
69  * supported for scroll feedback; the implementation may choose to provide no feedback for some
70  * valid input device source and motion axis arguments.
71  */
72 @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API)
73 public interface ScrollFeedbackProvider {
74 
75     /**
76      * Creates a {@link ScrollFeedbackProvider} implementation for this device.
77      *
78      * <p>Use a feedback provider created by this method, unless you intend to use your custom
79      * scroll feedback providing logic. This allows your use cases to generate scroll feedback that
80      * is consistent with the rest of the use cases on the device.
81      *
82      * @param view the {@link View} for which to provide scroll feedback.
83      * @return the default {@link ScrollFeedbackProvider} implementation for the device.
84      */
85     @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API)
86     @NonNull
createProvider(@onNull View view)87     static ScrollFeedbackProvider createProvider(@NonNull View view) {
88         return new HapticScrollFeedbackProvider(view);
89     }
90 
91     /**
92      * Call this when the view has snapped to an item.
93      *
94      * @param inputDeviceId the ID of the {@link InputDevice} that generated the motion triggering
95      *          the snap.
96      * @param source the input source of the motion causing the snap.
97      * @param axis the axis of {@code event} that caused the item to snap.
98      */
99     @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API)
onSnapToItem(int inputDeviceId, int source, int axis)100     void onSnapToItem(int inputDeviceId, int source, int axis);
101 
102     /**
103      * Call this when the view has reached the scroll limit.
104      *
105      * <p>Note that a feedback may not be provided on every call to this method. This interface, for
106      * instance, may provide feedback on every `N`th scroll limit event. For the interface to
107      * properly provide feedback when needed, call this method for each scroll limit event that you
108      * want to be accounted to scroll limit feedback.
109      *
110      * @param inputDeviceId the ID of the {@link InputDevice} that caused scrolling to hit limit.
111      * @param source the input source of the motion that caused scrolling to hit the limit.
112      * @param axis the axis of {@code event} that caused scrolling to hit the limit.
113      * @param isStart {@code true} if scrolling hit limit at the start of the scrolling list, and
114      *                {@code false} if the scrolling hit limit at the end of the scrolling list.
115      *                <i>start</i> and <i>end<i> in this context are not geometrical references.
116      *                Instead, they refer to the start and end of a scrolling experience. As such,
117      *                "start" for some views may be at the bottom of a scrolling list, while it may
118      *                be at the top of scrolling list for others.
119      */
120     @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API)
onScrollLimit(int inputDeviceId, int source, int axis, boolean isStart)121     void onScrollLimit(int inputDeviceId, int source, int axis, boolean isStart);
122 
123     /**
124      * Call this when the view has scrolled.
125      *
126      * <p>Different axes have different ways to map their raw axis values to pixels for scrolling.
127      * When calling this method, use the scroll values in pixels by which the view was scrolled; do
128      * not use the raw axis values. That is, use whatever value is passed to one of View's scrolling
129      * methods (example: {@link View#scrollBy(int, int)}). For example, for vertical scrolling on
130      * {@link MotionEvent#AXIS_SCROLL}, convert the raw axis value to the equivalent pixels by using
131      * {@link ViewConfiguration#getScaledVerticalScrollFactor()}, and use that value for this method
132      * call.
133      *
134      * <p>Note that a feedback may not be provided on every call to this method. This interface, for
135      * instance, may provide feedback for every `x` pixels scrolled. For the interface to properly
136      * track scroll progress and provide feedback when needed, call this method for each scroll
137      * event that you want to be accounted to scroll feedback.
138      *
139      * @param inputDeviceId the ID of the {@link InputDevice} that caused scroll progress.
140      * @param source the input source of the motion that caused scroll progress.
141      * @param axis the axis of {@code event} that caused scroll progress.
142      * @param deltaInPixels the amount of scroll progress, in pixels.
143      */
144     @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API)
onScrollProgress(int inputDeviceId, int source, int axis, int deltaInPixels)145     void onScrollProgress(int inputDeviceId, int source, int axis, int deltaInPixels);
146 }
147