1 /* 2 * Copyright (C) 2010 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 17 package android.view.accessibility.cts; 18 19 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen; 20 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_IN_DIRECTION; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertSame; 26 import static org.junit.Assert.assertThrows; 27 import static org.junit.Assert.assertTrue; 28 import static org.junit.Assert.fail; 29 30 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; 31 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule; 32 import android.app.Activity; 33 import android.app.Instrumentation; 34 import android.app.UiAutomation; 35 import android.content.Context; 36 import android.os.Message; 37 import android.os.Parcel; 38 import android.os.SystemClock; 39 import android.platform.test.annotations.FlakyTest; 40 import android.text.SpannableString; 41 import android.text.TextUtils; 42 import android.text.style.LocaleSpan; 43 import android.view.Display; 44 import android.view.View; 45 import android.view.accessibility.AccessibilityEvent; 46 import android.view.accessibility.AccessibilityNodeInfo; 47 import android.view.accessibility.AccessibilityRecord; 48 import android.widget.LinearLayout; 49 import android.widget.TextView; 50 51 import androidx.test.InstrumentationRegistry; 52 import androidx.test.filters.SmallTest; 53 import androidx.test.rule.ActivityTestRule; 54 import androidx.test.runner.AndroidJUnit4; 55 56 import org.junit.Before; 57 import org.junit.Rule; 58 import org.junit.Test; 59 import org.junit.rules.RuleChain; 60 import org.junit.runner.RunWith; 61 62 import java.util.ArrayList; 63 import java.util.List; 64 import java.util.Locale; 65 import java.util.concurrent.TimeoutException; 66 67 /** Class for testing {@link AccessibilityEvent}. */ 68 // TODO(b/263942937) Re-enable @Presubmit 69 @RunWith(AndroidJUnit4.class) 70 public class AccessibilityEventTest { 71 private static final long IDLE_TIMEOUT_MS = 500; 72 private static final long DEFAULT_TIMEOUT_MS = 2000; 73 74 // From ViewConfiguration.SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS 75 private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 100; 76 77 private EventReportingLinearLayout mParentView; 78 private View mChildView; 79 private TextView mTextView; 80 private String mPackageName; 81 82 private static Instrumentation sInstrumentation; 83 private static UiAutomation sUiAutomation; 84 private final ActivityTestRule<DummyActivity> mActivityRule = 85 new ActivityTestRule<>(DummyActivity.class, false, false); 86 private final AccessibilityDumpOnFailureRule mDumpOnFailureRule = 87 new AccessibilityDumpOnFailureRule(); 88 private InstrumentedAccessibilityServiceTestRule<SpeakingAccessibilityService> 89 mInstrumentedAccessibilityServiceRule = 90 new InstrumentedAccessibilityServiceTestRule<>( 91 SpeakingAccessibilityService.class, false); 92 93 @Rule 94 public final RuleChain mRuleChain = 95 RuleChain.outerRule(mActivityRule) 96 .around(mInstrumentedAccessibilityServiceRule) 97 .around(mDumpOnFailureRule); 98 99 @Before setUp()100 public void setUp() throws Throwable { 101 sInstrumentation = InstrumentationRegistry.getInstrumentation(); 102 sUiAutomation = sInstrumentation.getUiAutomation(); 103 final Activity activity = launchActivityAndWaitForItToBeOnscreen( 104 sInstrumentation, sUiAutomation, mActivityRule); 105 mPackageName = activity.getApplicationContext().getPackageName(); 106 mInstrumentedAccessibilityServiceRule.enableService(); 107 sUiAutomation.executeAndWaitForEvent(() -> { 108 try { 109 mActivityRule.runOnUiThread(() -> { 110 final LinearLayout grandparent = new LinearLayout(activity); 111 activity.setContentView(grandparent); 112 mParentView = new EventReportingLinearLayout(activity); 113 mChildView = new View(activity); 114 mTextView = new TextView(activity); 115 grandparent.addView(mParentView); 116 mParentView.addView(mChildView); 117 mParentView.addView(mTextView); 118 }); 119 } catch (Throwable e) { 120 fail(e.toString()); 121 } 122 }, 123 // There can be a race where the test Activity gets focus and we start test. 124 // Because we don't specify flagRetrieveInteractiveWindows in this test, until 125 // the Activity gets focus, no events will be delivered from it. 126 // So. this waits for any event from the test activity. 127 accessibilityEvent -> mPackageName.equals(accessibilityEvent.getPackageName()), 128 DEFAULT_TIMEOUT_MS); 129 sUiAutomation.waitForIdle(IDLE_TIMEOUT_MS, DEFAULT_TIMEOUT_MS); 130 } 131 132 private static class EventReportingLinearLayout extends LinearLayout { 133 public List<AccessibilityEvent> mReceivedEvents = new ArrayList<AccessibilityEvent>(); 134 EventReportingLinearLayout(Context context)135 public EventReportingLinearLayout(Context context) { 136 super(context); 137 } 138 139 @Override requestSendAccessibilityEvent(View child, AccessibilityEvent event)140 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { 141 mReceivedEvents.add(AccessibilityEvent.obtain(event)); 142 return super.requestSendAccessibilityEvent(child, event); 143 } 144 } 145 146 @Test testScrollEvent()147 public void testScrollEvent() throws Exception { 148 sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( 149 () -> mChildView.scrollTo(0, 100)), new ScrollEventFilter(1), DEFAULT_TIMEOUT_MS); 150 } 151 152 @Test testScrollEventBurstCombined()153 public void testScrollEventBurstCombined() throws Exception { 154 sUiAutomation.executeAndWaitForEvent( 155 () -> sInstrumentation.runOnMainSync( 156 () -> { 157 mChildView.scrollTo(0, 100); 158 mChildView.scrollTo(0, 125); 159 mChildView.scrollTo(0, 150); 160 mChildView.scrollTo(0, 175); 161 }), 162 new ScrollEventFilter(1), 163 DEFAULT_TIMEOUT_MS); 164 } 165 166 @Test 167 @FlakyTest testScrollEventsDeliveredInCorrectInterval()168 public void testScrollEventsDeliveredInCorrectInterval() throws Exception { 169 sUiAutomation.executeAndWaitForEvent( 170 () -> { 171 sInstrumentation.runOnMainSync(() -> { 172 mChildView.scrollTo(0, 25); 173 mChildView.scrollTo(0, 50); 174 mChildView.scrollTo(0, 100); 175 }); 176 SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2); 177 sInstrumentation.runOnMainSync(() -> { 178 mChildView.scrollTo(0, 150); 179 mChildView.scrollTo(0, 175); 180 }); 181 SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS / 2); 182 sInstrumentation.runOnMainSync(() -> { 183 mChildView.scrollTo(0, 200); 184 }); 185 }, 186 new ScrollEventFilter(2), 187 DEFAULT_TIMEOUT_MS); 188 } 189 190 class ScrollEventFilter extends AccessibilityEventFilter { 191 private int mCount = 0; 192 private int mTargetCount; 193 ScrollEventFilter(int count)194 ScrollEventFilter(int count) { 195 mTargetCount = count; 196 } 197 accept(AccessibilityEvent event)198 public boolean accept(AccessibilityEvent event) { 199 if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { 200 mCount += 1; 201 mEvents.add(event); 202 return mCount >= mTargetCount; 203 } 204 return false; 205 } 206 } 207 208 @Test testScrollEventsClearedOnDetach()209 public void testScrollEventsClearedOnDetach() throws Throwable { 210 ScrollEventFilter scrollEventFilter = new ScrollEventFilter(1); 211 sUiAutomation.executeAndWaitForEvent( 212 () -> sInstrumentation.runOnMainSync( 213 () -> { 214 mChildView.scrollTo(0, 25); 215 mChildView.scrollTo(5, 50); 216 mChildView.scrollTo(7, 100); 217 }), 218 scrollEventFilter, 219 DEFAULT_TIMEOUT_MS); 220 mActivityRule.runOnUiThread( 221 () -> { 222 mParentView.removeView(mChildView); 223 mParentView.addView(mChildView); 224 }); 225 sUiAutomation.executeAndWaitForEvent( 226 () -> sInstrumentation.runOnMainSync( 227 () -> { 228 mChildView.scrollTo(0, 150); 229 }), 230 scrollEventFilter, 231 DEFAULT_TIMEOUT_MS); 232 AccessibilityEvent event = scrollEventFilter.getLastEvent(); 233 assertEquals(-7, event.getScrollDeltaX()); 234 assertEquals(50, event.getScrollDeltaY()); 235 } 236 237 @Test testScrollEventsCaptureTotalDelta()238 public void testScrollEventsCaptureTotalDelta() throws Throwable { 239 ScrollEventFilter scrollEventFilter = new ScrollEventFilter(1); 240 sUiAutomation.executeAndWaitForEvent( 241 () -> sInstrumentation.runOnMainSync( 242 () -> { 243 mChildView.scrollTo(0, 25); 244 mChildView.scrollTo(5, 50); 245 mChildView.scrollTo(7, 100); 246 }), 247 scrollEventFilter, 248 DEFAULT_TIMEOUT_MS); 249 AccessibilityEvent event = scrollEventFilter.getLastEvent(); 250 assertEquals(7, event.getScrollDeltaX()); 251 assertEquals(100, event.getScrollDeltaY()); 252 } 253 254 @Test testScrollEventsClearDeltaAfterSending()255 public void testScrollEventsClearDeltaAfterSending() throws Throwable { 256 ScrollEventFilter scrollEventFilter = new ScrollEventFilter(2); 257 sUiAutomation.executeAndWaitForEvent( 258 () -> { 259 sInstrumentation.runOnMainSync(() -> { 260 mChildView.scrollTo(0, 25); 261 mChildView.scrollTo(5, 50); 262 mChildView.scrollTo(7, 100); 263 }); 264 SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2); 265 sInstrumentation.runOnMainSync(() -> { 266 mChildView.scrollTo(0, 25); 267 mChildView.scrollTo(5, 50); 268 mChildView.scrollTo(7, 100); 269 mChildView.scrollTo(0, 150); 270 }); 271 }, 272 scrollEventFilter, 273 DEFAULT_TIMEOUT_MS); 274 AccessibilityEvent event = scrollEventFilter.getLastEvent(); 275 assertEquals(-7, event.getScrollDeltaX()); 276 assertEquals(50, event.getScrollDeltaY()); 277 } 278 279 @Test testEventViewTargetedByScroll()280 public void testEventViewTargetedByScroll() throws Throwable { 281 final AccessibilityEvent awaitedEvent = sUiAutomation.executeAndWaitForEvent( 282 () -> { 283 AccessibilityEvent event = new AccessibilityEvent( 284 AccessibilityEvent.TYPE_VIEW_TARGETED_BY_SCROLL); 285 event.setAction(ACTION_SCROLL_IN_DIRECTION.getId()); 286 mChildView.sendAccessibilityEventUnchecked(event); 287 }, 288 event -> event.getEventType() == AccessibilityEvent.TYPE_VIEW_TARGETED_BY_SCROLL, 289 DEFAULT_TIMEOUT_MS); 290 assertThat(awaitedEvent.getAction()).isEqualTo(ACTION_SCROLL_IN_DIRECTION.getId()); 291 assertThat(awaitedEvent.getSource()).isEqualTo(mChildView.createAccessibilityNodeInfo()); 292 } 293 294 @Test testStateEvent()295 public void testStateEvent() throws Throwable { 296 sUiAutomation.executeAndWaitForEvent( 297 () -> { 298 sendStateDescriptionChangedEvent(mChildView); 299 }, 300 new StateDescriptionEventFilter(1), 301 DEFAULT_TIMEOUT_MS); 302 } 303 304 @Test testStateEventBurstCombined()305 public void testStateEventBurstCombined() throws Throwable { 306 sUiAutomation.executeAndWaitForEvent( 307 () -> { 308 sendStateDescriptionChangedEvent(mChildView); 309 sendStateDescriptionChangedEvent(mChildView); 310 sendStateDescriptionChangedEvent(mChildView); 311 sendStateDescriptionChangedEvent(mChildView); 312 }, 313 new StateDescriptionEventFilter(1), 314 DEFAULT_TIMEOUT_MS); 315 } 316 317 @Test testStateEventsDeliveredInCorrectInterval()318 public void testStateEventsDeliveredInCorrectInterval() throws Throwable { 319 sUiAutomation.executeAndWaitForEvent( 320 () -> { 321 sendStateDescriptionChangedEvent(mChildView); 322 sendStateDescriptionChangedEvent(mChildView); 323 sendStateDescriptionChangedEvent(mChildView); 324 SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2); 325 sendStateDescriptionChangedEvent(mChildView); 326 sendStateDescriptionChangedEvent(mChildView); 327 SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS / 2); 328 sendStateDescriptionChangedEvent(mChildView); 329 }, 330 new StateDescriptionEventFilter(2), 331 DEFAULT_TIMEOUT_MS); 332 } 333 334 @Test testStateEventsHaveLastEventText()335 public void testStateEventsHaveLastEventText() throws Throwable { 336 StateDescriptionEventFilter stateDescriptionEventFilter = 337 new StateDescriptionEventFilter(1); 338 String expectedState = "Second state"; 339 sUiAutomation.executeAndWaitForEvent( 340 () -> { 341 sendStateDescriptionChangedEvent(mChildView, "First state"); 342 sendStateDescriptionChangedEvent(mChildView, expectedState); 343 }, 344 stateDescriptionEventFilter, 345 DEFAULT_TIMEOUT_MS); 346 AccessibilityEvent event = stateDescriptionEventFilter.getLastEvent(); 347 assertEquals(expectedState, event.getText().get(0)); 348 } 349 350 class StateDescriptionEventFilter extends AccessibilityEventFilter { 351 private int mCount; 352 private int mTargetCount; 353 StateDescriptionEventFilter(int count)354 StateDescriptionEventFilter(int count) { 355 mTargetCount = count; 356 } 357 accept(AccessibilityEvent event)358 public boolean accept(AccessibilityEvent event) { 359 if (event.getContentChangeTypes() 360 == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION) { 361 mCount += 1; 362 mEvents.add(event); 363 return mCount >= mTargetCount; 364 } 365 return false; 366 } 367 } 368 ; 369 370 private abstract class AccessibilityEventFilter 371 implements UiAutomation.AccessibilityEventFilter { 372 protected List<AccessibilityEvent> mEvents = new ArrayList<>(); 373 accept(AccessibilityEvent event)374 public abstract boolean accept(AccessibilityEvent event); 375 assertReceivedEventCount(int count)376 void assertReceivedEventCount(int count) { 377 assertEquals(count, mEvents.size()); 378 } 379 getLastEvent()380 AccessibilityEvent getLastEvent() { 381 if (mEvents.size() > 0) { 382 return mEvents.get(mEvents.size() - 1); 383 } 384 return null; 385 } 386 } 387 sendStateDescriptionChangedEvent(View view)388 private void sendStateDescriptionChangedEvent(View view) { 389 sendStateDescriptionChangedEvent(view, null); 390 } 391 sendStateDescriptionChangedEvent(View view, CharSequence text)392 private void sendStateDescriptionChangedEvent(View view, CharSequence text) { 393 AccessibilityEvent event = 394 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 395 event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION); 396 event.getText().add(text); 397 view.sendAccessibilityEventUnchecked(event); 398 } 399 400 @Test 401 @FlakyTest setTextError_receiveEvent()402 public void setTextError_receiveEvent() throws Throwable { 403 sUiAutomation.executeAndWaitForEvent( 404 () -> sInstrumentation.runOnMainSync(() -> mTextView.setError("error")), 405 event -> isExpectedChangeType(event, 406 AccessibilityEvent.CONTENT_CHANGE_TYPE_ERROR 407 | AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_INVALID) 408 && event.getSource().getError() != null, 409 DEFAULT_TIMEOUT_MS); 410 } 411 412 @Test setViewEnable_receiveEvent()413 public void setViewEnable_receiveEvent() throws Throwable { 414 sUiAutomation.executeAndWaitForEvent( 415 () -> sInstrumentation.runOnMainSync(() -> { 416 mChildView.setEnabled(!mChildView.isEnabled()); 417 }), 418 event -> isExpectedChangeType(event, 419 AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED), 420 DEFAULT_TIMEOUT_MS); 421 } 422 423 @Test setText_unChanged_doNotReceiveEvent()424 public void setText_unChanged_doNotReceiveEvent() throws Throwable { 425 sUiAutomation.executeAndWaitForEvent( 426 () -> sInstrumentation.runOnMainSync(() -> mTextView.setText("a")), 427 event -> isExpectedChangeType(event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT), 428 DEFAULT_TIMEOUT_MS); 429 430 assertThrows( 431 TimeoutException.class, 432 () -> 433 sUiAutomation.executeAndWaitForEvent( 434 () -> { 435 sInstrumentation.runOnMainSync( 436 () -> { 437 mTextView.setText("a"); 438 }); 439 }, 440 event -> isExpectedSource(event, mTextView), 441 DEFAULT_TIMEOUT_MS)); 442 } 443 444 @Test setText_textChanged_receivesTextEvent()445 public void setText_textChanged_receivesTextEvent() throws Throwable { 446 sUiAutomation.executeAndWaitForEvent( 447 () -> sInstrumentation.runOnMainSync(() -> mTextView.setText("a")), 448 event -> isExpectedChangeType(event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT), 449 DEFAULT_TIMEOUT_MS); 450 451 sUiAutomation.executeAndWaitForEvent( 452 () -> { 453 sInstrumentation.runOnMainSync( 454 () -> { 455 mTextView.setText("b"); 456 }); 457 }, 458 event -> 459 isExpectedSource(event, mTextView) 460 && isExpectedChangeType( 461 event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT), 462 DEFAULT_TIMEOUT_MS); 463 } 464 465 @Test setText_parcelableSpanChanged_receivesUndefinedEvent()466 public void setText_parcelableSpanChanged_receivesUndefinedEvent() throws Throwable { 467 String text = "a"; 468 sUiAutomation.executeAndWaitForEvent( 469 () -> sInstrumentation.runOnMainSync(() -> mTextView.setText(text)), 470 event -> isExpectedChangeType(event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT), 471 DEFAULT_TIMEOUT_MS); 472 473 sUiAutomation.executeAndWaitForEvent( 474 () -> { 475 sInstrumentation.runOnMainSync( 476 () -> { 477 SpannableString spannableString = new SpannableString(text); 478 spannableString.setSpan(new LocaleSpan(Locale.ENGLISH), 0, 1, 0); 479 mTextView.setText(spannableString); 480 }); 481 }, 482 event -> 483 isExpectedSource(event, mTextView) 484 && isExpectedChangeType( 485 event, AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED), 486 DEFAULT_TIMEOUT_MS); 487 } 488 isExpectedSource(AccessibilityEvent event, View view)489 private static boolean isExpectedSource(AccessibilityEvent event, View view) { 490 return TextUtils.equals(view.getContext().getPackageName(), event.getPackageName()) 491 && TextUtils.equals(view.getAccessibilityClassName(), event.getClassName()); 492 } 493 isExpectedChangeType(AccessibilityEvent event, int changeType)494 private static boolean isExpectedChangeType(AccessibilityEvent event, int changeType) { 495 return (event.getContentChangeTypes() & changeType) == changeType; 496 } 497 498 /** 499 * Tests whether accessibility events are correctly written and read from a parcel (version 1). 500 */ 501 @SmallTest 502 @Test testMarshaling()503 public void testMarshaling() throws Exception { 504 // fully populate the event to marshal 505 AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); 506 fullyPopulateAccessibilityEvent(sentEvent); 507 508 // marshal and unmarshal the event 509 Parcel parcel = Parcel.obtain(); 510 sentEvent.writeToParcel(parcel, 0); 511 parcel.setDataPosition(0); 512 AccessibilityEvent receivedEvent = AccessibilityEvent.CREATOR.createFromParcel(parcel); 513 514 // make sure all fields properly marshaled 515 assertEqualsAccessibilityEvent(sentEvent, receivedEvent); 516 517 parcel.recycle(); 518 } 519 520 /** Tests if {@link AccessibilityEvent} can be acquired through obtain(). */ 521 @SmallTest 522 @Test 523 @FlakyTest testRecycle()524 public void testRecycle() { 525 // evaluate that recycle() can be called on an event acquired by obtain() 526 AccessibilityEvent.obtain().recycle(); 527 } 528 529 /** Tests whether the event types are correctly converted to strings. */ 530 @SmallTest 531 @Test testEventTypeToString()532 public void testEventTypeToString() { 533 assertEquals( 534 "TYPE_NOTIFICATION_STATE_CHANGED", 535 AccessibilityEvent.eventTypeToString( 536 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)); 537 assertEquals( 538 "TYPE_TOUCH_EXPLORATION_GESTURE_END", 539 AccessibilityEvent.eventTypeToString( 540 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END)); 541 assertEquals( 542 "TYPE_TOUCH_EXPLORATION_GESTURE_START", 543 AccessibilityEvent.eventTypeToString( 544 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START)); 545 assertEquals( 546 "TYPE_VIEW_CLICKED", 547 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_CLICKED)); 548 assertEquals( 549 "TYPE_VIEW_FOCUSED", 550 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_FOCUSED)); 551 assertEquals( 552 "TYPE_VIEW_HOVER_ENTER", 553 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)); 554 assertEquals( 555 "TYPE_VIEW_HOVER_EXIT", 556 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)); 557 assertEquals( 558 "TYPE_VIEW_LONG_CLICKED", 559 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED)); 560 assertEquals( 561 "TYPE_VIEW_CONTEXT_CLICKED", 562 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED)); 563 assertEquals( 564 "TYPE_VIEW_SCROLLED", 565 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_SCROLLED)); 566 assertEquals( 567 "TYPE_VIEW_SELECTED", 568 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_SELECTED)); 569 assertEquals( 570 "TYPE_VIEW_TEXT_CHANGED", 571 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)); 572 assertEquals( 573 "TYPE_VIEW_TEXT_SELECTION_CHANGED", 574 AccessibilityEvent.eventTypeToString( 575 AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED)); 576 assertEquals( 577 "TYPE_WINDOW_CONTENT_CHANGED", 578 AccessibilityEvent.eventTypeToString( 579 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)); 580 assertEquals( 581 "TYPE_WINDOW_STATE_CHANGED", 582 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)); 583 } 584 585 /** Tests whether the event describes its contents consistently. */ 586 @SmallTest 587 @Test testDescribeContents()588 public void testDescribeContents() { 589 AccessibilityEvent event = AccessibilityEvent.obtain(); 590 assertSame( 591 "Accessibility events always return 0 for this method.", 592 0, 593 event.describeContents()); 594 fullyPopulateAccessibilityEvent(event); 595 assertSame( 596 "Accessibility events always return 0 for this method.", 597 0, 598 event.describeContents()); 599 } 600 601 /** 602 * Tests whether accessibility events are correctly written and read from a parcel (version 2). 603 */ 604 @SmallTest 605 @Test testMarshaling2()606 public void testMarshaling2() { 607 // fully populate the event to marshal 608 AccessibilityEvent marshaledEvent = AccessibilityEvent.obtain(); 609 fullyPopulateAccessibilityEvent(marshaledEvent); 610 611 // marshal and unmarshal the event 612 Parcel parcel = Parcel.obtain(); 613 marshaledEvent.writeToParcel(parcel, 0); 614 parcel.setDataPosition(0); 615 AccessibilityEvent unmarshaledEvent = AccessibilityEvent.obtain(); 616 unmarshaledEvent.initFromParcel(parcel); 617 618 // make sure all fields properly marshaled 619 assertEqualsAccessibilityEvent(marshaledEvent, unmarshaledEvent); 620 621 parcel.recycle(); 622 } 623 624 /** 625 * While CharSequence is immutable, some classes implementing it are mutable. Make sure they 626 * can't change the object by changing the objects backing CharSequence 627 */ 628 @SmallTest 629 @Test testChangeTextAfterSetting_shouldNotAffectEvent()630 public void testChangeTextAfterSetting_shouldNotAffectEvent() { 631 final String originalText = "Cassowary"; 632 final String newText = "Hornbill"; 633 AccessibilityEvent event = AccessibilityEvent.obtain(); 634 StringBuffer updatingString = new StringBuffer(originalText); 635 event.setBeforeText(updatingString); 636 event.setContentDescription(updatingString); 637 638 updatingString.delete(0, updatingString.length()); 639 updatingString.append(newText); 640 641 assertTrue(TextUtils.equals(originalText, event.getBeforeText())); 642 assertTrue(TextUtils.equals(originalText, event.getContentDescription())); 643 } 644 645 @SmallTest 646 @Test testConstructors()647 public void testConstructors() { 648 final AccessibilityEvent populatedEvent = new AccessibilityEvent(); 649 fullyPopulateAccessibilityEvent(populatedEvent); 650 final AccessibilityEvent event = new AccessibilityEvent(populatedEvent); 651 652 assertEqualsAccessibilityEvent(event, populatedEvent); 653 654 final AccessibilityEvent firstEvent = new AccessibilityEvent(); 655 firstEvent.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED); 656 final AccessibilityEvent secondEvent = 657 new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 658 659 assertEqualsAccessibilityEvent(firstEvent, secondEvent); 660 } 661 662 /** 663 * Fully populates the {@link AccessibilityEvent} to marshal. 664 * 665 * @param sentEvent The event to populate. 666 */ fullyPopulateAccessibilityEvent(AccessibilityEvent sentEvent)667 private void fullyPopulateAccessibilityEvent(AccessibilityEvent sentEvent) { 668 sentEvent.setAddedCount(1); 669 sentEvent.setBeforeText("BeforeText"); 670 sentEvent.setChecked(true); 671 sentEvent.setClassName("foo.bar.baz.Class"); 672 sentEvent.setContentDescription("ContentDescription"); 673 sentEvent.setCurrentItemIndex(1); 674 sentEvent.setEnabled(true); 675 sentEvent.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED); 676 sentEvent.setEventTime(1000); 677 sentEvent.setFromIndex(1); 678 sentEvent.setFullScreen(true); 679 sentEvent.setItemCount(1); 680 sentEvent.setPackageName("foo.bar.baz"); 681 sentEvent.setParcelableData(Message.obtain(null, 1, 2, 3)); 682 sentEvent.setPassword(true); 683 sentEvent.setRemovedCount(1); 684 sentEvent.getText().add("Foo"); 685 sentEvent.setMaxScrollX(1); 686 sentEvent.setMaxScrollY(1); 687 sentEvent.setScrollX(1); 688 sentEvent.setScrollY(1); 689 sentEvent.setScrollDeltaX(3); 690 sentEvent.setScrollDeltaY(3); 691 sentEvent.setToIndex(1); 692 sentEvent.setScrollable(true); 693 sentEvent.setAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 694 sentEvent.setMovementGranularity(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE); 695 sentEvent.setDisplayId(Display.DEFAULT_DISPLAY); 696 sentEvent.setSpeechStateChangeTypes(AccessibilityEvent.SPEECH_STATE_SPEAKING_START); 697 698 AccessibilityRecord record = AccessibilityRecord.obtain(); 699 AccessibilityRecordTest.fullyPopulateAccessibilityRecord(record); 700 sentEvent.appendRecord(record); 701 } 702 703 /** 704 * Compares all properties of the <code>expectedEvent</code> and the <code>receivedEvent</code> 705 * to verify that the received event is the one that is expected. 706 */ assertEqualsAccessibilityEvent( AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent)707 private static void assertEqualsAccessibilityEvent( 708 AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent) { 709 assertEquals( 710 "addedCount has incorrect value", 711 expectedEvent.getAddedCount(), 712 receivedEvent.getAddedCount()); 713 assertEquals( 714 "beforeText has incorrect value", 715 expectedEvent.getBeforeText(), 716 receivedEvent.getBeforeText()); 717 assertEquals( 718 "checked has incorrect value", 719 expectedEvent.isChecked(), 720 receivedEvent.isChecked()); 721 assertEquals( 722 "className has incorrect value", 723 expectedEvent.getClassName(), 724 receivedEvent.getClassName()); 725 assertEquals( 726 "contentDescription has incorrect value", 727 expectedEvent.getContentDescription(), 728 receivedEvent.getContentDescription()); 729 assertEquals( 730 "currentItemIndex has incorrect value", 731 expectedEvent.getCurrentItemIndex(), 732 receivedEvent.getCurrentItemIndex()); 733 assertEquals( 734 "enabled has incorrect value", 735 expectedEvent.isEnabled(), 736 receivedEvent.isEnabled()); 737 assertEquals( 738 "eventType has incorrect value", 739 expectedEvent.getEventType(), 740 receivedEvent.getEventType()); 741 assertEquals( 742 "fromIndex has incorrect value", 743 expectedEvent.getFromIndex(), 744 receivedEvent.getFromIndex()); 745 assertEquals( 746 "fullScreen has incorrect value", 747 expectedEvent.isFullScreen(), 748 receivedEvent.isFullScreen()); 749 assertEquals( 750 "itemCount has incorrect value", 751 expectedEvent.getItemCount(), 752 receivedEvent.getItemCount()); 753 assertEquals( 754 "password has incorrect value", 755 expectedEvent.isPassword(), 756 receivedEvent.isPassword()); 757 assertEquals( 758 "removedCount has incorrect value", 759 expectedEvent.getRemovedCount(), 760 receivedEvent.getRemovedCount()); 761 assertSame( 762 "maxScrollX has incorrect value", 763 expectedEvent.getMaxScrollX(), 764 receivedEvent.getMaxScrollX()); 765 assertSame( 766 "maxScrollY has incorrect value", 767 expectedEvent.getMaxScrollY(), 768 receivedEvent.getMaxScrollY()); 769 assertSame( 770 "scrollX has incorrect value", 771 expectedEvent.getScrollX(), 772 receivedEvent.getScrollX()); 773 assertSame( 774 "scrollY has incorrect value", 775 expectedEvent.getScrollY(), 776 receivedEvent.getScrollY()); 777 assertSame( 778 "scrollDeltaX has incorrect value", 779 expectedEvent.getScrollDeltaX(), 780 receivedEvent.getScrollDeltaX()); 781 assertSame( 782 "scrollDeltaY has incorrect value", 783 expectedEvent.getScrollDeltaY(), 784 receivedEvent.getScrollDeltaY()); 785 assertSame( 786 "toIndex has incorrect value", 787 expectedEvent.getToIndex(), 788 receivedEvent.getToIndex()); 789 assertSame( 790 "scrollable has incorrect value", 791 expectedEvent.isScrollable(), 792 receivedEvent.isScrollable()); 793 assertSame( 794 "granularity has incorrect value", 795 expectedEvent.getMovementGranularity(), 796 receivedEvent.getMovementGranularity()); 797 assertSame( 798 "action has incorrect value", expectedEvent.getAction(), receivedEvent.getAction()); 799 assertSame( 800 "windowChangeTypes has incorrect value", 801 expectedEvent.getWindowChanges(), 802 receivedEvent.getWindowChanges()); 803 assertEquals( 804 "speechStateChangeTypes has incorrect value,", 805 expectedEvent.getSpeechStateChangeTypes(), 806 receivedEvent.getSpeechStateChangeTypes()); 807 808 AccessibilityRecordTest.assertEqualsText(expectedEvent.getText(), receivedEvent.getText()); 809 AccessibilityRecordTest.assertEqualAccessibilityRecord(expectedEvent, receivedEvent); 810 811 assertEqualAppendedRecord(expectedEvent, receivedEvent); 812 } 813 assertEqualAppendedRecord( AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent)814 private static void assertEqualAppendedRecord( 815 AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent) { 816 assertEquals( 817 "recordCount has incorrect value", 818 expectedEvent.getRecordCount(), 819 receivedEvent.getRecordCount()); 820 if (expectedEvent.getRecordCount() != 0 && receivedEvent.getRecordCount() != 0) { 821 AccessibilityRecord expectedRecord = expectedEvent.getRecord(0); 822 AccessibilityRecord receivedRecord = receivedEvent.getRecord(0); 823 AccessibilityRecordTest.assertEqualAccessibilityRecord(expectedRecord, receivedRecord); 824 } 825 } 826 } 827