FocusFinderTest.java revision 22e31e5b609136d5bf7d77b1dccd6b042b83ebdf
1/* 2 * Copyright (C) 2008 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 android.view; 18 19import android.graphics.Rect; 20import android.test.AndroidTestCase; 21import android.test.suitebuilder.annotation.MediumTest; 22import android.test.suitebuilder.annotation.SmallTest; 23 24public class FocusFinderTest extends AndroidTestCase { 25 26 private FocusFinderHelper mFocusFinder; 27 28 @Override 29 protected void setUp() throws Exception { 30 super.setUp(); 31 32 mFocusFinder = new FocusFinderHelper(FocusFinder.getInstance()); 33 } 34 35 @SmallTest 36 public void testPreconditions() { 37 assertNotNull("focus finder instance", mFocusFinder); 38 } 39 40 @SmallTest 41 public void testBelowNotCandidateForDirectionUp() { 42 assertIsNotCandidate(View.FOCUS_UP, 43 new Rect(0, 30, 10, 40), // src (left, top, right, bottom) 44 new Rect(0, 50, 10, 60)); // dest (left, top, right, bottom) 45 } 46 47 @SmallTest 48 public void testAboveShareEdgeEdgeOkForDirectionUp() { 49 final Rect src = new Rect(0, 30, 10, 40); 50 51 final Rect dest = new Rect(src); 52 dest.offset(0, -src.height()); 53 assertEquals(src.top, dest.bottom); 54 55 assertDirectionIsCandidate(View.FOCUS_UP, src, dest); 56 } 57 58 @SmallTest 59 public void testCompletelyContainedNotCandidate() { 60 assertIsNotCandidate( 61 View.FOCUS_DOWN, 62 // L T R B 63 new Rect(0, 0, 50, 50), 64 new Rect(0, 1, 50, 49)); 65 } 66 67 @SmallTest 68 public void testContinaedWithCommonBottomNotCandidate() { 69 assertIsNotCandidate( 70 View.FOCUS_DOWN, 71 // L T R B 72 new Rect(0, 0, 50, 50), 73 new Rect(0, 1, 50, 50)); 74 } 75 76 @SmallTest 77 public void testOverlappingIsCandidateWhenBothEdgesAreInDirection() { 78 assertDirectionIsCandidate( 79 View.FOCUS_DOWN, 80 // L T R B 81 new Rect(0, 0, 50, 50), 82 new Rect(0, 1, 50, 51)); 83 } 84 85 @SmallTest 86 public void testTopEdgeOfDestAtOrAboveTopOfSrcNotCandidateForDown() { 87 assertIsNotCandidate( 88 View.FOCUS_DOWN, 89 // L T R B 90 new Rect(0, 0, 50, 50), 91 new Rect(0, 0, 50, 51)); 92 assertIsNotCandidate( 93 View.FOCUS_DOWN, 94 // L T R B 95 new Rect(0, 0, 50, 50), 96 new Rect(0, -1, 50, 51)); 97 } 98 99 @SmallTest 100 public void testSameRectBeamsOverlap() { 101 final Rect rect = new Rect(0, 0, 20, 20); 102 103 assertBeamsOverlap(View.FOCUS_LEFT, rect, rect); 104 assertBeamsOverlap(View.FOCUS_RIGHT, rect, rect); 105 assertBeamsOverlap(View.FOCUS_UP, rect, rect); 106 assertBeamsOverlap(View.FOCUS_DOWN, rect, rect); 107 } 108 109 @SmallTest 110 public void testOverlapBeamsRightLeftUpToEdge() { 111 final Rect rect1 = new Rect(0, 0, 20, 20); 112 final Rect rect2 = new Rect(rect1); 113 114 // just below bottom edge 115 rect2.offset(0, rect1.height() - 1); 116 assertBeamsOverlap(View.FOCUS_LEFT, rect1, rect2); 117 assertBeamsOverlap(View.FOCUS_RIGHT, rect1, rect2); 118 119 // at edge 120 rect2.offset(0, 1); 121 assertBeamsOverlap(View.FOCUS_LEFT, rect1, rect2); 122 assertBeamsOverlap(View.FOCUS_RIGHT, rect1, rect2); 123 124 // just beyond 125 rect2.offset(0, 1); 126 assertBeamsDontOverlap(View.FOCUS_LEFT, rect1, rect2); 127 assertBeamsDontOverlap(View.FOCUS_RIGHT, rect1, rect2); 128 129 // just below top edge 130 rect2.set(rect1); 131 rect2.offset(0, -(rect1.height() - 1)); 132 assertBeamsOverlap(View.FOCUS_LEFT, rect1, rect2); 133 assertBeamsOverlap(View.FOCUS_RIGHT, rect1, rect2); 134 135 // at top edge 136 rect2.offset(0, -1); 137 assertBeamsOverlap(View.FOCUS_LEFT, rect1, rect2); 138 assertBeamsOverlap(View.FOCUS_RIGHT, rect1, rect2); 139 140 // just beyond top edge 141 rect2.offset(0, -1); 142 assertBeamsDontOverlap(View.FOCUS_LEFT, rect1, rect2); 143 assertBeamsDontOverlap(View.FOCUS_RIGHT, rect1, rect2); 144 } 145 146 @SmallTest 147 public void testOverlapBeamsUpDownUpToEdge() { 148 final Rect rect1 = new Rect(0, 0, 20, 20); 149 final Rect rect2 = new Rect(rect1); 150 151 // just short of right edge 152 rect2.offset(rect1.width() - 1, 0); 153 assertBeamsOverlap(View.FOCUS_UP, rect1, rect2); 154 assertBeamsOverlap(View.FOCUS_DOWN, rect1, rect2); 155 156 // at edge 157 rect2.offset(1, 0); 158 assertBeamsOverlap(View.FOCUS_UP, rect1, rect2); 159 assertBeamsOverlap(View.FOCUS_DOWN, rect1, rect2); 160 161 // just beyond 162 rect2.offset(1, 0); 163 assertBeamsDontOverlap(View.FOCUS_UP, rect1, rect2); 164 assertBeamsDontOverlap(View.FOCUS_DOWN, rect1, rect2); 165 166 // just short of left edge 167 rect2.set(rect1); 168 rect2.offset(-(rect1.width() - 1), 0); 169 assertBeamsOverlap(View.FOCUS_UP, rect1, rect2); 170 assertBeamsOverlap(View.FOCUS_DOWN, rect1, rect2); 171 172 // at edge 173 rect2.offset(-1, 0); 174 assertBeamsOverlap(View.FOCUS_UP, rect1, rect2); 175 assertBeamsOverlap(View.FOCUS_DOWN, rect1, rect2); 176 177 // just beyond edge 178 rect2.offset(-1, 0); 179 assertBeamsDontOverlap(View.FOCUS_UP, rect1, rect2); 180 assertBeamsDontOverlap(View.FOCUS_DOWN, rect1, rect2); 181 } 182 183 @SmallTest 184 public void testDirectlyAboveTrumpsAboveLeft() { 185 Rect src = new Rect(0, 50, 20, 70); // src (left, top, right, bottom) 186 187 Rect directlyAbove = new Rect(src); 188 directlyAbove.offset(0, -(1 + src.height())); 189 190 Rect aboveLeft = new Rect(src); 191 aboveLeft.offset(-(1 + src.width()), -(1 + src.height())); 192 193 assertBetterCandidate(View.FOCUS_UP, src, directlyAbove, aboveLeft); 194 } 195 196 @SmallTest 197 public void testAboveInBeamTrumpsSlightlyCloserOutOfBeam() { 198 Rect src = new Rect(0, 50, 20, 70); // src (left, top, right, bottom) 199 200 Rect directlyAbove = new Rect(src); 201 directlyAbove.offset(0, -(1 + src.height())); 202 203 Rect aboveLeft = new Rect(src); 204 aboveLeft.offset(-(1 + src.width()), -(1 + src.height())); 205 206 // offset directly above a little further up 207 directlyAbove.offset(0, -5); 208 assertBetterCandidate(View.FOCUS_UP, src, directlyAbove, aboveLeft); 209 } 210 211 @SmallTest 212 public void testOutOfBeamBeatsInBeamUp() { 213 214 Rect src = new Rect(0, 0, 50, 50); // (left, top, right, bottom) 215 216 Rect aboveLeftOfBeam = new Rect(src); 217 aboveLeftOfBeam.offset(-(src.width() + 1), -src.height()); 218 assertBeamsDontOverlap(View.FOCUS_UP, src, aboveLeftOfBeam); 219 220 Rect aboveInBeam = new Rect(src); 221 aboveInBeam.offset(0, -src.height()); 222 assertBeamsOverlap(View.FOCUS_UP, src, aboveInBeam); 223 224 // in beam wins 225 assertBetterCandidate(View.FOCUS_UP, src, aboveInBeam, aboveLeftOfBeam); 226 227 // still wins while aboveInBeam's bottom edge is < out of beams' top 228 aboveInBeam.offset(0, -(aboveLeftOfBeam.height() - 1)); 229 assertTrue("aboveInBeam.bottom > aboveLeftOfBeam.top", aboveInBeam.bottom > aboveLeftOfBeam.top); 230 assertBetterCandidate(View.FOCUS_UP, src, aboveInBeam, aboveLeftOfBeam); 231 232 // cross the threshold: the out of beam prevails 233 aboveInBeam.offset(0, -1); 234 assertEquals(aboveInBeam.bottom, aboveLeftOfBeam.top); 235 assertBetterCandidate(View.FOCUS_UP, src, aboveLeftOfBeam, aboveInBeam); 236 } 237 238 /** 239 * A non-candidate (even a much closer one) is always a worse choice 240 * than a real candidate. 241 */ 242 @MediumTest 243 public void testSomeCandidateBetterThanNonCandidate() { 244 Rect src = new Rect(0, 0, 50, 50); // (left, top, right, bottom) 245 246 Rect nonCandidate = new Rect(src); 247 nonCandidate.offset(src.width() + 1, 0); 248 249 assertIsNotCandidate(View.FOCUS_LEFT, src, nonCandidate); 250 251 Rect candidate = new Rect(src); 252 candidate.offset(-(4 * src.width()), 0); 253 assertDirectionIsCandidate(View.FOCUS_LEFT, src, candidate); 254 255 assertBetterCandidate(View.FOCUS_LEFT, src, candidate, nonCandidate); 256 } 257 258 /** 259 * Grabbed from {@link com.android.frameworktest.focus.VerticalFocusSearchTest#testSearchFromMidLeft()} 260 */ 261 @SmallTest 262 public void testVerticalFocusSearchScenario() { 263 assertBetterCandidate(View.FOCUS_DOWN, 264 // L T R B 265 new Rect(0, 109, 153, 169), // src 266 new Rect(166, 169, 319, 229), // expectedbetter 267 new Rect(0, 229, 320, 289)); // expectedworse 268 269 // failing test 4/10/2008, the values were tweaked somehow in functional 270 // test... 271 assertBetterCandidate(View.FOCUS_DOWN, 272 // L T R B 273 new Rect(0, 91, 153, 133), // src 274 new Rect(166, 133, 319, 175), // expectedbetter 275 new Rect(0, 175, 320, 217)); // expectedworse 276 277 } 278 279 /** 280 * Example: going down from a thin button all the way to the left of a 281 * screen where, just below, is a very wide button, and just below that, 282 * is an equally skinny button all the way to the left. want to make 283 * sure any minor axis factor doesn't override the fact that the one below 284 * in vertical beam should be next focus 285 */ 286 @SmallTest 287 public void testBeamsOverlapMajorAxisCloserMinorAxisFurther() { 288 assertBetterCandidate(View.FOCUS_DOWN, 289 // L T R B 290 new Rect(0, 0, 100, 100), // src 291 new Rect(0, 100, 480, 200), // expectedbetter 292 new Rect(0, 200, 100, 300)); // expectedworse 293 } 294 295 /** 296 * Real scenario grabbed from song playback screen. 297 */ 298 @SmallTest 299 public void testMusicPlaybackScenario() { 300 assertBetterCandidate(View.FOCUS_LEFT, 301 // L T R B 302 new Rect(227, 185, 312, 231), // src 303 new Rect(195, 386, 266, 438), // expectedbetter 304 new Rect(124, 386, 195, 438)); // expectedworse 305 } 306 307 /** 308 * more generalized version of {@link #testMusicPlaybackScenario()} 309 */ 310 @SmallTest 311 public void testOutOfBeamOverlapBeatsOutOfBeamFurtherOnMajorAxis() { 312 assertBetterCandidate(View.FOCUS_DOWN, 313 // L T R B 314 new Rect(0, 0, 50, 50), // src 315 new Rect(60, 40, 110, 90), // expectedbetter 316 new Rect(60, 70, 110, 120)); // expectedworse 317 } 318 319 /** 320 * Make sure that going down prefers views that are actually 321 * down (and not those next to but still a candidate because 322 * they are overlapping on the major axis) 323 */ 324 @SmallTest 325 public void testInBeamTrumpsOutOfBeamOverlapping() { 326 assertBetterCandidate(View.FOCUS_DOWN, 327 // L T R B 328 new Rect(0, 0, 50, 50), // src 329 new Rect(0, 60, 50, 110), // expectedbetter 330 new Rect(51, 1, 101, 51)); // expectedworse 331 } 332 333 @SmallTest 334 public void testOverlappingBeatsNonOverlapping() { 335 assertBetterCandidate(View.FOCUS_DOWN, 336 // L T R B 337 new Rect(0, 0, 50, 50), // src 338 new Rect(0, 40, 50, 90), // expectedbetter 339 new Rect(0, 75, 50, 125)); // expectedworse 340 } 341 342 @SmallTest 343 public void testEditContactScenarioLeftFromDiscardChangesGoesToSaveContactInLandscape() { 344 assertBetterCandidate(View.FOCUS_LEFT, 345 // L T R B 346 new Rect(357, 258, 478, 318), // src 347 new Rect(2, 258, 100, 318), // better 348 new Rect(106, 120, 424, 184)); // worse 349 } 350 351 /** 352 * A dial pad with 9 squares arranged in a grid. no padding, so 353 * the edges are equal. see {@link com.android.frameworktest.focus.LinearLayoutGrid} 354 */ 355 @SmallTest 356 public void testGridWithTouchingEdges() { 357 assertBetterCandidate(View.FOCUS_DOWN, 358 // L T R B 359 new Rect(106, 49, 212, 192), // src 360 new Rect(106, 192, 212, 335), // better 361 new Rect(0, 192, 106, 335)); // worse 362 363 assertBetterCandidate(View.FOCUS_DOWN, 364 // L T R B 365 new Rect(106, 49, 212, 192), // src 366 new Rect(106, 192, 212, 335), // better 367 new Rect(212, 192, 318, 335)); // worse 368 } 369 370 @SmallTest 371 public void testSearchFromEmptyRect() { 372 assertBetterCandidate(View.FOCUS_DOWN, 373 // L T R B 374 new Rect(0, 0, 0, 0), // src 375 new Rect(0, 0, 320, 45), // better 376 new Rect(0, 45, 320, 545)); // worse 377 } 378 379 /** 380 * Reproduce bug 1124559, drilling down to actual bug 381 * (majorAxisDistance was wrong for direction left) 382 */ 383 @SmallTest 384 public void testGmailReplyButtonsScenario() { 385 assertBetterCandidate(View.FOCUS_LEFT, 386 // L T R B 387 new Rect(223, 380, 312, 417), // src 388 new Rect(102, 380, 210, 417), // better 389 new Rect(111, 443, 206, 480)); // worse 390 391 assertBeamBeats(View.FOCUS_LEFT, 392 // L T R B 393 new Rect(223, 380, 312, 417), // src 394 new Rect(102, 380, 210, 417), // better 395 new Rect(111, 443, 206, 480)); // worse 396 397 assertBeamsOverlap(View.FOCUS_LEFT, 398 // L T R B 399 new Rect(223, 380, 312, 417), 400 new Rect(102, 380, 210, 417)); 401 402 assertBeamsDontOverlap(View.FOCUS_LEFT, 403 // L T R B 404 new Rect(223, 380, 312, 417), 405 new Rect(111, 443, 206, 480)); 406 407 assertTrue( 408 "major axis distance less than major axis distance to " 409 + "far edge", 410 FocusFinderHelper.majorAxisDistance(View.FOCUS_LEFT, 411 // L T R B 412 new Rect(223, 380, 312, 417), 413 new Rect(102, 380, 210, 417)) < 414 FocusFinderHelper.majorAxisDistanceToFarEdge(View.FOCUS_LEFT, 415 // L T R B 416 new Rect(223, 380, 312, 417), 417 new Rect(111, 443, 206, 480))); 418 } 419 420 @SmallTest 421 public void testGmailScenarioBug1203288() { 422 assertBetterCandidate(View.FOCUS_DOWN, 423 // L T R B 424 new Rect(0, 2, 480, 82), // src 425 new Rect(344, 87, 475, 124), // better 426 new Rect(0, 130, 480, 203)); // worse 427 } 428 429 @SmallTest 430 public void testHomeShortcutScenarioBug1295354() { 431 assertBetterCandidate(View.FOCUS_RIGHT, 432 // L T R B 433 new Rect(3, 338, 77, 413), // src 434 new Rect(163, 338, 237, 413), // better 435 new Rect(83, 38, 157, 113)); // worse 436 } 437 438 @SmallTest 439 public void testBeamAlwaysBeatsHoriz() { 440 assertBetterCandidate(View.FOCUS_RIGHT, 441 // L T R B 442 new Rect(0, 0, 50, 50), // src 443 new Rect(150, 0, 200, 50), // better, (way further, but in beam) 444 new Rect(60, 51, 110, 101)); // worse, even though it is closer 445 446 assertBetterCandidate(View.FOCUS_LEFT, 447 // L T R B 448 new Rect(150, 0, 200, 50), // src 449 new Rect(0, 50, 50, 50), // better, (way further, but in beam) 450 new Rect(49, 99, 149, 101)); // worse, even though it is closer 451 } 452 453 @SmallTest 454 public void testIsCandidateOverlappingEdgeFromEmptyRect() { 455 assertDirectionIsCandidate(View.FOCUS_DOWN, 456 // L T R B 457 new Rect(0, 0, 0, 0), // src 458 new Rect(0, 0, 20, 1)); // candidate 459 460 assertDirectionIsCandidate(View.FOCUS_UP, 461 // L T R B 462 new Rect(0, 0, 0, 0), // src 463 new Rect(0, -1, 20, 0)); // candidate 464 465 assertDirectionIsCandidate(View.FOCUS_LEFT, 466 // L T R B 467 new Rect(0, 0, 0, 0), // src 468 new Rect(-1, 0, 0, 20)); // candidate 469 470 assertDirectionIsCandidate(View.FOCUS_RIGHT, 471 // L T R B 472 new Rect(0, 0, 0, 0), // src 473 new Rect(0, 0, 1, 20)); // candidate 474 } 475 476 private void assertBeamsOverlap(int direction, Rect rect1, Rect rect2) { 477 String directionStr = validateAndGetStringFor(direction); 478 String assertMsg = String.format("Expected beams to overlap in direction %s " 479 + "for rectangles %s and %s", directionStr, rect1, rect2); 480 assertTrue(assertMsg, mFocusFinder.beamsOverlap(direction, rect1, rect2)); 481 } 482 483 private void assertBeamsDontOverlap(int direction, Rect rect1, Rect rect2) { 484 String directionStr = validateAndGetStringFor(direction); 485 String assertMsg = String.format("Expected beams not to overlap in direction %s " 486 + "for rectangles %s and %s", directionStr, rect1, rect2); 487 assertFalse(assertMsg, mFocusFinder.beamsOverlap(direction, rect1, rect2)); 488 } 489 490 /** 491 * Assert that particular rect is a better focus search candidate from a 492 * source rect than another. 493 * @param direction The direction of focus search. 494 * @param srcRect The src rectangle. 495 * @param expectedBetter The candidate that should be better. 496 * @param expectedWorse The candidate that should be worse. 497 */ 498 private void assertBetterCandidate(int direction, Rect srcRect, 499 Rect expectedBetter, Rect expectedWorse) { 500 501 String directionStr = validateAndGetStringFor(direction); 502 String assertMsg = String.format( 503 "expected %s to be a better focus search candidate than " 504 + "%s when searching " 505 + "from %s in direction %s", 506 expectedBetter, expectedWorse, srcRect, directionStr); 507 508 assertTrue(assertMsg, 509 mFocusFinder.isBetterCandidate(direction, srcRect, 510 expectedBetter, expectedWorse)); 511 512 assertMsg = String.format( 513 "expected %s to not be a better focus search candidate than " 514 + "%s when searching " 515 + "from %s in direction %s", 516 expectedWorse, expectedBetter, srcRect, directionStr); 517 518 assertFalse(assertMsg, 519 mFocusFinder.isBetterCandidate(direction, srcRect, 520 expectedWorse, expectedBetter)); 521 } 522 523 private void assertIsNotCandidate(int direction, Rect src, Rect dest) { 524 String directionStr = validateAndGetStringFor(direction); 525 526 final String assertMsg = String.format( 527 "expected going from %s to %s in direction %s to be an invalid " 528 + "focus search candidate", 529 src, dest, directionStr); 530 assertFalse(assertMsg, mFocusFinder.isCandidate(src, dest, direction)); 531 } 532 533 private void assertBeamBeats(int direction, Rect srcRect, 534 Rect rect1, Rect rect2) { 535 536 String directionStr = validateAndGetStringFor(direction); 537 String assertMsg = String.format( 538 "expecting %s to beam beat %s w.r.t %s in direction %s", 539 rect1, rect2, srcRect, directionStr); 540 assertTrue(assertMsg, mFocusFinder.beamBeats(direction, srcRect, rect1, rect2)); 541 } 542 543 544 private void assertDirectionIsCandidate(int direction, Rect src, Rect dest) { 545 String directionStr = validateAndGetStringFor(direction); 546 547 final String assertMsg = String.format( 548 "expected going from %s to %s in direction %s to be a valid " 549 + "focus search candidate", 550 src, dest, directionStr); 551 assertTrue(assertMsg, mFocusFinder.isCandidate(src, dest, direction)); 552 } 553 554 private String validateAndGetStringFor(int direction) { 555 String directionStr = "??"; 556 switch(direction) { 557 case View.FOCUS_UP: 558 directionStr = "FOCUS_UP"; 559 break; 560 case View.FOCUS_DOWN: 561 directionStr = "FOCUS_DOWN"; 562 break; 563 case View.FOCUS_LEFT: 564 directionStr = "FOCUS_LEFT"; 565 break; 566 case View.FOCUS_RIGHT: 567 directionStr = "FOCUS_RIGHT"; 568 break; 569 default: 570 fail("passed in unknown direction, ya blewit!"); 571 } 572 return directionStr; 573 } 574 575 576} 577