GestureDescriptionTest.java revision 2f165944ce6109134e7285a71da32d1a1647960b
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.accessibility; 18 19import static android.accessibilityservice.GestureDescription.StrokeDescription.INVALID_STROKE_ID; 20import static org.hamcrest.CoreMatchers.allOf; 21import static org.hamcrest.CoreMatchers.everyItem; 22import static org.hamcrest.MatcherAssert.assertThat; 23 24import android.accessibilityservice.GestureDescription; 25import android.accessibilityservice.GestureDescription.GestureStep; 26import android.accessibilityservice.GestureDescription.MotionEventGenerator; 27import android.accessibilityservice.GestureDescription.StrokeDescription; 28import android.graphics.Path; 29import android.graphics.PointF; 30import org.hamcrest.Description; 31import org.hamcrest.Matcher; 32import org.hamcrest.TypeSafeMatcher; 33import org.junit.Test; 34 35import java.util.List; 36 37import static junit.framework.TestCase.assertEquals; 38 39/** 40 * Tests for GestureDescription 41 */ 42public class GestureDescriptionTest { 43 @Test 44 public void testGestureShorterThanSampleRate_producesStartAndEnd() { 45 PointF click = new PointF(10, 20); 46 Path clickPath = new Path(); 47 clickPath.moveTo(click.x, click.y); 48 StrokeDescription clickStroke = new StrokeDescription(clickPath, 0, 10); 49 GestureDescription.Builder clickBuilder = new GestureDescription.Builder(); 50 clickBuilder.addStroke(clickStroke); 51 GestureDescription clickGesture = clickBuilder.build(); 52 53 List<GestureStep> clickGestureSteps = MotionEventGenerator 54 .getGestureStepsFromGestureDescription(clickGesture, 100); 55 56 assertEquals(2, clickGestureSteps.size()); 57 assertThat(clickGestureSteps.get(0), allOf(numTouchPointsIs(1), numStartsOfStroke(1), 58 numEndsOfStroke(0), hasPoint(click))); 59 assertThat(clickGestureSteps.get(1), allOf(numTouchPointsIs(1), numStartsOfStroke(0), 60 numEndsOfStroke(1), hasPoint(click))); 61 } 62 63 @Test 64 public void testSwipe_shouldContainEvenlySpacedPoints() { 65 int samplePeriod = 10; 66 int numSamples = 5; 67 float stepX = 2; 68 float stepY = 3; 69 PointF start = new PointF(10, 20); 70 PointF end = new PointF(10 + numSamples * stepX, 20 + numSamples * stepY); 71 72 GestureDescription swipe = 73 createSwipe(start.x, start.y, end.x, end.y, numSamples * samplePeriod); 74 List<GestureStep> swipeGestureSteps = MotionEventGenerator 75 .getGestureStepsFromGestureDescription(swipe, samplePeriod); 76 assertEquals(numSamples + 1, swipeGestureSteps.size()); 77 78 assertThat(swipeGestureSteps.get(0), allOf(numTouchPointsIs(1), numStartsOfStroke(1), 79 numEndsOfStroke(0), hasPoint(start))); 80 assertThat(swipeGestureSteps.get(numSamples), allOf(numTouchPointsIs(1), 81 numStartsOfStroke(0), numEndsOfStroke(1), hasPoint(end))); 82 83 for (int i = 1; i < numSamples; ++i) { 84 PointF interpPoint = new PointF(start.x + stepX * i, start.y + stepY * i); 85 assertThat(swipeGestureSteps.get(i), allOf(numTouchPointsIs(1), 86 numStartsOfStroke(0), numEndsOfStroke(0), hasPoint(interpPoint))); 87 } 88 } 89 90 @Test 91 public void testSwipeWithNonIntegerValues_shouldRound() { 92 int strokeTime = 10; 93 94 GestureDescription swipe = createSwipe(10.1f, 20.6f, 11.9f, 22.1f, strokeTime); 95 List<GestureStep> swipeGestureSteps = MotionEventGenerator 96 .getGestureStepsFromGestureDescription(swipe, strokeTime); 97 assertEquals(2, swipeGestureSteps.size()); 98 assertThat(swipeGestureSteps.get(0), hasPoint(new PointF(10, 21))); 99 assertThat(swipeGestureSteps.get(1), hasPoint(new PointF(12, 22))); 100 } 101 102 @Test 103 public void testPathsWithOverlappingTiming_produceCorrectSteps() { 104 // There are 4 paths 105 // 0: an L-shaped path that starts first 106 // 1: a swipe that starts in the middle of the L-shaped path and ends when the L ends 107 // 2: a swipe that starts at the same time as #1 but extends past the end of the L 108 // 3: a swipe that starts when #3 ends 109 PointF path0Start = new PointF(100, 150); 110 PointF path0Turn = new PointF(100, 200); 111 PointF path0End = new PointF(250, 200); 112 int path0StartTime = 0; 113 int path0EndTime = 100; 114 int path0Duration = path0EndTime - path0StartTime; 115 Path path0 = new Path(); 116 path0.moveTo(path0Start.x, path0Start.y); 117 path0.lineTo(path0Turn.x, path0Turn.y); 118 path0.lineTo(path0End.x, path0End.y); 119 StrokeDescription path0Stroke = new StrokeDescription(path0, path0StartTime, path0Duration); 120 121 PointF path1Start = new PointF(300, 350); 122 PointF path1End = new PointF(300, 400); 123 int path1StartTime = 50; 124 int path1EndTime = path0EndTime; 125 StrokeDescription path1Stroke = createSwipeStroke( 126 path1Start.x, path1Start.y, path1End.x, path1End.y, path1StartTime, path1EndTime); 127 128 PointF path2Start = new PointF(400, 450); 129 PointF path2End = new PointF(400, 500); 130 int path2StartTime = 50; 131 int path2EndTime = 150; 132 StrokeDescription path2Stroke = createSwipeStroke( 133 path2Start.x, path2Start.y, path2End.x, path2End.y, path2StartTime, path2EndTime); 134 135 PointF path3Start = new PointF(500, 550); 136 PointF path3End = new PointF(500, 600); 137 int path3StartTime = path2EndTime; 138 int path3EndTime = 200; 139 StrokeDescription path3Stroke = createSwipeStroke( 140 path3Start.x, path3Start.y, path3End.x, path3End.y, path3StartTime, path3EndTime); 141 142 int deltaT = 12; // Force samples to happen on extra boundaries 143 GestureDescription.Builder builder = new GestureDescription.Builder(); 144 builder.addStroke(path0Stroke); 145 builder.addStroke(path1Stroke); 146 builder.addStroke(path2Stroke); 147 builder.addStroke(path3Stroke); 148 List<GestureStep> steps = MotionEventGenerator 149 .getGestureStepsFromGestureDescription(builder.build(), deltaT); 150 151 long start = 0; 152 assertThat(steps.get(0), allOf(numStartsOfStroke(1), numEndsOfStroke(0), isAtTime(start), 153 numTouchPointsIs(1), hasPoint(path0Start))); 154 assertThat(steps.get(1), allOf(numTouchPointsIs(1), noStartsOrEnds(), 155 isAtTime(start + deltaT))); 156 assertThat(steps.get(2), allOf(numTouchPointsIs(1), isAtTime(start + deltaT * 2))); 157 assertThat(steps.get(3), allOf(numTouchPointsIs(1), isAtTime(start + deltaT * 3))); 158 assertThat(steps.get(4), allOf(numTouchPointsIs(1), isAtTime(start + deltaT * 4))); 159 160 assertThat(steps.get(5), allOf(numTouchPointsIs(3), numStartsOfStroke(2), 161 numEndsOfStroke(0), isAtTime(path1StartTime), hasPoint(path1Start), 162 hasPoint(path2Start))); 163 164 start = path1StartTime; 165 assertThat(steps.get(6), allOf(numTouchPointsIs(3), isAtTime(start + deltaT * 1))); 166 assertThat(steps.get(7), allOf(noStartsOrEnds(), isAtTime(start + deltaT * 2))); 167 assertThat(steps.get(8), allOf(numTouchPointsIs(3), isAtTime(start + deltaT * 3))); 168 assertThat(steps.get(9), allOf(noStartsOrEnds(), isAtTime(start + deltaT * 4))); 169 170 assertThat(steps.get(10), allOf(numTouchPointsIs(3), numStartsOfStroke(0), 171 numEndsOfStroke(2), isAtTime(path0EndTime), hasPoint(path0End), 172 hasPoint(path1End))); 173 174 start = path0EndTime; 175 assertThat(steps.get(11), allOf(numTouchPointsIs(1), isAtTime(start + deltaT * 1))); 176 assertThat(steps.get(12), allOf(noStartsOrEnds(), isAtTime(start + deltaT * 2))); 177 assertThat(steps.get(13), allOf(numTouchPointsIs(1), isAtTime(start + deltaT * 3))); 178 assertThat(steps.get(14), allOf(noStartsOrEnds(), isAtTime(start + deltaT * 4))); 179 180 assertThat(steps.get(15), allOf(numTouchPointsIs(2), numStartsOfStroke(1), 181 numEndsOfStroke(1), isAtTime(path2EndTime), hasPoint(path2End), 182 hasPoint(path3Start))); 183 184 start = path2EndTime; 185 assertThat(steps.get(16), allOf(numTouchPointsIs(1), isAtTime(start + deltaT * 1))); 186 assertThat(steps.get(17), allOf(noStartsOrEnds(), isAtTime(start + deltaT * 2))); 187 assertThat(steps.get(18), allOf(numTouchPointsIs(1), isAtTime(start + deltaT * 3))); 188 assertThat(steps.get(19), allOf(noStartsOrEnds(), isAtTime(start + deltaT * 4))); 189 190 assertThat(steps.get(20), allOf(numTouchPointsIs(1), numStartsOfStroke(0), 191 numEndsOfStroke(1), isAtTime(path3EndTime), hasPoint(path3End))); 192 } 193 194 @Test 195 public void testMaxTouchpoints_shouldHaveValidCoords() { 196 GestureDescription.Builder maxPointBuilder = new GestureDescription.Builder(); 197 PointF baseStartPoint = new PointF(100, 100); 198 PointF baseEndPoint = new PointF(100, 200); 199 int xStep = 10; 200 int samplePeriod = 15; 201 int numSamples = 2; 202 int numPoints = GestureDescription.getMaxStrokeCount(); 203 for (int i = 0; i < numPoints; i++) { 204 Path path = new Path(); 205 path.moveTo(baseStartPoint.x + i * xStep, baseStartPoint.y); 206 path.lineTo(baseEndPoint.x + i * xStep, baseEndPoint.y); 207 maxPointBuilder.addStroke(new StrokeDescription(path, 0, samplePeriod * numSamples)); 208 } 209 210 List<GestureStep> steps = MotionEventGenerator 211 .getGestureStepsFromGestureDescription(maxPointBuilder.build(), samplePeriod); 212 assertEquals(3, steps.size()); 213 214 assertThat(steps.get(0), allOf(numTouchPointsIs(numPoints), numStartsOfStroke(numPoints), 215 numEndsOfStroke(0), isAtTime(0))); 216 assertThat(steps.get(1), allOf(numTouchPointsIs(numPoints), numStartsOfStroke(0), 217 numEndsOfStroke(0), isAtTime(samplePeriod))); 218 assertThat(steps.get(2), allOf(numTouchPointsIs(numPoints), numStartsOfStroke(0), 219 numEndsOfStroke(numPoints), isAtTime(samplePeriod * 2))); 220 221 PointF baseMidPoint = new PointF((baseStartPoint.x + baseEndPoint.x) / 2, 222 (baseStartPoint.y + baseEndPoint.y) / 2); 223 for (int i = 0; i < numPoints; i++) { 224 assertThat(steps.get(0), 225 hasPoint(new PointF(baseStartPoint.x + i * xStep, baseStartPoint.y))); 226 assertThat(steps.get(1), 227 hasPoint(new PointF(baseMidPoint.x + i * xStep, baseMidPoint.y))); 228 assertThat(steps.get(2), 229 hasPoint(new PointF(baseEndPoint.x + i * xStep, baseEndPoint.y))); 230 } 231 } 232 233 @Test 234 public void testGetGestureSteps_touchPointsHaveStrokeId() { 235 StrokeDescription swipeStroke = createSwipeStroke(10, 20, 30, 40, 0, 100); 236 GestureDescription swipe = new GestureDescription.Builder().addStroke(swipeStroke).build(); 237 List<GestureStep> swipeGestureSteps = MotionEventGenerator 238 .getGestureStepsFromGestureDescription(swipe, 10); 239 240 assertThat(swipeGestureSteps, everyItem(hasStrokeId(swipeStroke.getId()))); 241 } 242 243 @Test 244 public void testGetGestureSteps_continuedStroke_hasNoEndPoint() { 245 Path swipePath = new Path(); 246 swipePath.moveTo(10, 20); 247 swipePath.lineTo(30, 40); 248 StrokeDescription stroke1 = 249 new StrokeDescription(swipePath, 0, 100, 0, true); 250 GestureDescription gesture = new GestureDescription.Builder().addStroke(stroke1).build(); 251 List<GestureStep> steps = MotionEventGenerator 252 .getGestureStepsFromGestureDescription(gesture, 10); 253 254 assertThat(steps, everyItem(numEndsOfStroke(0))); 255 } 256 257 @Test 258 public void testGetGestureSteps_continuingStroke_hasNoStartPointAndHasContinuedId() { 259 Path swipePath = new Path(); 260 swipePath.moveTo(10, 20); 261 swipePath.lineTo(30, 40); 262 StrokeDescription stroke1 = 263 new StrokeDescription(swipePath, 0, 100, INVALID_STROKE_ID, true); 264 StrokeDescription stroke2 = 265 new StrokeDescription(swipePath, 0, 100, stroke1.getId(), false); 266 GestureDescription gesture = new GestureDescription.Builder().addStroke(stroke2).build(); 267 List<GestureStep> steps = MotionEventGenerator 268 .getGestureStepsFromGestureDescription(gesture, 10); 269 270 assertThat(steps, everyItem( 271 allOf(continuesStrokeId(stroke1.getId()), numStartsOfStroke(0)))); 272 } 273 274 private GestureDescription createSwipe( 275 float startX, float startY, float endX, float endY, long duration) { 276 GestureDescription.Builder swipeBuilder = new GestureDescription.Builder(); 277 swipeBuilder.addStroke(createSwipeStroke(startX, startY, endX, endY, 0, duration)); 278 return swipeBuilder.build(); 279 } 280 281 private StrokeDescription createSwipeStroke( 282 float startX, float startY, float endX, float endY, long startTime, long endTime) { 283 Path swipePath = new Path(); 284 swipePath.moveTo(startX, startY); 285 swipePath.lineTo(endX, endY); 286 StrokeDescription swipeStroke = 287 new StrokeDescription(swipePath, startTime, endTime - startTime); 288 return swipeStroke; 289 } 290 291 Matcher<GestureStep> numTouchPointsIs(final int numTouchPoints) { 292 return new TypeSafeMatcher<GestureStep>() { 293 @Override 294 protected boolean matchesSafely(GestureStep gestureStep) { 295 return gestureStep.numTouchPoints == numTouchPoints; 296 } 297 298 @Override 299 public void describeTo(Description description) { 300 description.appendText("Has " + numTouchPoints + " touch point(s)"); 301 } 302 }; 303 } 304 305 Matcher<GestureStep> numStartsOfStroke(final int numStarts) { 306 return new TypeSafeMatcher<GestureStep>() { 307 @Override 308 protected boolean matchesSafely(GestureStep gestureStep) { 309 int numStartsFound = 0; 310 for (int i = 0; i < gestureStep.numTouchPoints; i++) { 311 if (gestureStep.touchPoints[i].mIsStartOfPath) { 312 numStartsFound++; 313 } 314 } 315 return numStartsFound == numStarts; 316 } 317 318 @Override 319 public void describeTo(Description description) { 320 description.appendText("Starts " + numStarts + " stroke(s)"); 321 } 322 }; 323 } 324 325 Matcher<GestureStep> numEndsOfStroke(final int numEnds) { 326 return new TypeSafeMatcher<GestureStep>() { 327 @Override 328 protected boolean matchesSafely(GestureStep gestureStep) { 329 int numEndsFound = 0; 330 for (int i = 0; i < gestureStep.numTouchPoints; i++) { 331 if (gestureStep.touchPoints[i].mIsEndOfPath) { 332 numEndsFound++; 333 } 334 } 335 return numEndsFound == numEnds; 336 } 337 338 @Override 339 public void describeTo(Description description) { 340 description.appendText("Ends " + numEnds + " stroke(s)"); 341 } 342 }; 343 } 344 345 Matcher<GestureStep> hasPoint(final PointF point) { 346 return new TypeSafeMatcher<GestureStep>() { 347 @Override 348 protected boolean matchesSafely(GestureStep gestureStep) { 349 for (int i = 0; i < gestureStep.numTouchPoints; i++) { 350 if ((gestureStep.touchPoints[i].mX == point.x) 351 && (gestureStep.touchPoints[i].mY == point.y)) { 352 return true; 353 } 354 } 355 return false; 356 } 357 358 @Override 359 public void describeTo(Description description) { 360 description.appendText("Has at least one point at " + point); 361 } 362 }; 363 } 364 365 Matcher<GestureStep> hasStrokeId(final int strokeId) { 366 return new TypeSafeMatcher<GestureStep>() { 367 @Override 368 protected boolean matchesSafely(GestureStep gestureStep) { 369 for (int i = 0; i < gestureStep.numTouchPoints; i++) { 370 if (gestureStep.touchPoints[i].mStrokeId == strokeId) { 371 return true; 372 } 373 } 374 return false; 375 } 376 377 @Override 378 public void describeTo(Description description) { 379 description.appendText("Has at least one point with stroke id " + strokeId); 380 } 381 }; 382 } 383 384 Matcher<GestureStep> continuesStrokeId(final int strokeId) { 385 return new TypeSafeMatcher<GestureStep>() { 386 @Override 387 protected boolean matchesSafely(GestureStep gestureStep) { 388 for (int i = 0; i < gestureStep.numTouchPoints; i++) { 389 if (gestureStep.touchPoints[i].mContinuedStrokeId == strokeId) { 390 return true; 391 } 392 } 393 return false; 394 } 395 396 @Override 397 public void describeTo(Description description) { 398 description.appendText("Continues stroke id " + strokeId); 399 } 400 }; 401 } 402 403 Matcher<GestureStep> isAtTime(final long time) { 404 return new TypeSafeMatcher<GestureStep>() { 405 @Override 406 protected boolean matchesSafely(GestureStep gestureStep) { 407 return gestureStep.timeSinceGestureStart == time; 408 } 409 410 @Override 411 public void describeTo(Description description) { 412 description.appendText("Is at time " + time); 413 } 414 }; 415 } 416 417 Matcher<GestureStep> noStartsOrEnds() { 418 return allOf(numStartsOfStroke(0), numEndsOfStroke(0)); 419 } 420} 421