1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chromoting; 6 7 import android.content.Context; 8 import android.os.SystemClock; 9 import android.test.InstrumentationTestCase; 10 import android.test.suitebuilder.annotation.SmallTest; 11 import android.view.InputDevice; 12 import android.view.MotionEvent; 13 import android.view.ViewConfiguration; 14 15 import org.chromium.base.test.util.Feature; 16 17 /** Tests for {@link SwipePinchDetector}. */ 18 public class SwipePinchDetectorTest extends InstrumentationTestCase { 19 private SwipePinchDetector mDetector; 20 private float mTouchSlop; 21 private MotionEvent.PointerProperties[] mPointers; 22 23 // Stores the current finger positions, for convenience in writing tests. These values are 24 // initialized during setUp(). 25 private MotionEvent.PointerCoords[] mCurrentPositions; 26 27 @Override 28 public void setUp() { 29 Context context = getInstrumentation().getTargetContext(); 30 mDetector = new SwipePinchDetector(context); 31 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 32 MotionEvent.PointerProperties pointer0 = new MotionEvent.PointerProperties(); 33 pointer0.id = 0; 34 MotionEvent.PointerProperties pointer1 = new MotionEvent.PointerProperties(); 35 pointer1.id = 1; 36 mPointers = new MotionEvent.PointerProperties[] {pointer0, pointer1}; 37 38 MotionEvent.PointerCoords position0 = new MotionEvent.PointerCoords(); 39 MotionEvent.PointerCoords position1 = new MotionEvent.PointerCoords(); 40 mCurrentPositions = new MotionEvent.PointerCoords[] {position0, position1}; 41 42 // The starting points are arbitrary, but non-zero to ensure that the tests detect relative, 43 // not absolute, motion. 44 mCurrentPositions[0].x = 100; 45 mCurrentPositions[0].y = 200; 46 mCurrentPositions[1].x = 300; 47 mCurrentPositions[1].y = 400; 48 } 49 50 /** 51 * Simulates a 2-finger event. The action parameter should be MotionEvent.ACTION_POINTER_DOWN, 52 * MotionEvent.ACTION_MOVE or MotionEvent.ACTION_POINTER_UP. 53 */ 54 private void injectEvent(int action) { 55 final long eventTime = SystemClock.uptimeMillis(); 56 MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action, 2, mPointers, 57 mCurrentPositions, 0, 0, 1, 1, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); 58 mDetector.onTouchEvent(event); 59 } 60 61 /** Verifies that a simple swipe gesture is recognized as a swipe. */ 62 @SmallTest 63 @Feature({"Chromoting"}) 64 public void testSwipeRecognition() throws Exception { 65 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 66 assertFalse(mDetector.isSwiping()); 67 assertFalse(mDetector.isPinching()); 68 69 // Any distance greater than the touch-slop threshold should work. 70 mCurrentPositions[0].y += mTouchSlop * 2; 71 mCurrentPositions[1].y += mTouchSlop * 2; 72 injectEvent(MotionEvent.ACTION_MOVE); 73 assertTrue(mDetector.isSwiping()); 74 assertFalse(mDetector.isPinching()); 75 } 76 77 /** Verifies that a simple pinch gesture is recognized. */ 78 @SmallTest 79 @Feature({"Chromoting"}) 80 public void testPinchRecognition() throws Exception { 81 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 82 assertFalse(mDetector.isSwiping()); 83 assertFalse(mDetector.isPinching()); 84 85 // Any distance greater than the touch-slop threshold should work. 86 mCurrentPositions[0].x -= mTouchSlop * 2; 87 mCurrentPositions[1].x += mTouchSlop * 2; 88 injectEvent(MotionEvent.ACTION_MOVE); 89 assertFalse(mDetector.isSwiping()); 90 assertTrue(mDetector.isPinching()); 91 } 92 93 /** Verifies that motion less than touch-slop does not trigger anything. */ 94 @SmallTest 95 @Feature({"Chromoting"}) 96 public void testNoMotion() throws Exception { 97 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 98 mCurrentPositions[0].x += mTouchSlop / 2; 99 mCurrentPositions[0].y += mTouchSlop / 2; 100 mCurrentPositions[1].x -= mTouchSlop / 2; 101 mCurrentPositions[1].y -= mTouchSlop / 2; 102 injectEvent(MotionEvent.ACTION_MOVE); 103 assertFalse(mDetector.isSwiping()); 104 assertFalse(mDetector.isPinching()); 105 } 106 107 /** Verifies that a pinch with one finger stationary is detected. */ 108 @SmallTest 109 @Feature({"Chromoting"}) 110 public void testOneFingerStationary() throws Exception { 111 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 112 113 // The triggering threshold in this case (one finger stationary) is mTouchSlop * 2; 114 mCurrentPositions[1].x += mTouchSlop * 3; 115 injectEvent(MotionEvent.ACTION_MOVE); 116 assertFalse(mDetector.isSwiping()); 117 assertTrue(mDetector.isPinching()); 118 119 // Do the same test for the other finger. 120 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 121 mCurrentPositions[0].x += mTouchSlop * 3; 122 injectEvent(MotionEvent.ACTION_MOVE); 123 assertFalse(mDetector.isSwiping()); 124 assertTrue(mDetector.isPinching()); 125 } 126 127 /** 128 * Verifies that a pinch is recognized, when the fingers cross the motion threshold at 129 * different times. 130 */ 131 @SmallTest 132 @Feature({"Chromoting"}) 133 public void testUnevenPinch() throws Exception { 134 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 135 for (int i = 0; i < 50; i++) { 136 mCurrentPositions[0].x -= 2; 137 mCurrentPositions[1].x += 3; 138 injectEvent(MotionEvent.ACTION_MOVE); 139 } 140 141 assertFalse(mDetector.isSwiping()); 142 assertTrue(mDetector.isPinching()); 143 } 144 145 /** Same test as testUnevenPinch() except the slow/fast fingers are reversed. */ 146 @SmallTest 147 @Feature({"Chromoting"}) 148 public void testUnevenPinch2() throws Exception { 149 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 150 for (int i = 0; i < 50; i++) { 151 mCurrentPositions[0].x -= 3; 152 mCurrentPositions[1].x += 2; 153 injectEvent(MotionEvent.ACTION_MOVE); 154 } 155 156 assertFalse(mDetector.isSwiping()); 157 assertTrue(mDetector.isPinching()); 158 } 159 160 /** Verifies that a swipe is recognized, even if the fingers move at different rates. */ 161 @SmallTest 162 @Feature({"Chromoting"}) 163 public void testUnevenSwipe() throws Exception { 164 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 165 for (int i = 0; i < 50; i++) { 166 // The fingers need to move similarly enough so that one finger moves a distance of 167 // 2 * mTouchSlop after the other finger moves a distance of mTouchSlop. 168 // Otherwise the gesture would be mis-detected as a one-finger-stationary pinch. 169 mCurrentPositions[0].y += 2; 170 mCurrentPositions[1].y += 3; 171 injectEvent(MotionEvent.ACTION_MOVE); 172 } 173 174 assertTrue(mDetector.isSwiping()); 175 assertFalse(mDetector.isPinching()); 176 } 177 178 /** Same test as testUnevenSwipe() except the slow/fast fingers are reversed. */ 179 @SmallTest 180 @Feature({"Chromoting"}) 181 public void testUnevenSwipe2() throws Exception { 182 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 183 for (int i = 0; i < 50; i++) { 184 // The fingers need to move similarly enough so that one finger moves a distance of 185 // 2 * mTouchSlop after the other finger moves a distance of mTouchSlop. 186 // Otherwise the gesture would be mis-detected as a one-finger-stationary pinch. 187 mCurrentPositions[0].y += 3; 188 mCurrentPositions[1].y += 2; 189 injectEvent(MotionEvent.ACTION_MOVE); 190 } 191 192 assertTrue(mDetector.isSwiping()); 193 assertFalse(mDetector.isPinching()); 194 } 195 196 /** Verifies that the detector is reset when a gesture terminates or a new gesture begins. */ 197 @SmallTest 198 @Feature({"Chromoting"}) 199 public void testDetectorReset() throws Exception { 200 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 201 mCurrentPositions[0].x += mTouchSlop * 3; 202 injectEvent(MotionEvent.ACTION_MOVE); 203 assertTrue(mDetector.isPinching()); 204 205 // ACTION_POINTER_UP should terminate the gesture. 206 injectEvent(MotionEvent.ACTION_POINTER_UP); 207 assertFalse(mDetector.isPinching()); 208 209 // Repeat the same test, but use ACTION_POINTER_DOWN to start a new gesture, which should 210 // terminate the current one. 211 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 212 mCurrentPositions[0].x += mTouchSlop * 3; 213 injectEvent(MotionEvent.ACTION_MOVE); 214 assertTrue(mDetector.isPinching()); 215 injectEvent(MotionEvent.ACTION_POINTER_DOWN); 216 assertFalse(mDetector.isPinching()); 217 } 218 } 219