/**
   * Generates a random trackball event. This consists of a sequence of small moves, followed by an
   * optional single click.
   *
   * <p>TODO: Longpress. TODO: Meta state TODO: Parameterize the % clicked TODO: More useful than
   * the random walk here would be to pick a single random direction and distance, and divvy it up
   * into a random number of segments. (This would serve to generate fling gestures, which are
   * important).
   *
   * @param random Random number source for positioning
   */
  private void generateTrackballEvent(Random random) {
    Display display = WindowManagerImpl.getDefault().getDefaultDisplay();

    boolean drop = false;
    int count = random.nextInt(10);
    for (int i = 0; i < 10; ++i) {
      // generate a small random step
      int dX = random.nextInt(10) - 5;
      int dY = random.nextInt(10) - 5;

      mQ.addLast(
          new MonkeyTrackballEvent(MotionEvent.ACTION_MOVE)
              .addPointer(0, dX, dY)
              .setIntermediateNote(i > 0));
    }

    // 10% of trackball moves end with a click
    if (0 == random.nextInt(10)) {
      long downAt = SystemClock.uptimeMillis();

      mQ.addLast(
          new MonkeyTrackballEvent(MotionEvent.ACTION_DOWN)
              .setDownTime(downAt)
              .addPointer(0, 0, 0)
              .setIntermediateNote(true));

      mQ.addLast(
          new MonkeyTrackballEvent(MotionEvent.ACTION_UP)
              .setDownTime(downAt)
              .addPointer(0, 0, 0)
              .setIntermediateNote(false));
    }
  }
  /**
   * Generates a random trackball event. This consists of a sequence of small moves, followed by an
   * optional single click.
   *
   * <p>TODO: Longpress. TODO: Meta state TODO: Parameterize the % clicked TODO: More useful than
   * the random walk here would be to pick a single random direction and distance, and divvy it up
   * into a random number of segments. (This would serve to generate fling gestures, which are
   * important).
   *
   * @param random Random number source for positioning
   */
  private void generateTrackballEvent(Random random) {
    Display display = WindowManagerImpl.getDefault().getDefaultDisplay();

    boolean drop = false;
    int count = random.nextInt(10);
    MonkeyMotionEvent e;
    for (int i = 0; i < 10; ++i) {
      // generate a small random step
      int dX = random.nextInt(10) - 5;
      int dY = random.nextInt(10) - 5;

      e =
          new MonkeyMotionEvent(
              MonkeyEvent.EVENT_TYPE_TRACKBALL, -1, MotionEvent.ACTION_MOVE, dX, dY, 0);
      e.setIntermediateNote(i > 0);
      mQ.addLast(e);
    }

    // 10% of trackball moves end with a click
    if (0 == random.nextInt(10)) {
      long downAt = SystemClock.uptimeMillis();

      e =
          new MonkeyMotionEvent(
              MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt, MotionEvent.ACTION_DOWN, 0, 0, 0);
      e.setIntermediateNote(true);
      mQ.addLast(e);

      e =
          new MonkeyMotionEvent(
              MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt, MotionEvent.ACTION_UP, 0, 0, 0);
      e.setIntermediateNote(false);
      mQ.addLast(e);
    }
  }
  /** generate a random event based on mFactor */
  private void generateEvents() {
    float cls = mRandom.nextFloat();
    int lastKey = 0;

    if (cls < mFactors[FACTOR_TOUCH]) {
      generatePointerEvent(mRandom, GESTURE_TAP);
      return;
    } else if (cls < mFactors[FACTOR_MOTION]) {
      generatePointerEvent(mRandom, GESTURE_DRAG);
      return;
    } else if (cls < mFactors[FACTOR_PINCHZOOM]) {
      generatePointerEvent(mRandom, GESTURE_PINCH_OR_ZOOM);
      return;
    } else if (cls < mFactors[FACTOR_TRACKBALL]) {
      generateTrackballEvent(mRandom);
      return;
    } else if (cls < mFactors[FACTOR_ROTATION]) {
      generateRotationEvent(mRandom);
      return;
    }

    // The remaining event categories are injected as key events
    for (; ; ) {
      if (cls < mFactors[FACTOR_NAV]) {
        lastKey = NAV_KEYS[mRandom.nextInt(NAV_KEYS.length)];
      } else if (cls < mFactors[FACTOR_MAJORNAV]) {
        lastKey = MAJOR_NAV_KEYS[mRandom.nextInt(MAJOR_NAV_KEYS.length)];
      } else if (cls < mFactors[FACTOR_SYSOPS]) {
        lastKey = SYS_KEYS[mRandom.nextInt(SYS_KEYS.length)];
      } else if (cls < mFactors[FACTOR_APPSWITCH]) {
        MonkeyActivityEvent e =
            new MonkeyActivityEvent(mMainApps.get(mRandom.nextInt(mMainApps.size())));
        mQ.addLast(e);
        return;
      } else if (cls < mFactors[FACTOR_FLIP]) {
        MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen);
        mKeyboardOpen = !mKeyboardOpen;
        mQ.addLast(e);
        return;
      } else {
        lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
      }

      if (lastKey != KeyEvent.KEYCODE_POWER
          && lastKey != KeyEvent.KEYCODE_ENDCALL
          && PHYSICAL_KEY_EXISTS[lastKey]) {
        break;
      }
    }

    MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
    mQ.addLast(e);

    e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
    mQ.addLast(e);
  }
  /** generate a random event based on mFactor */
  private void generateEvents() {
    float cls = mRandom.nextFloat();
    int lastKey = 0;

    boolean touchEvent = cls < mFactors[FACTOR_TOUCH];
    boolean motionEvent = !touchEvent && (cls < mFactors[FACTOR_MOTION]);
    if (touchEvent || motionEvent) {
      generateMotionEvent(mRandom, motionEvent);
      return;
    }

    if (cls < mFactors[FACTOR_TRACKBALL]) {
      generateTrackballEvent(mRandom);
      return;
    }

    // The remaining event categories are injected as key events
    do {
      if (cls < mFactors[FACTOR_NAV]) {
        lastKey = NAV_KEYS[mRandom.nextInt(NAV_KEYS.length)];
      } else if (cls < mFactors[FACTOR_MAJORNAV]) {
        lastKey = MAJOR_NAV_KEYS[mRandom.nextInt(MAJOR_NAV_KEYS.length)];
      } else if (cls < mFactors[FACTOR_SYSOPS]) {
        lastKey = SYS_KEYS[mRandom.nextInt(SYS_KEYS.length)];
      } else if (cls < mFactors[FACTOR_APPSWITCH]) {
        MonkeyActivityEvent e =
            new MonkeyActivityEvent(mMainApps.get(mRandom.nextInt(mMainApps.size())));
        mQ.addLast(e);
        return;
      } else if (cls < mFactors[FACTOR_FLIP]) {
        MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen);
        mKeyboardOpen = !mKeyboardOpen;
        mQ.addLast(e);
        return;
      } else {
        lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
      }
    } while (!PHYSICAL_KEY_EXISTS[lastKey]);

    MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
    mQ.addLast(e);

    e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
    mQ.addLast(e);
  }
  /**
   * Generates a random motion event. This method counts a down, move, and up as multiple events.
   *
   * <p>TODO: Test & fix the selectors when non-zero percentages TODO: Longpress. TODO: Fling. TODO:
   * Meta state TODO: More useful than the random walk here would be to pick a single random
   * direction and distance, and divvy it up into a random number of segments. (This would serve to
   * generate fling gestures, which are important).
   *
   * @param random Random number source for positioning
   * @param motionEvent If false, touch/release. If true, touch/move/release.
   */
  private void generateMotionEvent(Random random, boolean motionEvent) {

    Display display = WindowManagerImpl.getDefault().getDefaultDisplay();

    float x = Math.abs(random.nextInt() % display.getWidth());
    float y = Math.abs(random.nextInt() % display.getHeight());
    long downAt = SystemClock.uptimeMillis();
    long eventTime = SystemClock.uptimeMillis();
    if (downAt == -1) {
      downAt = eventTime;
    }

    MonkeyMotionEvent e =
        new MonkeyMotionEvent(
            MonkeyEvent.EVENT_TYPE_POINTER, downAt, MotionEvent.ACTION_DOWN, x, y, 0);
    e.setIntermediateNote(false);
    mQ.addLast(e);

    // sometimes we'll move during the touch
    if (motionEvent) {
      int count = random.nextInt(10);
      for (int i = 0; i < count; i++) {
        // generate some slop in the up event
        x = (x + (random.nextInt() % 10)) % display.getWidth();
        y = (y + (random.nextInt() % 10)) % display.getHeight();

        e =
            new MonkeyMotionEvent(
                MonkeyEvent.EVENT_TYPE_POINTER, downAt, MotionEvent.ACTION_MOVE, x, y, 0);
        e.setIntermediateNote(true);
        mQ.addLast(e);
      }
    }

    // TODO generate some slop in the up event
    e =
        new MonkeyMotionEvent(
            MonkeyEvent.EVENT_TYPE_POINTER, downAt, MotionEvent.ACTION_UP, x, y, 0);
    e.setIntermediateNote(false);
    mQ.addLast(e);
  }
 /** generate an activity event */
 public void generateActivity() {
   MonkeyActivityEvent e =
       new MonkeyActivityEvent(mMainApps.get(mRandom.nextInt(mMainApps.size())));
   mQ.addLast(e);
 }
 /**
  * Generates a random screen rotation event.
  *
  * @param random Random number source for rotation degree.
  */
 private void generateRotationEvent(Random random) {
   mQ.addLast(
       new MonkeyRotationEvent(
           SCREEN_ROTATION_DEGREES[random.nextInt(SCREEN_ROTATION_DEGREES.length)],
           random.nextBoolean()));
 }
  /**
   * Generates a random motion event. This method counts a down, move, and up as multiple events.
   *
   * <p>TODO: Test & fix the selectors when non-zero percentages TODO: Longpress. TODO: Fling. TODO:
   * Meta state TODO: More useful than the random walk here would be to pick a single random
   * direction and distance, and divvy it up into a random number of segments. (This would serve to
   * generate fling gestures, which are important).
   *
   * @param random Random number source for positioning
   * @param gesture The gesture to perform.
   */
  private void generatePointerEvent(Random random, int gesture) {
    Display display = WindowManagerImpl.getDefault().getDefaultDisplay();

    PointF p1 = randomPoint(random, display);
    PointF v1 = randomVector(random);

    long downAt = SystemClock.uptimeMillis();

    mQ.addLast(
        new MonkeyTouchEvent(MotionEvent.ACTION_DOWN)
            .setDownTime(downAt)
            .addPointer(0, p1.x, p1.y)
            .setIntermediateNote(false));

    // sometimes we'll move during the touch
    if (gesture == GESTURE_DRAG) {
      int count = random.nextInt(10);
      for (int i = 0; i < count; i++) {
        randomWalk(random, display, p1, v1);

        mQ.addLast(
            new MonkeyTouchEvent(MotionEvent.ACTION_MOVE)
                .setDownTime(downAt)
                .addPointer(0, p1.x, p1.y)
                .setIntermediateNote(true));
      }
    } else if (gesture == GESTURE_PINCH_OR_ZOOM) {
      PointF p2 = randomPoint(random, display);
      PointF v2 = randomVector(random);

      randomWalk(random, display, p1, v1);
      mQ.addLast(
          new MonkeyTouchEvent(
                  MotionEvent.ACTION_POINTER_DOWN | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT))
              .setDownTime(downAt)
              .addPointer(0, p1.x, p1.y)
              .addPointer(1, p2.x, p2.y)
              .setIntermediateNote(true));

      int count = random.nextInt(10);
      for (int i = 0; i < count; i++) {
        randomWalk(random, display, p1, v1);
        randomWalk(random, display, p2, v2);

        mQ.addLast(
            new MonkeyTouchEvent(MotionEvent.ACTION_MOVE)
                .setDownTime(downAt)
                .addPointer(0, p1.x, p1.y)
                .addPointer(1, p2.x, p2.y)
                .setIntermediateNote(true));
      }

      randomWalk(random, display, p1, v1);
      randomWalk(random, display, p2, v2);
      mQ.addLast(
          new MonkeyTouchEvent(
                  MotionEvent.ACTION_POINTER_UP | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT))
              .setDownTime(downAt)
              .addPointer(0, p1.x, p1.y)
              .addPointer(1, p2.x, p2.y)
              .setIntermediateNote(true));
    }

    randomWalk(random, display, p1, v1);
    mQ.addLast(
        new MonkeyTouchEvent(MotionEvent.ACTION_UP)
            .setDownTime(downAt)
            .addPointer(0, p1.x, p1.y)
            .setIntermediateNote(false));
  }