| /* |
| * 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. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.hardware.input; |
| |
| import android.annotation.FloatRange; |
| import android.annotation.IntDef; |
| import android.annotation.IntRange; |
| import android.annotation.NonNull; |
| import android.annotation.SystemApi; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.os.SystemClock; |
| import android.view.InputEvent; |
| import android.view.MotionEvent; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| |
| /** |
| * An event describing a touchscreen interaction originating from a remote device. |
| * |
| * The pointer id, tool type, action, and location are required; pressure and main axis size are |
| * optional. |
| * |
| * Note: A VirtualTouchEvent with ACTION_CANCEL can only be created with TOOL_TYPE_PALM (and vice |
| * versa). Events are injected into the uinput kernel module, which has no concept of cancelling |
| * an action. The only way to state the intention that a pointer should not be handled as a pointer |
| * is to change its tool type to TOOL_TYPE_PALM. |
| * |
| * @hide |
| */ |
| @SystemApi |
| public final class VirtualTouchEvent implements Parcelable { |
| |
| /** @hide */ |
| public static final int TOOL_TYPE_UNKNOWN = MotionEvent.TOOL_TYPE_UNKNOWN; |
| /** Tool type indicating that the user's finger is the origin of the event. */ |
| public static final int TOOL_TYPE_FINGER = MotionEvent.TOOL_TYPE_FINGER; |
| /** |
| * Tool type indicating that a user's palm (or other input mechanism to be rejected) is the |
| * origin of the event. |
| */ |
| public static final int TOOL_TYPE_PALM = MotionEvent.TOOL_TYPE_PALM; |
| /** @hide */ |
| @IntDef(prefix = { "TOOL_TYPE_" }, value = { |
| TOOL_TYPE_UNKNOWN, |
| TOOL_TYPE_FINGER, |
| TOOL_TYPE_PALM, |
| }) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface ToolType {} |
| |
| /** @hide */ |
| public static final int ACTION_UNKNOWN = -1; |
| /** Action indicating the tool has been pressed down to the touchscreen. */ |
| public static final int ACTION_DOWN = MotionEvent.ACTION_DOWN; |
| /** Action indicating the tool has been lifted from the touchscreen. */ |
| public static final int ACTION_UP = MotionEvent.ACTION_UP; |
| /** Action indicating the tool has been moved along the face of the touchscreen. */ |
| public static final int ACTION_MOVE = MotionEvent.ACTION_MOVE; |
| /** Action indicating the tool cancelled the current movement. */ |
| public static final int ACTION_CANCEL = MotionEvent.ACTION_CANCEL; |
| /** @hide */ |
| @IntDef(prefix = { "ACTION_" }, value = { |
| ACTION_UNKNOWN, |
| ACTION_DOWN, |
| ACTION_UP, |
| ACTION_MOVE, |
| ACTION_CANCEL, |
| }) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface Action {} |
| |
| // The maximum number of pointers that can be touching the screen at once. (See MAX_POINTERS |
| // in frameworks/native/include/input/Input.h) |
| private static final int MAX_POINTERS = 16; |
| |
| private final int mPointerId; |
| private final @ToolType int mToolType; |
| private final @Action int mAction; |
| private final float mX; |
| private final float mY; |
| private final float mPressure; |
| private final float mMajorAxisSize; |
| private final long mEventTimeNanos; |
| |
| private VirtualTouchEvent(int pointerId, @ToolType int toolType, @Action int action, |
| float x, float y, float pressure, float majorAxisSize, long eventTimeNanos) { |
| mPointerId = pointerId; |
| mToolType = toolType; |
| mAction = action; |
| mX = x; |
| mY = y; |
| mPressure = pressure; |
| mMajorAxisSize = majorAxisSize; |
| mEventTimeNanos = eventTimeNanos; |
| } |
| |
| private VirtualTouchEvent(@NonNull Parcel parcel) { |
| mPointerId = parcel.readInt(); |
| mToolType = parcel.readInt(); |
| mAction = parcel.readInt(); |
| mX = parcel.readFloat(); |
| mY = parcel.readFloat(); |
| mPressure = parcel.readFloat(); |
| mMajorAxisSize = parcel.readFloat(); |
| mEventTimeNanos = parcel.readLong(); |
| } |
| |
| @Override |
| public void writeToParcel(@NonNull Parcel dest, int flags) { |
| dest.writeInt(mPointerId); |
| dest.writeInt(mToolType); |
| dest.writeInt(mAction); |
| dest.writeFloat(mX); |
| dest.writeFloat(mY); |
| dest.writeFloat(mPressure); |
| dest.writeFloat(mMajorAxisSize); |
| dest.writeLong(mEventTimeNanos); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** |
| * Returns the pointer id associated with this event. |
| */ |
| public int getPointerId() { |
| return mPointerId; |
| } |
| |
| /** |
| * Returns the tool type associated with this event. |
| */ |
| public @ToolType int getToolType() { |
| return mToolType; |
| } |
| |
| /** |
| * Returns the action associated with this event. |
| */ |
| public @Action int getAction() { |
| return mAction; |
| } |
| |
| /** |
| * Returns the x-axis location associated with this event. |
| */ |
| public float getX() { |
| return mX; |
| } |
| |
| /** |
| * Returns the y-axis location associated with this event. |
| */ |
| public float getY() { |
| return mY; |
| } |
| |
| /** |
| * Returns the pressure associated with this event. Returns {@link Float#NaN} if omitted. |
| */ |
| public float getPressure() { |
| return mPressure; |
| } |
| |
| /** |
| * Returns the major axis size associated with this event. Returns {@link Float#NaN} if omitted. |
| */ |
| public float getMajorAxisSize() { |
| return mMajorAxisSize; |
| } |
| |
| /** |
| * Returns the time this event occurred, in the {@link SystemClock#uptimeMillis()} time base but |
| * with nanosecond (instead of millisecond) precision. |
| * |
| * @see InputEvent#getEventTime() |
| */ |
| public long getEventTimeNanos() { |
| return mEventTimeNanos; |
| } |
| |
| /** |
| * Builder for {@link VirtualTouchEvent}. |
| */ |
| public static final class Builder { |
| |
| private @ToolType int mToolType = TOOL_TYPE_UNKNOWN; |
| private int mPointerId = MotionEvent.INVALID_POINTER_ID; |
| private @Action int mAction = ACTION_UNKNOWN; |
| private float mX = Float.NaN; |
| private float mY = Float.NaN; |
| private float mPressure = Float.NaN; |
| private float mMajorAxisSize = Float.NaN; |
| private long mEventTimeNanos = 0L; |
| |
| /** |
| * Creates a {@link VirtualTouchEvent} object with the current builder configuration. |
| * |
| * @throws IllegalArgumentException if one of the required arguments is missing or if |
| * ACTION_CANCEL is not set in combination with TOOL_TYPE_PALM. See |
| * {@link VirtualTouchEvent} for a detailed explanation. |
| */ |
| public @NonNull VirtualTouchEvent build() { |
| if (mToolType == TOOL_TYPE_UNKNOWN || mPointerId == MotionEvent.INVALID_POINTER_ID |
| || mAction == ACTION_UNKNOWN || Float.isNaN(mX) || Float.isNaN(mY)) { |
| throw new IllegalArgumentException( |
| "Cannot build virtual touch event with unset required fields"); |
| } |
| if ((mToolType == TOOL_TYPE_PALM && mAction != ACTION_CANCEL) |
| || (mAction == ACTION_CANCEL && mToolType != TOOL_TYPE_PALM)) { |
| throw new IllegalArgumentException( |
| "ACTION_CANCEL and TOOL_TYPE_PALM must always appear together"); |
| } |
| return new VirtualTouchEvent(mPointerId, mToolType, mAction, mX, mY, mPressure, |
| mMajorAxisSize, mEventTimeNanos); |
| } |
| |
| /** |
| * Sets the pointer id of the event. |
| * |
| * <p>A Valid pointer id need to be in the range of 0 to 15. |
| * |
| * @return this builder, to allow for chaining of calls |
| */ |
| public @NonNull Builder setPointerId( |
| @IntRange(from = 0, to = MAX_POINTERS - 1) int pointerId) { |
| if (pointerId < 0 || pointerId > 15) { |
| throw new IllegalArgumentException( |
| "The pointer id must be in the range 0 - " + (MAX_POINTERS - 1) |
| + "inclusive, but was: " + pointerId); |
| } |
| mPointerId = pointerId; |
| return this; |
| } |
| |
| /** |
| * Sets the tool type of the event. |
| * |
| * @return this builder, to allow for chaining of calls |
| */ |
| public @NonNull Builder setToolType(@ToolType int toolType) { |
| if (toolType != TOOL_TYPE_FINGER && toolType != TOOL_TYPE_PALM) { |
| throw new IllegalArgumentException("Unsupported touch event tool type"); |
| } |
| mToolType = toolType; |
| return this; |
| } |
| |
| /** |
| * Sets the action of the event. |
| * |
| * @return this builder, to allow for chaining of calls |
| */ |
| public @NonNull Builder setAction(@Action int action) { |
| if (action != ACTION_DOWN && action != ACTION_UP && action != ACTION_MOVE |
| && action != ACTION_CANCEL) { |
| throw new IllegalArgumentException("Unsupported touch event action type"); |
| } |
| mAction = action; |
| return this; |
| } |
| |
| /** |
| * Sets the x-axis location of the event. |
| * |
| * @return this builder, to allow for chaining of calls |
| */ |
| public @NonNull Builder setX(float absX) { |
| mX = absX; |
| return this; |
| } |
| |
| /** |
| * Sets the y-axis location of the event. |
| * |
| * @return this builder, to allow for chaining of calls |
| */ |
| public @NonNull Builder setY(float absY) { |
| mY = absY; |
| return this; |
| } |
| |
| /** |
| * Sets the pressure of the event. This field is optional and can be omitted. |
| * |
| * @param pressure The pressure of the touch. |
| * Note: The VirtualTouchscreen, consuming VirtualTouchEvents, is |
| * configured with a pressure axis range from 0.0 to 255.0. Only the |
| * lower end of the range is enforced. You can pass values larger than |
| * 255.0. With physical input devices this could happen if the |
| * calibration is off. Values larger than 255.0 will not be trimmed and |
| * passed on as is. |
| * |
| * @throws IllegalArgumentException if the pressure is smaller than 0. |
| * |
| * @return this builder, to allow for chaining of calls |
| */ |
| public @NonNull Builder setPressure(@FloatRange(from = 0f) float pressure) { |
| if (pressure < 0f) { |
| throw new IllegalArgumentException("Touch event pressure cannot be negative"); |
| } |
| mPressure = pressure; |
| return this; |
| } |
| |
| /** |
| * Sets the major axis size of the event. This field is optional and can be omitted. |
| * |
| * @return this builder, to allow for chaining of calls |
| */ |
| public @NonNull Builder setMajorAxisSize(@FloatRange(from = 0f) float majorAxisSize) { |
| if (majorAxisSize < 0f) { |
| throw new IllegalArgumentException( |
| "Touch event major axis size cannot be negative"); |
| } |
| mMajorAxisSize = majorAxisSize; |
| return this; |
| } |
| |
| /** |
| * Sets the time (in nanoseconds) when this specific event was generated. This may be |
| * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of |
| * millisecond), but can be different depending on the use case. |
| * This field is optional and can be omitted. |
| * |
| * @return this builder, to allow for chaining of calls |
| * @see InputEvent#getEventTime() |
| */ |
| public @NonNull Builder setEventTimeNanos(long eventTimeNanos) { |
| if (eventTimeNanos < 0L) { |
| throw new IllegalArgumentException("Event time cannot be negative"); |
| } |
| mEventTimeNanos = eventTimeNanos; |
| return this; |
| } |
| } |
| |
| public static final @NonNull Parcelable.Creator<VirtualTouchEvent> CREATOR = |
| new Parcelable.Creator<VirtualTouchEvent>() { |
| public VirtualTouchEvent createFromParcel(Parcel source) { |
| return new VirtualTouchEvent(source); |
| } |
| public VirtualTouchEvent[] newArray(int size) { |
| return new VirtualTouchEvent[size]; |
| } |
| }; |
| } |