content: /*
   * Copyright (C) 2018 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 androidx.test.core.view;
  
  import android.os.SystemClock;
  import android.view.MotionEvent;
  import android.view.MotionEvent.PointerCoords;
  import android.view.MotionEvent.PointerProperties;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * A helper builder for creating {@link MotionEvent}'s.
   *
   * <p>Default values for unspecified attributes are 0 unless otherwise noted.
   */
  public class MotionEventBuilder {
  
    private long downTime = 0;
    private long eventTime = SystemClock.uptimeMillis();
    private int action = MotionEvent.ACTION_DOWN;
    private int actionIndex = -1;
    private List<PointerProperties> pointerPropertiesList = new ArrayList<>();
    private List<PointerCoords> pointerCoordsList = new ArrayList<>();
    private int metaState = 0;
    private int buttonState = 0;
    private float xPrecision = 0f;
    private float yPrecision = 0f;
    private int deviceId = 0;
    private int edgeFlags = 0;
    private int source = 0;
    private int flags = 0;
  
    private MotionEventBuilder() {}
  
    /**
     * Start building a new MotionEvent.
     *
     * @return a new MotionEventBuilder.
     */
    public static MotionEventBuilder newBuilder() {
      return new MotionEventBuilder();
    }
  
    /**
     * Sets the down time.
     *
     * @see MotionEvent#getDownTime()
     */
    public MotionEventBuilder setDownTime(long downTime) {
      this.downTime = downTime;
      return this;
    }
  
    /**
     * Sets the event time. Default is SystemClock.uptimeMillis().
     *
     * @see MotionEvent#getEventTime()
     */
    public MotionEventBuilder setEventTime(long eventTime) {
      this.eventTime = eventTime;
      return this;
    }
  
    /**
     * Sets the action. Default is {@link MotionEvent.ACTION_DOWN}.
     *
     * @see MotionEvent#getAction()
     */
    public MotionEventBuilder setAction(int action) {
      this.action = action;
      return this;
    }
  
    /**
     * Sets the pointer index associated with the action.
     *
     * @see MotionEvent#getActionIndex()
     */
    public MotionEventBuilder setActionIndex(int pointerIndex) {
      checkState(pointerIndex <= 0xFF, "pointerIndex must be less than 0xff");
      this.actionIndex = pointerIndex;
      return this;
    }
  
    /**
     * Sets the metaState.
     *
     * @see MotionEvent#getMetaState()
     */
    public MotionEventBuilder setMetaState(int metastate) {
      this.metaState = metastate;
      return this;
    }
  
    /**
     * Sets the button state.
     *
     * @see MotionEvent#getButtonState()
     */
    public MotionEventBuilder setButtonState(int buttonState) {
      this.buttonState = buttonState;
      return this;
    }
  
    /**
     * Sets the x precision.
     *
     * @see MotionEvent#getXPrecision()
     */
    public MotionEventBuilder setXPrecision(float xPrecision) {
      this.xPrecision = xPrecision;
      return this;
    }
  
    /**
     * Sets the y precision.
     *
     * @see MotionEvent#getYPrecision()
     */
    public MotionEventBuilder setYPrecision(float yPrecision) {
      this.yPrecision = yPrecision;
      return this;
    }
  
    /**
     * Sets the device id.
     *
     * @see MotionEvent#getDeviceId()
     */
    public MotionEventBuilder setDeviceId(int deviceId) {
      this.deviceId = deviceId;
      return this;
    }
  
    /**
     * Sets the edge flags.
     *
     * @see MotionEvent#getEdgeFlags()
     */
    public MotionEventBuilder setEdgeFlags(int edgeFlags) {
      this.edgeFlags = edgeFlags;
      return this;
    }
  
    /**
     * Sets the source.
     *
     * @see MotionEvent#getSource()
     */
    public MotionEventBuilder setSource(int source) {
      this.source = source;
      return this;
    }
  
    /**
     * Sets the flags.
     *
     * @see MotionEvent#getFlags()
     */
    public MotionEventBuilder setFlags(int flags) {
      this.flags = flags;
      return this;
    }
  
    /**
     * Simple mechanism to add a pointer to the MotionEvent.
     *
     * <p>Can be called multiple times to add multiple pointers to the event.
     */
    public MotionEventBuilder setPointer(float x, float y) {
      PointerProperties pointerProperties = new PointerProperties();
      pointerProperties.id = pointerPropertiesList.size();
      PointerCoords pointerCoords = new PointerCoords();
      pointerCoords.x = x;
      pointerCoords.y = y;
      return setPointer(pointerProperties, pointerCoords);
    }
  
    /**
     * An expanded variant of {@link #setPointer(float, float)} that supports specifying all pointer
     * properties and coords data.
     */
    public MotionEventBuilder setPointer(
        PointerProperties pointerProperties, PointerCoords pointerCoords) {
      pointerPropertiesList.add(pointerProperties);
      pointerCoordsList.add(pointerCoords);
      return this;
    }
  
    /** Returns a MotionEvent with the provided data or reasonable defaults. */
    public MotionEvent build() {
      if (pointerPropertiesList.size() == 0) {
        setPointer(0, 0);
      }
      if (actionIndex != -1) {
        action = action | (actionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
      }
      return MotionEvent.obtain(
          downTime,
          eventTime,
          action,
          pointerPropertiesList.size(),
          pointerPropertiesList.toArray(new PointerProperties[pointerPropertiesList.size()]),
          pointerCoordsList.toArray(new MotionEvent.PointerCoords[pointerCoordsList.size()]),
          metaState,
          buttonState,
          xPrecision,
          yPrecision,
          deviceId,
          edgeFlags,
          source,
          flags);
    }
  
    private static void checkState(boolean expression, String message) {
      if (!expression) {
        throw new IllegalStateException(message);
      }
    }
  }
  