content: /*
   * Copyright (C) 2010 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.view;
  
  import android.annotation.UnsupportedAppUsage;
  import android.os.Parcel;
  import android.os.Parcelable;
  
  import java.util.concurrent.atomic.AtomicInteger;
  
  /**
   * Common base class for input events.
   */
  public abstract class InputEvent implements Parcelable {
      /** @hide */
      protected static final int PARCEL_TOKEN_MOTION_EVENT = 1;
      /** @hide */
      protected static final int PARCEL_TOKEN_KEY_EVENT = 2;
  
      // Next sequence number.
      private static final AtomicInteger mNextSeq = new AtomicInteger();
  
      /** @hide */
      protected int mSeq;
  
      /** @hide */
      protected boolean mRecycled;
  
      private static final boolean TRACK_RECYCLED_LOCATION = false;
      private RuntimeException mRecycledLocation;
  
      /*package*/ InputEvent() {
          mSeq = mNextSeq.getAndIncrement();
      }
  
      /**
       * Gets the id for the device that this event came from.  An id of
       * zero indicates that the event didn't come from a physical device
       * and maps to the default keymap.  The other numbers are arbitrary and
       * you shouldn't depend on the values.
       *
       * @return The device id.
       * @see InputDevice#getDevice
       */
      public abstract int getDeviceId();
  
      /**
       * Gets the device that this event came from.
       *
       * @return The device, or null if unknown.
       */
      public final InputDevice getDevice() {
          return InputDevice.getDevice(getDeviceId());
      }
  
      /**
       * Gets the source of the event.
       *
       * @return The event source or {@link InputDevice#SOURCE_UNKNOWN} if unknown.
       * @see InputDevice#getSources
       */
      public abstract int getSource();
  
      /**
       * Modifies the source of the event.
       *
       * @param source The new source.
       * @hide
       */
      public abstract void setSource(int source);
  
      /**
       * Determines whether the event is from the given source.
       *
       * @param source The input source to check against. This can be a specific device type, such as
       * {@link InputDevice#SOURCE_TOUCH_NAVIGATION}, or a more generic device class, such as
       * {@link InputDevice#SOURCE_CLASS_POINTER}.
       * @return Whether the event is from the given source.
       */
      public boolean isFromSource(int source) {
          return (getSource() & source) == source;
      }
  
      /**
       * Gets the display id of the event.
       * @return The display id associated with the event.
       * @hide
       */
      public abstract int getDisplayId();
  
      /**
       * Modifies the display id associated with the event
       * @param displayId
       * @hide
       */
      public abstract void setDisplayId(int displayId);
      /**
       * Copies the event.
       *
       * @return A deep copy of the event.
       * @hide
       */
      public abstract InputEvent copy();
  
      /**
       * Recycles the event.
       * This method should only be used by the system since applications do not
       * expect {@link KeyEvent} objects to be recycled, although {@link MotionEvent}
       * objects are fine.  See {@link KeyEvent#recycle()} for details.
       * @hide
       */
      public void recycle() {
          if (TRACK_RECYCLED_LOCATION) {
              if (mRecycledLocation != null) {
                  throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
              }
              mRecycledLocation = new RuntimeException("Last recycled here");
          } else {
              if (mRecycled) {
                  throw new RuntimeException(toString() + " recycled twice!");
              }
              mRecycled = true;
          }
      }
  
      /**
       * Conditionally recycled the event if it is appropriate to do so after
       * dispatching the event to an application.
       *
       * If the event is a {@link MotionEvent} then it is recycled.
       *
       * If the event is a {@link KeyEvent} then it is NOT recycled, because applications
       * expect key events to be immutable so once the event has been dispatched to
       * the application we can no longer recycle it.
       * @hide
       */
      public void recycleIfNeededAfterDispatch() {
          recycle();
      }
  
      /**
       * Reinitializes the event on reuse (after recycling).
       * @hide
       */
      protected void prepareForReuse() {
          mRecycled = false;
          mRecycledLocation = null;
          mSeq = mNextSeq.getAndIncrement();
      }
  
      /**
       * Gets a private flag that indicates when the system has detected that this input event
       * may be inconsistent with respect to the sequence of previously delivered input events,
       * such as when a key up event is sent but the key was not down or when a pointer
       * move event is sent but the pointer is not down.
       *
       * @return True if this event is tainted.
       * @hide
       */
      public abstract boolean isTainted();
  
      /**
       * Sets a private flag that indicates when the system has detected that this input event
       * may be inconsistent with respect to the sequence of previously delivered input events,
       * such as when a key up event is sent but the key was not down or when a pointer
       * move event is sent but the pointer is not down.
       *
       * @param tainted True if this event is tainted.
       * @hide
       */
      public abstract void setTainted(boolean tainted);
  
      /**
       * Retrieve the time this event occurred,
       * in the {@link android.os.SystemClock#uptimeMillis} time base.
       *
       * @return Returns the time this event occurred,
       * in the {@link android.os.SystemClock#uptimeMillis} time base.
       */
      public abstract long getEventTime();
  
      /**
       * Retrieve the time this event occurred,
       * in the {@link android.os.SystemClock#uptimeMillis} time base but with
       * nanosecond (instead of millisecond) precision.
       * <p>
       * The value is in nanosecond precision but it may not have nanosecond accuracy.
       * </p>
       *
       * @return Returns the time this event occurred,
       * in the {@link android.os.SystemClock#uptimeMillis} time base but with
       * nanosecond (instead of millisecond) precision.
       *
       * @hide
       */
      public abstract long getEventTimeNano();
  
      /**
       * Marks the input event as being canceled.
       *
       * @hide
       */
      public abstract void cancel();
  
      /**
       * Gets the unique sequence number of this event.
       * Every input event that is created or received by a process has a
       * unique sequence number.  Moreover, a new sequence number is obtained
       * each time an event object is recycled.
       *
       * Sequence numbers are only guaranteed to be locally unique within a process.
       * Sequence numbers are not preserved when events are parceled.
       *
       * @return The unique sequence number of this event.
       * @hide
       */
      @UnsupportedAppUsage
      public int getSequenceNumber() {
          return mSeq;
      }
  
      public int describeContents() {
          return 0;
      }
  
      public static final @android.annotation.NonNull Parcelable.Creator<InputEvent> CREATOR
              = new Parcelable.Creator<InputEvent>() {
          public InputEvent createFromParcel(Parcel in) {
              int token = in.readInt();
              if (token == PARCEL_TOKEN_KEY_EVENT) {
                  return KeyEvent.createFromParcelBody(in);
              } else if (token == PARCEL_TOKEN_MOTION_EVENT) {
                  return MotionEvent.createFromParcelBody(in);
              } else {
                  throw new IllegalStateException("Unexpected input event type token in parcel.");
              }
          }
  
          public InputEvent[] newArray(int size) {
              return new InputEvent[size];
          }
      };
  }
  