• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.hardware.input;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.SystemApi;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.os.SystemClock;
26 import android.view.InputEvent;
27 import android.view.MotionEvent;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 
32 /**
33  * An event describing a stylus interaction originating from a remote device.
34  *
35  * The tool type, location and action are required; tilts and pressure are optional.
36  *
37  * @hide
38  */
39 @SystemApi
40 public final class VirtualStylusMotionEvent implements Parcelable {
41     private static final int TILT_MIN = -90;
42     private static final int TILT_MAX = 90;
43     private static final int PRESSURE_MIN = 0;
44     private static final int PRESSURE_MAX = 255;
45 
46     /** @hide */
47     public static final int TOOL_TYPE_UNKNOWN = MotionEvent.TOOL_TYPE_UNKNOWN;
48     /** Tool type indicating that a stylus is the origin of the event. */
49     public static final int TOOL_TYPE_STYLUS = MotionEvent.TOOL_TYPE_STYLUS;
50     /** Tool type indicating that an eraser is the origin of the event. */
51     public static final int TOOL_TYPE_ERASER = MotionEvent.TOOL_TYPE_ERASER;
52     /** @hide */
53     @IntDef(prefix = { "TOOL_TYPE_" }, value = {
54             TOOL_TYPE_UNKNOWN,
55             TOOL_TYPE_STYLUS,
56             TOOL_TYPE_ERASER,
57     })
58     @Retention(RetentionPolicy.SOURCE)
59     public @interface ToolType {}
60 
61     /** @hide */
62     public static final int ACTION_UNKNOWN = -1;
63     /**
64      * Action indicating the stylus has been pressed down to the screen. ACTION_DOWN with pressure
65      * {@code 0} indicates that the stylus is hovering over the screen, and non-zero pressure
66      * indicates that the stylus is touching the screen.
67      */
68     public static final int ACTION_DOWN = MotionEvent.ACTION_DOWN;
69     /** Action indicating the stylus has been lifted from the screen. */
70     public static final int ACTION_UP = MotionEvent.ACTION_UP;
71     /** Action indicating the stylus has been moved along the screen. */
72     public static final int ACTION_MOVE = MotionEvent.ACTION_MOVE;
73     /** @hide */
74     @IntDef(prefix = { "ACTION_" }, value = {
75             ACTION_UNKNOWN,
76             ACTION_DOWN,
77             ACTION_UP,
78             ACTION_MOVE,
79     })
80     @Retention(RetentionPolicy.SOURCE)
81     public @interface Action {}
82 
83     @ToolType
84     private final int mToolType;
85     @Action
86     private final int mAction;
87     private final int mX;
88     private final int mY;
89     private final int mPressure;
90     private final int mTiltX;
91     private final int mTiltY;
92     private final long mEventTimeNanos;
93 
VirtualStylusMotionEvent(@oolType int toolType, @Action int action, int x, int y, int pressure, int tiltX, int tiltY, long eventTimeNanos)94     private VirtualStylusMotionEvent(@ToolType int toolType, @Action int action, int x, int y,
95             int pressure, int tiltX, int tiltY, long eventTimeNanos) {
96         mToolType = toolType;
97         mAction = action;
98         mX = x;
99         mY = y;
100         mPressure = pressure;
101         mTiltX = tiltX;
102         mTiltY = tiltY;
103         mEventTimeNanos = eventTimeNanos;
104     }
105 
VirtualStylusMotionEvent(@onNull Parcel parcel)106     private VirtualStylusMotionEvent(@NonNull Parcel parcel) {
107         mToolType = parcel.readInt();
108         mAction = parcel.readInt();
109         mX = parcel.readInt();
110         mY = parcel.readInt();
111         mPressure = parcel.readInt();
112         mTiltX = parcel.readInt();
113         mTiltY = parcel.readInt();
114         mEventTimeNanos = parcel.readLong();
115     }
116 
117     @Override
writeToParcel(@onNull Parcel dest, int flags)118     public void writeToParcel(@NonNull Parcel dest, int flags) {
119         dest.writeInt(mToolType);
120         dest.writeInt(mAction);
121         dest.writeInt(mX);
122         dest.writeInt(mY);
123         dest.writeInt(mPressure);
124         dest.writeInt(mTiltX);
125         dest.writeInt(mTiltY);
126         dest.writeLong(mEventTimeNanos);
127     }
128 
129     @Override
describeContents()130     public int describeContents() {
131         return 0;
132     }
133 
134     /**
135      * Returns the tool type associated with this event.
136      */
137     @ToolType
getToolType()138     public int getToolType() {
139         return mToolType;
140     }
141 
142     /**
143      * Returns the action associated with this event.
144      */
145     @Action
getAction()146     public int getAction() {
147         return mAction;
148     }
149 
150     /**
151      * Returns the x-axis location associated with this event.
152      */
getX()153     public int getX() {
154         return mX;
155     }
156 
157     /**
158      * Returns the y-axis location associated with this event.
159      */
getY()160     public int getY() {
161         return mY;
162     }
163 
164     /**
165      * Returns the pressure associated with this event. {@code 0} pressure indicates that the stylus
166      * is hovering, otherwise the stylus is touching the screen. Returns {@code 255} if omitted.
167      */
getPressure()168     public int getPressure() {
169         return mPressure;
170     }
171 
172     /**
173      * Returns the plane angle (in degrees, in the range of [{@code -90}, {@code 90}]) between the
174      * y-z plane and the plane containing both the stylus axis and the y axis. A positive tiltX is
175      * to the right, in the direction of increasing x values. {@code 0} tilt indicates that the
176      * stylus is perpendicular to the x-axis. Returns {@code 0} if omitted.
177      *
178      * @see Builder#setTiltX
179      */
getTiltX()180     public int getTiltX() {
181         return mTiltX;
182     }
183 
184     /**
185      * Returns the plane angle (in degrees, in the range of [{@code -90}, {@code 90}]) between the
186      * x-z plane and the plane containing both the stylus axis and the x axis. A positive tiltY is
187      * towards the user, in the direction of increasing y values. {@code 0} tilt indicates that the
188      * stylus is perpendicular to the y-axis. Returns {@code 0} if omitted.
189      *
190      * @see Builder#setTiltY
191      */
getTiltY()192     public int getTiltY() {
193         return mTiltY;
194     }
195 
196     /**
197      * Returns the time this event occurred, in the {@link SystemClock#uptimeMillis()} time base but
198      * with nanosecond (instead of millisecond) precision.
199      *
200      * @see InputEvent#getEventTime()
201      */
getEventTimeNanos()202     public long getEventTimeNanos() {
203         return mEventTimeNanos;
204     }
205 
206     /**
207      * Builder for {@link VirtualStylusMotionEvent}.
208      */
209     public static final class Builder {
210 
211         @ToolType
212         private int mToolType = TOOL_TYPE_UNKNOWN;
213         @Action
214         private int mAction = ACTION_UNKNOWN;
215         private int mX = 0;
216         private int mY = 0;
217         private boolean mIsXSet = false;
218         private boolean mIsYSet = false;
219         private int mPressure = PRESSURE_MAX;
220         private int mTiltX = 0;
221         private int mTiltY = 0;
222         private long mEventTimeNanos = 0L;
223 
224         /**
225          * Creates a {@link VirtualStylusMotionEvent} object with the current builder configuration.
226          *
227          * @throws IllegalArgumentException if one of the required arguments (action, tool type,
228          * x-axis location and y-axis location) is missing.
229          * {@link VirtualStylusMotionEvent} for a detailed explanation.
230          */
231         @NonNull
build()232         public VirtualStylusMotionEvent build() {
233             if (mToolType == TOOL_TYPE_UNKNOWN) {
234                 throw new IllegalArgumentException(
235                         "Cannot build stylus motion event with unset tool type");
236             }
237             if (mAction == ACTION_UNKNOWN) {
238                 throw new IllegalArgumentException(
239                         "Cannot build stylus motion event with unset action");
240             }
241             if (!mIsXSet) {
242                 throw new IllegalArgumentException(
243                         "Cannot build stylus motion event with unset x-axis location");
244             }
245             if (!mIsYSet) {
246                 throw new IllegalArgumentException(
247                         "Cannot build stylus motion event with unset y-axis location");
248             }
249             return new VirtualStylusMotionEvent(mToolType, mAction, mX, mY, mPressure, mTiltX,
250                     mTiltY, mEventTimeNanos);
251         }
252 
253         /**
254          * Sets the tool type of the event.
255          *
256          * @return this builder, to allow for chaining of calls
257          */
258         @NonNull
setToolType(@oolType int toolType)259         public Builder setToolType(@ToolType int toolType) {
260             if (toolType != TOOL_TYPE_STYLUS && toolType != TOOL_TYPE_ERASER) {
261                 throw new IllegalArgumentException("Unsupported stylus tool type: " + toolType);
262             }
263             mToolType = toolType;
264             return this;
265         }
266 
267         /**
268          * Sets the action of the event.
269          *
270          * @return this builder, to allow for chaining of calls
271          */
272         @NonNull
setAction(@ction int action)273         public Builder setAction(@Action int action) {
274             if (action != ACTION_DOWN && action != ACTION_UP && action != ACTION_MOVE) {
275                 throw new IllegalArgumentException("Unsupported stylus action : " + action);
276             }
277             mAction = action;
278             return this;
279         }
280 
281         /**
282          * Sets the x-axis location of the event.
283          *
284          * @return this builder, to allow for chaining of calls
285          */
286         @NonNull
setX(int absX)287         public Builder setX(int absX) {
288             mX = absX;
289             mIsXSet = true;
290             return this;
291         }
292 
293         /**
294          * Sets the y-axis location of the event.
295          *
296          * @return this builder, to allow for chaining of calls
297          */
298         @NonNull
setY(int absY)299         public Builder setY(int absY) {
300             mY = absY;
301             mIsYSet = true;
302             return this;
303         }
304 
305         /**
306          * Sets the pressure of the event. {@code 0} pressure indicates that the stylus is hovering,
307          * otherwise the stylus is touching the screen. This field is optional and can be omitted
308          * (defaults to {@code 255}).
309          *
310          * @param pressure The pressure of the stylus.
311          *
312          * @throws IllegalArgumentException if the pressure is smaller than 0 or greater than 255.
313          *
314          * @return this builder, to allow for chaining of calls
315          */
316         @NonNull
setPressure( @ntRangefrom = PRESSURE_MIN, to = PRESSURE_MAX) int pressure)317         public Builder setPressure(
318                 @IntRange(from = PRESSURE_MIN, to = PRESSURE_MAX) int pressure) {
319             if (pressure < PRESSURE_MIN || pressure > PRESSURE_MAX) {
320                 throw new IllegalArgumentException(
321                         "Pressure should be between " + PRESSURE_MIN + " and " + PRESSURE_MAX);
322             }
323             mPressure = pressure;
324             return this;
325         }
326 
327         /**
328          * Sets the x-axis tilt of the event in degrees. {@code 0} tilt indicates that the stylus is
329          * perpendicular to the x-axis. This field is optional and can be omitted (defaults to
330          * {@code 0}). Both x-axis tilt and y-axis tilt are used to derive the tilt and orientation
331          * of the stylus, given by {@link MotionEvent#AXIS_TILT} and
332          * {@link MotionEvent#AXIS_ORIENTATION} respectively.
333          *
334          * @throws IllegalArgumentException if the tilt is smaller than -90 or greater than 90.
335          *
336          * @return this builder, to allow for chaining of calls
337          *
338          * @see VirtualStylusMotionEvent#getTiltX
339          * @see <a href="https://source.android.com/docs/core/interaction/input/touch-devices#orientation-and-tilt-fields">
340          *     Stylus tilt and orientation</a>
341          */
342         @NonNull
setTiltX(@ntRangefrom = TILT_MIN, to = TILT_MAX) int tiltX)343         public Builder setTiltX(@IntRange(from = TILT_MIN, to = TILT_MAX) int tiltX) {
344             validateTilt(tiltX);
345             mTiltX = tiltX;
346             return this;
347         }
348 
349         /**
350          * Sets the y-axis tilt of the event in degrees. {@code 0} tilt indicates that the stylus is
351          * perpendicular to the y-axis. This field is optional and can be omitted (defaults to
352          * {@code 0}). Both x-axis tilt and y-axis tilt are used to derive the tilt and orientation
353          * of the stylus, given by {@link MotionEvent#AXIS_TILT} and
354          * {@link MotionEvent#AXIS_ORIENTATION} respectively.
355          *
356          * @throws IllegalArgumentException if the tilt is smaller than -90 or greater than 90.
357          *
358          * @return this builder, to allow for chaining of calls
359          *
360          * @see VirtualStylusMotionEvent#getTiltY
361          * @see <a href="https://source.android.com/docs/core/interaction/input/touch-devices#orientation-and-tilt-fields">
362          *     Stylus tilt and orientation</a>
363          */
364         @NonNull
setTiltY(@ntRangefrom = TILT_MIN, to = TILT_MAX) int tiltY)365         public Builder setTiltY(@IntRange(from = TILT_MIN, to = TILT_MAX) int tiltY) {
366             validateTilt(tiltY);
367             mTiltY = tiltY;
368             return this;
369         }
370 
371         /**
372          * Sets the time (in nanoseconds) when this specific event was generated. This may be
373          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
374          * millisecond), but can be different depending on the use case.
375          * This field is optional and can be omitted.
376          * <p>
377          * If this field is unset, then the time at which this event is sent to the framework would
378          * be considered as the event time (even though
379          * {@link VirtualStylusMotionEvent#getEventTimeNanos()}) would return {@code 0L}).
380          *
381          * @return this builder, to allow for chaining of calls
382          * @see InputEvent#getEventTime()
383          */
384         @NonNull
setEventTimeNanos(long eventTimeNanos)385         public Builder setEventTimeNanos(long eventTimeNanos) {
386             if (eventTimeNanos < 0L) {
387                 throw new IllegalArgumentException("Event time cannot be negative");
388             }
389             mEventTimeNanos = eventTimeNanos;
390             return this;
391         }
392 
validateTilt(int tilt)393         private void validateTilt(int tilt) {
394             if (tilt < TILT_MIN || tilt > TILT_MAX) {
395                 throw new IllegalArgumentException(
396                         "Tilt must be between " + TILT_MIN + " and " + TILT_MAX);
397             }
398         }
399     }
400 
401     @NonNull
402     public static final Parcelable.Creator<VirtualStylusMotionEvent> CREATOR =
403             new Parcelable.Creator<>() {
404                 public VirtualStylusMotionEvent createFromParcel(Parcel source) {
405                     return new VirtualStylusMotionEvent(source);
406                 }
407                 public VirtualStylusMotionEvent[] newArray(int size) {
408                     return new VirtualStylusMotionEvent[size];
409                 }
410             };
411 }
412