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