public enum Gesture {
  TAP,
  TAP_AND_HOLD,
  TWO_FINGER_TAP,
  SWIPE_FORWARD,
  TWO_FINGER_SWIPE_FORWARD,
  SWIPE_BACKWARD,
  TWO_FINGER_SWIPE_BACKWARD,
  SWIPE_UP,
  TWO_FINGER_SWIPE_UP,
  SWIPE_DOWN,
  TWO_FINGER_SWIPE_DOWN
} public interface OnGestureListener {

  /**
   * Should notify about detected gesture.
   *
   * @param gesture is a detected gesture.
   * @return TRUE if gesture is handled by the method. FALSE otherwise.
   */
  boolean onGesture(Gesture gesture);

  /**
   * Notifies when a scroll occurs with the initial on down {@link MotionEvent} and the current
   * move {@link MotionEvent}. The distance in x and y is also supplied for convenience.
   *
   * @param e1 The first down motion event that started the scrolling.
   * @param e2 The move motion event that triggered the current onScroll.
   * @param distanceX The distance along the X axis that has been scrolled since the last call to
   * onScroll. This is NOT the distance between {@code e1} and {@code e2}.
   * @param distanceY The distance along the Y axis that has been scrolled since the last call to
   * onScroll. This is NOT the distance between {@code e1} and {@code e2}.
   * @return true if the event is consumed, else false
   */
  default boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    return false;
  }

  /**
   * Notifies when touch is ended.
   */
  default void onTouchEnded() {
  }
} static final int DOWN_TIME = 10; static final int INITIAL_X = 200; static final int INITIAL_Y = 200; static final int META_STATE = 0; static final int SECOND_FINGER_INITIAL_X = INITIAL_X + SECOND_FINGER_SHIFT_PX; public static MotionEvent getActionCancel() {
  return MotionEvent
      .obtain(DOWN_TIME, MOVE_EVENT_TIME, MotionEvent.ACTION_CANCEL, INITIAL_X, INITIAL_Y,
          META_STATE);
} public static MotionEvent getActionDown() {
  return MotionEvent
      .obtain(DOWN_TIME, DOWN_EVENT_TIME, MotionEvent.ACTION_DOWN, INITIAL_X, INITIAL_Y,
          META_STATE);
} public static MotionEvent getActionMove(float xPosition, float yPosition) {
  return MotionEvent
      .obtain(DOWN_TIME, MOVE_EVENT_TIME, MotionEvent.ACTION_MOVE, xPosition, yPosition,
          META_STATE);
} public static MotionEvent getActionUp(float xPosition, float yPosition) {
  return MotionEvent
      .obtain(DOWN_TIME, UP_EVENT_TIME, MotionEvent.ACTION_UP, xPosition, yPosition, META_STATE);
} public static MotionEvent getSecondFingerActionDown() {
  final MotionEvent motionEvent = MotionEvent
      .obtain(DOWN_TIME, DOWN_EVENT_TIME, MotionEvent.ACTION_POINTER_DOWN,
          TWO_FINGER_POINTER_COUNT, getPointerProperties(),
          getPointerCoords(SECOND_FINGER_INITIAL_X, SECOND_FINGER_INITIAL_Y),
          META_STATE, 0, 0, 0, 0, 0, 0, 0);
  setMotionEventParameters(motionEvent, SECOND_FINGER_INITIAL_X, SECOND_FINGER_INITIAL_Y);
  return motionEvent;
} public static MotionEvent getSecondFingerActionMove(float xPosition, float yPosition) {
  final MotionEvent motionEvent = MotionEvent
      .obtain(DOWN_TIME, DOWN_EVENT_TIME, MotionEvent.ACTION_MOVE, TWO_FINGER_POINTER_COUNT,
          getPointerProperties(), getPointerCoords(xPosition, yPosition), META_STATE, 0, 0, 0, 0,
          0, 0, 0);
  setMotionEventParameters(motionEvent, xPosition, yPosition);
  return motionEvent;
} public static MotionEvent getSecondFingerActionUp(float xPosition, float yPosition) {
  final MotionEvent motionEvent = MotionEvent
      .obtain(DOWN_TIME, DOWN_EVENT_TIME, MotionEvent.ACTION_POINTER_UP, TWO_FINGER_POINTER_COUNT,
          getPointerProperties(), getPointerCoords(xPosition, yPosition), META_STATE, 0, 0, 0, 0,
          0, 0, 0);
  setMotionEventParameters(motionEvent, xPosition, yPosition);
  return motionEvent;
}
import android.content.Context;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.example.glass.ui.GlassGestureDetector.Gesture;
import com.example.glass.ui.GlassGestureDetector.OnGestureListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import static com.example.glass.ui.MotionEventGenerator.DOWN_TIME;
import static com.example.glass.ui.MotionEventGenerator.INITIAL_X;
import static com.example.glass.ui.MotionEventGenerator.INITIAL_Y;
import static com.example.glass.ui.MotionEventGenerator.META_STATE;
import static com.example.glass.ui.MotionEventGenerator.SECOND_FINGER_INITIAL_X;
import static com.example.glass.ui.MotionEventGenerator.SECOND_FINGER_INITIAL_Y;
import static com.example.glass.ui.MotionEventGenerator.getActionCancel;
import static com.example.glass.ui.MotionEventGenerator.getActionDown;
import static com.example.glass.ui.MotionEventGenerator.getActionMove;
import static com.example.glass.ui.MotionEventGenerator.getActionUp;
import static com.example.glass.ui.MotionEventGenerator.getSecondFingerActionDown;
import static com.example.glass.ui.MotionEventGenerator.getSecondFingerActionMove;
import static com.example.glass.ui.MotionEventGenerator.getSecondFingerActionUp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/*
 * Copyright 2019 Google LLC
 *
 * 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 com.example.glass.ui;





@RunWith(RobolectricTestRunner.class)
public class GlassGestureDetectorTest {

  private static final int EVENT_TIME = 10;
  private static final int ACTION = MotionEvent.ACTION_UP;
  private static final int TWICE_SWIPE_DISTANCE_THRESHOLD_PX =
      2 * GlassGestureDetector.SWIPE_DISTANCE_THRESHOLD_PX;
  private static final int HALF_SWIPE_DISTANCE_THRESHOLD_PX =
      GlassGestureDetector.SWIPE_DISTANCE_THRESHOLD_PX / 2;

  private static final float ANGLE_EPSILON_DEGREES = 0.1F;
  private static final float ANGLE_60_DEGREES = 60;
  private static final float ANGLE_120_DEGREES = 120;
  private static final double DELTA = 1e-15;

  private GlassGestureDetector glassGestureDetector;
  private MotionEvent motionEvent;
  private Gesture detectedGesture;
  private boolean isTouchEnded;
  private boolean isScrolling;
  private float scrollingDistanceX;
  private float scrollingDistanceY;
  private int touchSlop;

  @Before
  public void setUp() {
    final Context context = RuntimeEnvironment.application;
    glassGestureDetector = new GlassGestureDetector(context, new GestureListener());
    motionEvent = MotionEvent
        .obtain(DOWN_TIME, EVENT_TIME, ACTION, INITIAL_X, INITIAL_Y, META_STATE);
    isTouchEnded = false;
    isScrolling = false;
    scrollingDistanceX = 0;
    scrollingDistanceY = 0;
    touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  }

  @Test
  public void testOnTouchEvent() {
    assertFalse(glassGestureDetector.onTouchEvent(motionEvent));
  }

  @Test
  public void testScrolling() {
    assertFalse(isScrolling);
    glassGestureDetector.onTouchEvent(getActionDown());
    assertFalse(isScrolling);
    glassGestureDetector.onTouchEvent(
        getActionMove(INITIAL_X, INITIAL_Y - TWICE_SWIPE_DISTANCE_THRESHOLD_PX));
    assertTrue(isScrolling);
    assertEquals(0, scrollingDistanceX, DELTA);
    assertEquals(TWICE_SWIPE_DISTANCE_THRESHOLD_PX, scrollingDistanceY, DELTA);
    glassGestureDetector.onTouchEvent(
        getActionUp(INITIAL_X, INITIAL_Y - TWICE_SWIPE_DISTANCE_THRESHOLD_PX));
    assertTrue(isScrolling);
    assertEquals(0, scrollingDistanceX, DELTA);
    assertEquals(TWICE_SWIPE_DISTANCE_THRESHOLD_PX, scrollingDistanceY, DELTA);
    assertEquals(Gesture.SWIPE_UP, detectedGesture);
  }

  @Test
  public void testScrollingSmallDistance() {
    assertFalse(isScrolling);
    glassGestureDetector.onTouchEvent(getActionDown());
    assertFalse(isScrolling);
    glassGestureDetector.onTouchEvent(
        getActionMove(INITIAL_X, INITIAL_Y - HALF_SWIPE_DISTANCE_THRESHOLD_PX));
    assertTrue(isScrolling);
    assertEquals(0, scrollingDistanceX, DELTA);
    assertEquals(HALF_SWIPE_DISTANCE_THRESHOLD_PX, scrollingDistanceY, DELTA);
    glassGestureDetector.onTouchEvent(
        getActionUp(INITIAL_X, INITIAL_Y - HALF_SWIPE_DISTANCE_THRESHOLD_PX));
    assertTrue(isScrolling);
    assertEquals(0, scrollingDistanceX, DELTA);
    assertEquals(HALF_SWIPE_DISTANCE_THRESHOLD_PX, scrollingDistanceY, DELTA);
    assertNull(detectedGesture);
  }

  @Test
  public void testSwipeTooSmallDistance() {
    assertNull(detectedGesture);
    assertFalse(glassGestureDetector.onTouchEvent(getActionDown()));
    assertNull(detectedGesture);
    assertFalse(glassGestureDetector.onTouchEvent(
        getActionMove(INITIAL_X + HALF_SWIPE_DISTANCE_THRESHOLD_PX,
            INITIAL_Y + HALF_SWIPE_DISTANCE_THRESHOLD_PX)));
    assertNull(detectedGesture);
    assertFalse(glassGestureDetector.onTouchEvent(
        getActionUp(INITIAL_X + HALF_SWIPE_DISTANCE_THRESHOLD_PX,
            INITIAL_Y + HALF_SWIPE_DISTANCE_THRESHOLD_PX)));
    assertNull(detectedGesture);
  }

  @Test
  public void testTapGesture() {
    assertNull(detectedGesture);
    assertFalse(glassGestureDetector.onTouchEvent(getActionDown()));
    assertNull(detectedGesture);
    assertFalse(glassGestureDetector.onTouchEvent(getActionMove(INITIAL_X, INITIAL_Y)));
    assertNull(detectedGesture);
    assertTrue(glassGestureDetector.onTouchEvent(getActionUp(INITIAL_X, INITIAL_Y)));
    assertEquals(Gesture.TAP, detectedGesture);
  }

  @Test
  public void testTwoFingerTapGesture() {
    assertNull(detectedGesture);
    assertFalse(glassGestureDetector.onTouchEvent(getActionDown()));
    assertFalse(glassGestureDetector.onTouchEvent(getSecondFingerActionDown()));
    assertNull(detectedGesture);
    assertFalse(glassGestureDetector.onTouchEvent(getActionMove(INITIAL_X, INITIAL_Y)));
    assertFalse(glassGestureDetector
        .onTouchEvent(getSecondFingerActionMove(SECOND_FINGER_INITIAL_X, SECOND_FINGER_INITIAL_Y)));