1 /* 2 * Copyright (C) 2021 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.FloatRange; 20 import android.annotation.IntDef; 21 import android.annotation.IntRange; 22 import android.annotation.NonNull; 23 import android.annotation.SystemApi; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.os.SystemClock; 27 import android.view.InputEvent; 28 import android.view.MotionEvent; 29 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 33 /** 34 * An event describing a touchscreen interaction originating from a remote device. 35 * 36 * The pointer id, tool type, action, and location are required; pressure and main axis size are 37 * optional. 38 * 39 * Note: A VirtualTouchEvent with ACTION_CANCEL can only be created with TOOL_TYPE_PALM (and vice 40 * versa). Events are injected into the uinput kernel module, which has no concept of cancelling 41 * an action. The only way to state the intention that a pointer should not be handled as a pointer 42 * is to change its tool type to TOOL_TYPE_PALM. 43 * 44 * @hide 45 */ 46 @SystemApi 47 public final class VirtualTouchEvent implements Parcelable { 48 49 /** @hide */ 50 public static final int TOOL_TYPE_UNKNOWN = MotionEvent.TOOL_TYPE_UNKNOWN; 51 /** Tool type indicating that the user's finger is the origin of the event. */ 52 public static final int TOOL_TYPE_FINGER = MotionEvent.TOOL_TYPE_FINGER; 53 /** 54 * Tool type indicating that a user's palm (or other input mechanism to be rejected) is the 55 * origin of the event. 56 */ 57 public static final int TOOL_TYPE_PALM = MotionEvent.TOOL_TYPE_PALM; 58 /** @hide */ 59 @IntDef(prefix = { "TOOL_TYPE_" }, value = { 60 TOOL_TYPE_UNKNOWN, 61 TOOL_TYPE_FINGER, 62 TOOL_TYPE_PALM, 63 }) 64 @Retention(RetentionPolicy.SOURCE) 65 public @interface ToolType {} 66 67 /** @hide */ 68 public static final int ACTION_UNKNOWN = -1; 69 /** Action indicating the tool has been pressed down to the touchscreen. */ 70 public static final int ACTION_DOWN = MotionEvent.ACTION_DOWN; 71 /** Action indicating the tool has been lifted from the touchscreen. */ 72 public static final int ACTION_UP = MotionEvent.ACTION_UP; 73 /** Action indicating the tool has been moved along the face of the touchscreen. */ 74 public static final int ACTION_MOVE = MotionEvent.ACTION_MOVE; 75 /** Action indicating the tool cancelled the current movement. */ 76 public static final int ACTION_CANCEL = MotionEvent.ACTION_CANCEL; 77 /** @hide */ 78 @IntDef(prefix = { "ACTION_" }, value = { 79 ACTION_UNKNOWN, 80 ACTION_DOWN, 81 ACTION_UP, 82 ACTION_MOVE, 83 ACTION_CANCEL, 84 }) 85 @Retention(RetentionPolicy.SOURCE) 86 public @interface Action {} 87 88 // The maximum number of pointers that can be touching the screen at once. (See MAX_POINTERS 89 // in frameworks/native/include/input/Input.h) 90 private static final int MAX_POINTERS = 16; 91 92 private final int mPointerId; 93 private final @ToolType int mToolType; 94 private final @Action int mAction; 95 private final float mX; 96 private final float mY; 97 private final float mPressure; 98 private final float mMajorAxisSize; 99 private final long mEventTimeNanos; 100 VirtualTouchEvent(int pointerId, @ToolType int toolType, @Action int action, float x, float y, float pressure, float majorAxisSize, long eventTimeNanos)101 private VirtualTouchEvent(int pointerId, @ToolType int toolType, @Action int action, 102 float x, float y, float pressure, float majorAxisSize, long eventTimeNanos) { 103 mPointerId = pointerId; 104 mToolType = toolType; 105 mAction = action; 106 mX = x; 107 mY = y; 108 mPressure = pressure; 109 mMajorAxisSize = majorAxisSize; 110 mEventTimeNanos = eventTimeNanos; 111 } 112 VirtualTouchEvent(@onNull Parcel parcel)113 private VirtualTouchEvent(@NonNull Parcel parcel) { 114 mPointerId = parcel.readInt(); 115 mToolType = parcel.readInt(); 116 mAction = parcel.readInt(); 117 mX = parcel.readFloat(); 118 mY = parcel.readFloat(); 119 mPressure = parcel.readFloat(); 120 mMajorAxisSize = parcel.readFloat(); 121 mEventTimeNanos = parcel.readLong(); 122 } 123 124 @Override writeToParcel(@onNull Parcel dest, int flags)125 public void writeToParcel(@NonNull Parcel dest, int flags) { 126 dest.writeInt(mPointerId); 127 dest.writeInt(mToolType); 128 dest.writeInt(mAction); 129 dest.writeFloat(mX); 130 dest.writeFloat(mY); 131 dest.writeFloat(mPressure); 132 dest.writeFloat(mMajorAxisSize); 133 dest.writeLong(mEventTimeNanos); 134 } 135 136 @Override describeContents()137 public int describeContents() { 138 return 0; 139 } 140 141 @Override toString()142 public String toString() { 143 return "VirtualTouchEvent(" 144 + " pointerId=" + mPointerId 145 + " toolType=" + MotionEvent.toolTypeToString(mToolType) 146 + " action=" + MotionEvent.actionToString(mAction) 147 + " x=" + mX 148 + " y=" + mY 149 + " pressure=" + mPressure 150 + " majorAxisSize=" + mMajorAxisSize 151 + " eventTime(ns)=" + mEventTimeNanos; 152 } 153 154 /** 155 * Returns the pointer id associated with this event. 156 */ getPointerId()157 public int getPointerId() { 158 return mPointerId; 159 } 160 161 /** 162 * Returns the tool type associated with this event. 163 */ getToolType()164 public @ToolType int getToolType() { 165 return mToolType; 166 } 167 168 /** 169 * Returns the action associated with this event. 170 */ getAction()171 public @Action int getAction() { 172 return mAction; 173 } 174 175 /** 176 * Returns the x-axis location associated with this event. 177 */ getX()178 public float getX() { 179 return mX; 180 } 181 182 /** 183 * Returns the y-axis location associated with this event. 184 */ getY()185 public float getY() { 186 return mY; 187 } 188 189 /** 190 * Returns the pressure associated with this event. Returns {@link Float#NaN} if omitted. 191 */ getPressure()192 public float getPressure() { 193 return mPressure; 194 } 195 196 /** 197 * Returns the major axis size associated with this event. Returns {@link Float#NaN} if omitted. 198 */ getMajorAxisSize()199 public float getMajorAxisSize() { 200 return mMajorAxisSize; 201 } 202 203 /** 204 * Returns the time this event occurred, in the {@link SystemClock#uptimeMillis()} time base but 205 * with nanosecond (instead of millisecond) precision. 206 * 207 * @see InputEvent#getEventTime() 208 */ getEventTimeNanos()209 public long getEventTimeNanos() { 210 return mEventTimeNanos; 211 } 212 213 /** 214 * Builder for {@link VirtualTouchEvent}. 215 */ 216 public static final class Builder { 217 218 private @ToolType int mToolType = TOOL_TYPE_UNKNOWN; 219 private int mPointerId = MotionEvent.INVALID_POINTER_ID; 220 private @Action int mAction = ACTION_UNKNOWN; 221 private float mX = Float.NaN; 222 private float mY = Float.NaN; 223 private float mPressure = Float.NaN; 224 private float mMajorAxisSize = Float.NaN; 225 private long mEventTimeNanos = 0L; 226 227 /** 228 * Creates a {@link VirtualTouchEvent} object with the current builder configuration. 229 * 230 * @throws IllegalArgumentException if one of the required arguments is missing or if 231 * ACTION_CANCEL is not set in combination with TOOL_TYPE_PALM. See 232 * {@link VirtualTouchEvent} for a detailed explanation. 233 */ build()234 public @NonNull VirtualTouchEvent build() { 235 if (mToolType == TOOL_TYPE_UNKNOWN || mPointerId == MotionEvent.INVALID_POINTER_ID 236 || mAction == ACTION_UNKNOWN || Float.isNaN(mX) || Float.isNaN(mY)) { 237 throw new IllegalArgumentException( 238 "Cannot build virtual touch event with unset required fields"); 239 } 240 if ((mToolType == TOOL_TYPE_PALM && mAction != ACTION_CANCEL) 241 || (mAction == ACTION_CANCEL && mToolType != TOOL_TYPE_PALM)) { 242 throw new IllegalArgumentException( 243 "ACTION_CANCEL and TOOL_TYPE_PALM must always appear together"); 244 } 245 return new VirtualTouchEvent(mPointerId, mToolType, mAction, mX, mY, mPressure, 246 mMajorAxisSize, mEventTimeNanos); 247 } 248 249 /** 250 * Sets the pointer id of the event. 251 * 252 * <p>A Valid pointer id need to be in the range of 0 to 15. 253 * 254 * @return this builder, to allow for chaining of calls 255 */ setPointerId( @ntRangefrom = 0, to = MAX_POINTERS - 1) int pointerId)256 public @NonNull Builder setPointerId( 257 @IntRange(from = 0, to = MAX_POINTERS - 1) int pointerId) { 258 if (pointerId < 0 || pointerId > 15) { 259 throw new IllegalArgumentException( 260 "The pointer id must be in the range 0 - " + (MAX_POINTERS - 1) 261 + "inclusive, but was: " + pointerId); 262 } 263 mPointerId = pointerId; 264 return this; 265 } 266 267 /** 268 * Sets the tool type of the event. 269 * 270 * @return this builder, to allow for chaining of calls 271 */ setToolType(@oolType int toolType)272 public @NonNull Builder setToolType(@ToolType int toolType) { 273 if (toolType != TOOL_TYPE_FINGER && toolType != TOOL_TYPE_PALM) { 274 throw new IllegalArgumentException("Unsupported touch event tool type"); 275 } 276 mToolType = toolType; 277 return this; 278 } 279 280 /** 281 * Sets the action of the event. 282 * 283 * @return this builder, to allow for chaining of calls 284 */ setAction(@ction int action)285 public @NonNull Builder setAction(@Action int action) { 286 if (action != ACTION_DOWN && action != ACTION_UP && action != ACTION_MOVE 287 && action != ACTION_CANCEL) { 288 throw new IllegalArgumentException( 289 "Unsupported touch event action type: " + action); 290 } 291 mAction = action; 292 return this; 293 } 294 295 /** 296 * Sets the x-axis location of the event. 297 * 298 * @return this builder, to allow for chaining of calls 299 */ setX(float absX)300 public @NonNull Builder setX(float absX) { 301 mX = absX; 302 return this; 303 } 304 305 /** 306 * Sets the y-axis location of the event. 307 * 308 * @return this builder, to allow for chaining of calls 309 */ setY(float absY)310 public @NonNull Builder setY(float absY) { 311 mY = absY; 312 return this; 313 } 314 315 /** 316 * Sets the pressure of the event. This field is optional and can be omitted. 317 * 318 * @param pressure The pressure of the touch. 319 * Note: The VirtualTouchscreen, consuming VirtualTouchEvents, is 320 * configured with a pressure axis range from 0.0 to 255.0. Only the 321 * lower end of the range is enforced. You can pass values larger than 322 * 255.0. With physical input devices this could happen if the 323 * calibration is off. Values larger than 255.0 will not be trimmed and 324 * passed on as is. 325 * 326 * @throws IllegalArgumentException if the pressure is smaller than 0. 327 * 328 * @return this builder, to allow for chaining of calls 329 */ setPressure(@loatRangefrom = 0f) float pressure)330 public @NonNull Builder setPressure(@FloatRange(from = 0f) float pressure) { 331 if (pressure < 0f) { 332 throw new IllegalArgumentException("Touch event pressure cannot be negative"); 333 } 334 mPressure = pressure; 335 return this; 336 } 337 338 /** 339 * Sets the major axis size of the event. This field is optional and can be omitted. 340 * 341 * @return this builder, to allow for chaining of calls 342 */ setMajorAxisSize(@loatRangefrom = 0f) float majorAxisSize)343 public @NonNull Builder setMajorAxisSize(@FloatRange(from = 0f) float majorAxisSize) { 344 if (majorAxisSize < 0f) { 345 throw new IllegalArgumentException( 346 "Touch event major axis size cannot be negative"); 347 } 348 mMajorAxisSize = majorAxisSize; 349 return this; 350 } 351 352 /** 353 * Sets the time (in nanoseconds) when this specific event was generated. This may be 354 * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of 355 * millisecond), but can be different depending on the use case. 356 * This field is optional and can be omitted. 357 * 358 * @return this builder, to allow for chaining of calls 359 * @see InputEvent#getEventTime() 360 */ setEventTimeNanos(long eventTimeNanos)361 public @NonNull Builder setEventTimeNanos(long eventTimeNanos) { 362 if (eventTimeNanos < 0L) { 363 throw new IllegalArgumentException("Event time cannot be negative"); 364 } 365 mEventTimeNanos = eventTimeNanos; 366 return this; 367 } 368 } 369 370 public static final @NonNull Parcelable.Creator<VirtualTouchEvent> CREATOR = 371 new Parcelable.Creator<VirtualTouchEvent>() { 372 public VirtualTouchEvent createFromParcel(Parcel source) { 373 return new VirtualTouchEvent(source); 374 } 375 public VirtualTouchEvent[] newArray(int size) { 376 return new VirtualTouchEvent[size]; 377 } 378 }; 379 } 380