BrowserAccessibilityManager.java revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.content.browser.accessibility; 6 7import android.content.Context; 8import android.graphics.Rect; 9import android.os.Build; 10import android.os.Bundle; 11import android.text.SpannableString; 12import android.text.style.URLSpan; 13import android.view.MotionEvent; 14import android.view.View; 15import android.view.ViewParent; 16import android.view.accessibility.AccessibilityEvent; 17import android.view.accessibility.AccessibilityManager; 18import android.view.accessibility.AccessibilityNodeInfo; 19import android.view.accessibility.AccessibilityNodeProvider; 20 21import org.chromium.base.CalledByNative; 22import org.chromium.base.JNINamespace; 23import org.chromium.content.browser.ContentViewCore; 24import org.chromium.content.browser.RenderCoordinates; 25 26import java.util.ArrayList; 27import java.util.List; 28import java.util.Locale; 29 30/** 31 * Native accessibility for a {@link ContentViewCore}. 32 * 33 * This class is safe to load on ICS and can be used to run tests, but 34 * only the subclass, JellyBeanBrowserAccessibilityManager, actually 35 * has a AccessibilityNodeProvider implementation needed for native 36 * accessibility. 37 */ 38@JNINamespace("content") 39public class BrowserAccessibilityManager { 40 private static final String TAG = "BrowserAccessibilityManager"; 41 42 private ContentViewCore mContentViewCore; 43 private final AccessibilityManager mAccessibilityManager; 44 private final RenderCoordinates mRenderCoordinates; 45 private long mNativeObj; 46 private int mAccessibilityFocusId; 47 private int mCurrentHoverId; 48 private int mCurrentRootId; 49 private final int[] mTempLocation = new int[2]; 50 private final View mView; 51 private boolean mUserHasTouchExplored; 52 private boolean mPendingScrollToMakeNodeVisible; 53 private boolean mFrameInfoInitialized; 54 55 /** 56 * Create a BrowserAccessibilityManager object, which is owned by the C++ 57 * BrowserAccessibilityManagerAndroid instance, and connects to the content view. 58 * @param nativeBrowserAccessibilityManagerAndroid A pointer to the counterpart native 59 * C++ object that owns this object. 60 * @param contentViewCore The content view that this object provides accessibility for. 61 */ 62 @CalledByNative 63 private static BrowserAccessibilityManager create(long nativeBrowserAccessibilityManagerAndroid, 64 ContentViewCore contentViewCore) { 65 // A bug in the KitKat framework prevents us from using these new APIs. 66 // http://crbug.com/348088/ 67 // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 68 // return new KitKatBrowserAccessibilityManager( 69 // nativeBrowserAccessibilityManagerAndroid, contentViewCore); 70 71 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 72 return new JellyBeanBrowserAccessibilityManager( 73 nativeBrowserAccessibilityManagerAndroid, contentViewCore); 74 } else { 75 return new BrowserAccessibilityManager( 76 nativeBrowserAccessibilityManagerAndroid, contentViewCore); 77 } 78 } 79 80 protected BrowserAccessibilityManager(long nativeBrowserAccessibilityManagerAndroid, 81 ContentViewCore contentViewCore) { 82 mNativeObj = nativeBrowserAccessibilityManagerAndroid; 83 mContentViewCore = contentViewCore; 84 mContentViewCore.setBrowserAccessibilityManager(this); 85 mAccessibilityFocusId = View.NO_ID; 86 mCurrentHoverId = View.NO_ID; 87 mCurrentRootId = View.NO_ID; 88 mView = mContentViewCore.getContainerView(); 89 mRenderCoordinates = mContentViewCore.getRenderCoordinates(); 90 mAccessibilityManager = 91 (AccessibilityManager) mContentViewCore.getContext() 92 .getSystemService(Context.ACCESSIBILITY_SERVICE); 93 } 94 95 @CalledByNative 96 private void onNativeObjectDestroyed() { 97 if (mContentViewCore.getBrowserAccessibilityManager() == this) { 98 mContentViewCore.setBrowserAccessibilityManager(null); 99 } 100 mNativeObj = 0; 101 mContentViewCore = null; 102 } 103 104 /** 105 * @return An AccessibilityNodeProvider on JellyBean, and null on previous versions. 106 */ 107 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 108 return null; 109 } 110 111 /** 112 * @see AccessibilityNodeProvider#createAccessibilityNodeInfo(int) 113 */ 114 protected AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { 115 if (!mAccessibilityManager.isEnabled() || mNativeObj == 0) { 116 return null; 117 } 118 119 int rootId = nativeGetRootId(mNativeObj); 120 121 if (virtualViewId == View.NO_ID) { 122 return createNodeForHost(rootId); 123 } 124 125 if (!mFrameInfoInitialized) { 126 return null; 127 } 128 129 final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(mView); 130 info.setPackageName(mContentViewCore.getContext().getPackageName()); 131 info.setSource(mView, virtualViewId); 132 133 if (virtualViewId == rootId) { 134 info.setParent(mView); 135 } 136 137 if (nativePopulateAccessibilityNodeInfo(mNativeObj, info, virtualViewId)) { 138 return info; 139 } else { 140 info.recycle(); 141 return null; 142 } 143 } 144 145 /** 146 * @see AccessibilityNodeProvider#findAccessibilityNodeInfosByText(String, int) 147 */ 148 protected List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text, 149 int virtualViewId) { 150 return new ArrayList<AccessibilityNodeInfo>(); 151 } 152 153 /** 154 * @see AccessibilityNodeProvider#performAction(int, int, Bundle) 155 */ 156 protected boolean performAction(int virtualViewId, int action, Bundle arguments) { 157 // We don't support any actions on the host view or nodes 158 // that are not (any longer) in the tree. 159 if (!mAccessibilityManager.isEnabled() || mNativeObj == 0 160 || !nativeIsNodeValid(mNativeObj, virtualViewId)) { 161 return false; 162 } 163 164 switch (action) { 165 case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: 166 if (mAccessibilityFocusId == virtualViewId) { 167 return true; 168 } 169 170 mAccessibilityFocusId = virtualViewId; 171 sendAccessibilityEvent(mAccessibilityFocusId, 172 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 173 if (mCurrentHoverId == View.NO_ID) { 174 nativeScrollToMakeNodeVisible( 175 mNativeObj, mAccessibilityFocusId); 176 } else { 177 mPendingScrollToMakeNodeVisible = true; 178 } 179 return true; 180 case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: 181 if (mAccessibilityFocusId == virtualViewId) { 182 sendAccessibilityEvent(mAccessibilityFocusId, 183 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); 184 mAccessibilityFocusId = View.NO_ID; 185 } 186 return true; 187 case AccessibilityNodeInfo.ACTION_CLICK: 188 nativeClick(mNativeObj, virtualViewId); 189 sendAccessibilityEvent(virtualViewId, 190 AccessibilityEvent.TYPE_VIEW_CLICKED); 191 return true; 192 case AccessibilityNodeInfo.ACTION_FOCUS: 193 nativeFocus(mNativeObj, virtualViewId); 194 return true; 195 case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: 196 nativeBlur(mNativeObj); 197 return true; 198 199 case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT: { 200 if (arguments == null) 201 return false; 202 String elementType = arguments.getString( 203 AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING); 204 if (elementType == null) 205 return false; 206 elementType = elementType.toUpperCase(Locale.US); 207 return jumpToElementType(elementType, true); 208 } 209 case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: { 210 if (arguments == null) 211 return false; 212 String elementType = arguments.getString( 213 AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING); 214 if (elementType == null) 215 return false; 216 elementType = elementType.toUpperCase(Locale.US); 217 return jumpToElementType(elementType, false); 218 } 219 220 default: 221 break; 222 } 223 return false; 224 } 225 226 /** 227 * @see View#onHoverEvent(MotionEvent) 228 */ 229 public boolean onHoverEvent(MotionEvent event) { 230 if (!mAccessibilityManager.isEnabled() || mNativeObj == 0) { 231 return false; 232 } 233 234 if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { 235 if (mCurrentHoverId != View.NO_ID) { 236 sendAccessibilityEvent(mCurrentHoverId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); 237 mCurrentHoverId = View.NO_ID; 238 } 239 if (mPendingScrollToMakeNodeVisible) { 240 nativeScrollToMakeNodeVisible( 241 mNativeObj, mAccessibilityFocusId); 242 } 243 mPendingScrollToMakeNodeVisible = false; 244 return true; 245 } 246 247 mUserHasTouchExplored = true; 248 float x = event.getX(); 249 float y = event.getY(); 250 251 // Convert to CSS coordinates. 252 int cssX = (int) (mRenderCoordinates.fromPixToLocalCss(x) + 253 mRenderCoordinates.getScrollX()); 254 int cssY = (int) (mRenderCoordinates.fromPixToLocalCss(y) + 255 mRenderCoordinates.getScrollY()); 256 int id = nativeHitTest(mNativeObj, cssX, cssY); 257 if (mCurrentHoverId != id) { 258 sendAccessibilityEvent(mCurrentHoverId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); 259 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); 260 mCurrentHoverId = id; 261 } 262 263 return true; 264 } 265 266 /** 267 * Called by ContentViewCore to notify us when the frame info is initialized, 268 * the first time, since until that point, we can't use mRenderCoordinates to transform 269 * web coordinates to screen coordinates. 270 */ 271 public void notifyFrameInfoInitialized() { 272 if (mFrameInfoInitialized) return; 273 274 mFrameInfoInitialized = true; 275 // Invalidate the host, since the chrome accessibility tree is now 276 // ready and listed as the child of the host. 277 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 278 279 // (Re-) focus focused element, since we weren't able to create an 280 // AccessibilityNodeInfo for this element before. 281 if (mAccessibilityFocusId != View.NO_ID) { 282 sendAccessibilityEvent(mAccessibilityFocusId, 283 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 284 } 285 } 286 287 private boolean jumpToElementType(String elementType, boolean forwards) { 288 int id = nativeFindElementType(mNativeObj, mAccessibilityFocusId, elementType, forwards); 289 if (id == 0) 290 return false; 291 292 mAccessibilityFocusId = id; 293 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 294 return true; 295 } 296 297 private void sendAccessibilityEvent(int virtualViewId, int eventType) { 298 // If mFrameInfoInitialized is false, then the virtual hierarchy 299 // doesn't exist in the view of the Android framework, so should 300 // never send any events. 301 if (!mAccessibilityManager.isEnabled() || mNativeObj == 0 302 || !mFrameInfoInitialized) { 303 return; 304 } 305 306 final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); 307 event.setPackageName(mContentViewCore.getContext().getPackageName()); 308 event.setSource(mView, virtualViewId); 309 if (!nativePopulateAccessibilityEvent(mNativeObj, event, virtualViewId, eventType)) { 310 event.recycle(); 311 return; 312 } 313 314 // This is currently needed if we want Android to draw the yellow box around 315 // the item that has accessibility focus. In practice, this doesn't seem to slow 316 // things down, because it's only called when the accessibility focus moves. 317 // TODO(dmazzoni): remove this if/when Android framework fixes bug. 318 mContentViewCore.getContainerView().postInvalidate(); 319 320 mContentViewCore.getContainerView().requestSendAccessibilityEvent(mView, event); 321 } 322 323 private Bundle getOrCreateBundleForAccessibilityEvent(AccessibilityEvent event) { 324 Bundle bundle = (Bundle) event.getParcelableData(); 325 if (bundle == null) { 326 bundle = new Bundle(); 327 event.setParcelableData(bundle); 328 } 329 return bundle; 330 } 331 332 private AccessibilityNodeInfo createNodeForHost(int rootId) { 333 // Since we don't want the parent to be focusable, but we can't remove 334 // actions from a node, copy over the necessary fields. 335 final AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(mView); 336 final AccessibilityNodeInfo source = AccessibilityNodeInfo.obtain(mView); 337 mView.onInitializeAccessibilityNodeInfo(source); 338 339 // Copy over parent and screen bounds. 340 Rect rect = new Rect(); 341 source.getBoundsInParent(rect); 342 result.setBoundsInParent(rect); 343 source.getBoundsInScreen(rect); 344 result.setBoundsInScreen(rect); 345 346 // Set up the parent view, if applicable. 347 final ViewParent parent = mView.getParentForAccessibility(); 348 if (parent instanceof View) { 349 result.setParent((View) parent); 350 } 351 352 // Populate the minimum required fields. 353 result.setVisibleToUser(source.isVisibleToUser()); 354 result.setEnabled(source.isEnabled()); 355 result.setPackageName(source.getPackageName()); 356 result.setClassName(source.getClassName()); 357 358 // Add the Chrome root node. 359 if (mFrameInfoInitialized) { 360 result.addChild(mView, rootId); 361 } 362 363 return result; 364 } 365 366 @CalledByNative 367 private void handlePageLoaded(int id) { 368 if (mUserHasTouchExplored) return; 369 370 mAccessibilityFocusId = id; 371 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 372 } 373 374 @CalledByNative 375 private void handleFocusChanged(int id) { 376 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_FOCUSED); 377 378 // Update accessibility focus if not already set to this node. 379 if (mAccessibilityFocusId != id) { 380 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 381 mAccessibilityFocusId = id; 382 } 383 } 384 385 @CalledByNative 386 private void handleCheckStateChanged(int id) { 387 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_CLICKED); 388 } 389 390 @CalledByNative 391 private void handleTextSelectionChanged(int id) { 392 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); 393 } 394 395 @CalledByNative 396 private void handleEditableTextChanged(int id) { 397 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); 398 } 399 400 @CalledByNative 401 private void handleContentChanged(int id) { 402 int rootId = nativeGetRootId(mNativeObj); 403 if (rootId != mCurrentRootId) { 404 mCurrentRootId = rootId; 405 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 406 } else { 407 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 408 } 409 } 410 411 @CalledByNative 412 private void handleNavigate() { 413 mAccessibilityFocusId = View.NO_ID; 414 mUserHasTouchExplored = false; 415 mFrameInfoInitialized = false; 416 // Invalidate the host, since its child is now gone. 417 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 418 } 419 420 @CalledByNative 421 private void handleScrollPositionChanged(int id) { 422 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_SCROLLED); 423 } 424 425 @CalledByNative 426 private void handleScrolledToAnchor(int id) { 427 if (mAccessibilityFocusId == id) { 428 return; 429 } 430 431 mAccessibilityFocusId = id; 432 sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 433 } 434 435 @CalledByNative 436 private void announceLiveRegionText(String text) { 437 mView.announceForAccessibility(text); 438 } 439 440 @CalledByNative 441 private void setAccessibilityNodeInfoParent(AccessibilityNodeInfo node, int parentId) { 442 node.setParent(mView, parentId); 443 } 444 445 @CalledByNative 446 private void addAccessibilityNodeInfoChild(AccessibilityNodeInfo node, int childId) { 447 node.addChild(mView, childId); 448 } 449 450 @CalledByNative 451 private void setAccessibilityNodeInfoBooleanAttributes(AccessibilityNodeInfo node, 452 int virtualViewId, boolean checkable, boolean checked, boolean clickable, 453 boolean enabled, boolean focusable, boolean focused, boolean password, 454 boolean scrollable, boolean selected, boolean visibleToUser) { 455 node.setCheckable(checkable); 456 node.setChecked(checked); 457 node.setClickable(clickable); 458 node.setEnabled(enabled); 459 node.setFocusable(focusable); 460 node.setFocused(focused); 461 node.setPassword(password); 462 node.setScrollable(scrollable); 463 node.setSelected(selected); 464 node.setVisibleToUser(visibleToUser); 465 466 node.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT); 467 node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT); 468 469 if (focusable) { 470 if (focused) { 471 node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS); 472 } else { 473 node.addAction(AccessibilityNodeInfo.ACTION_FOCUS); 474 } 475 } 476 477 if (mAccessibilityFocusId == virtualViewId) { 478 node.setAccessibilityFocused(true); 479 node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); 480 } else { 481 node.setAccessibilityFocused(false); 482 node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 483 } 484 485 if (clickable) { 486 node.addAction(AccessibilityNodeInfo.ACTION_CLICK); 487 } 488 } 489 490 @CalledByNative 491 private void setAccessibilityNodeInfoClassName(AccessibilityNodeInfo node, 492 String className) { 493 node.setClassName(className); 494 } 495 496 @CalledByNative 497 private void setAccessibilityNodeInfoContentDescription( 498 AccessibilityNodeInfo node, String contentDescription, boolean annotateAsLink) { 499 if (annotateAsLink) { 500 SpannableString spannable = new SpannableString(contentDescription); 501 spannable.setSpan(new URLSpan(""), 0, spannable.length(), 0); 502 node.setContentDescription(spannable); 503 } else { 504 node.setContentDescription(contentDescription); 505 } 506 } 507 508 @CalledByNative 509 private void setAccessibilityNodeInfoLocation(AccessibilityNodeInfo node, 510 int absoluteLeft, int absoluteTop, int parentRelativeLeft, int parentRelativeTop, 511 int width, int height, boolean isRootNode) { 512 // First set the bounds in parent. 513 Rect boundsInParent = new Rect(parentRelativeLeft, parentRelativeTop, 514 parentRelativeLeft + width, parentRelativeTop + height); 515 if (isRootNode) { 516 // Offset of the web content relative to the View. 517 boundsInParent.offset(0, (int) mRenderCoordinates.getContentOffsetYPix()); 518 } 519 node.setBoundsInParent(boundsInParent); 520 521 // Now set the absolute rect, which requires several transformations. 522 Rect rect = new Rect(absoluteLeft, absoluteTop, absoluteLeft + width, absoluteTop + height); 523 524 // Offset by the scroll position. 525 rect.offset(-(int) mRenderCoordinates.getScrollX(), 526 -(int) mRenderCoordinates.getScrollY()); 527 528 // Convert CSS (web) pixels to Android View pixels 529 rect.left = (int) mRenderCoordinates.fromLocalCssToPix(rect.left); 530 rect.top = (int) mRenderCoordinates.fromLocalCssToPix(rect.top); 531 rect.bottom = (int) mRenderCoordinates.fromLocalCssToPix(rect.bottom); 532 rect.right = (int) mRenderCoordinates.fromLocalCssToPix(rect.right); 533 534 // Offset by the location of the web content within the view. 535 rect.offset(0, 536 (int) mRenderCoordinates.getContentOffsetYPix()); 537 538 // Finally offset by the location of the view within the screen. 539 final int[] viewLocation = new int[2]; 540 mView.getLocationOnScreen(viewLocation); 541 rect.offset(viewLocation[0], viewLocation[1]); 542 543 node.setBoundsInScreen(rect); 544 } 545 546 @CalledByNative 547 protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node, 548 boolean canOpenPopup, 549 boolean contentInvalid, 550 boolean dismissable, 551 boolean multiLine, 552 int inputType, 553 int liveRegion) { 554 // Requires KitKat or higher. 555 } 556 557 @CalledByNative 558 protected void setAccessibilityNodeInfoCollectionInfo(AccessibilityNodeInfo node, 559 int rowCount, int columnCount, boolean hierarchical) { 560 // Requires KitKat or higher. 561 } 562 563 @CalledByNative 564 protected void setAccessibilityNodeInfoCollectionItemInfo(AccessibilityNodeInfo node, 565 int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading) { 566 // Requires KitKat or higher. 567 } 568 569 @CalledByNative 570 protected void setAccessibilityNodeInfoRangeInfo(AccessibilityNodeInfo node, 571 int rangeType, float min, float max, float current) { 572 // Requires KitKat or higher. 573 } 574 575 @CalledByNative 576 private void setAccessibilityEventBooleanAttributes(AccessibilityEvent event, 577 boolean checked, boolean enabled, boolean password, boolean scrollable) { 578 event.setChecked(checked); 579 event.setEnabled(enabled); 580 event.setPassword(password); 581 event.setScrollable(scrollable); 582 } 583 584 @CalledByNative 585 private void setAccessibilityEventClassName(AccessibilityEvent event, String className) { 586 event.setClassName(className); 587 } 588 589 @CalledByNative 590 private void setAccessibilityEventListAttributes(AccessibilityEvent event, 591 int currentItemIndex, int itemCount) { 592 event.setCurrentItemIndex(currentItemIndex); 593 event.setItemCount(itemCount); 594 } 595 596 @CalledByNative 597 private void setAccessibilityEventScrollAttributes(AccessibilityEvent event, 598 int scrollX, int scrollY, int maxScrollX, int maxScrollY) { 599 event.setScrollX(scrollX); 600 event.setScrollY(scrollY); 601 event.setMaxScrollX(maxScrollX); 602 event.setMaxScrollY(maxScrollY); 603 } 604 605 @CalledByNative 606 private void setAccessibilityEventTextChangedAttrs(AccessibilityEvent event, 607 int fromIndex, int addedCount, int removedCount, String beforeText, String text) { 608 event.setFromIndex(fromIndex); 609 event.setAddedCount(addedCount); 610 event.setRemovedCount(removedCount); 611 event.setBeforeText(beforeText); 612 event.getText().add(text); 613 } 614 615 @CalledByNative 616 private void setAccessibilityEventSelectionAttrs(AccessibilityEvent event, 617 int fromIndex, int addedCount, int itemCount, String text) { 618 event.setFromIndex(fromIndex); 619 event.setAddedCount(addedCount); 620 event.setItemCount(itemCount); 621 event.getText().add(text); 622 } 623 624 @CalledByNative 625 protected void setAccessibilityEventKitKatAttributes(AccessibilityEvent event, 626 boolean canOpenPopup, 627 boolean contentInvalid, 628 boolean dismissable, 629 boolean multiLine, 630 int inputType, 631 int liveRegion) { 632 // Backwards compatibility for KitKat AccessibilityNodeInfo fields. 633 Bundle bundle = getOrCreateBundleForAccessibilityEvent(event); 634 bundle.putBoolean("AccessibilityNodeInfo.canOpenPopup", canOpenPopup); 635 bundle.putBoolean("AccessibilityNodeInfo.contentInvalid", contentInvalid); 636 bundle.putBoolean("AccessibilityNodeInfo.dismissable", dismissable); 637 bundle.putBoolean("AccessibilityNodeInfo.multiLine", multiLine); 638 bundle.putInt("AccessibilityNodeInfo.inputType", inputType); 639 bundle.putInt("AccessibilityNodeInfo.liveRegion", liveRegion); 640 } 641 642 @CalledByNative 643 protected void setAccessibilityEventCollectionInfo(AccessibilityEvent event, 644 int rowCount, int columnCount, boolean hierarchical) { 645 // Backwards compatibility for KitKat AccessibilityNodeInfo fields. 646 Bundle bundle = getOrCreateBundleForAccessibilityEvent(event); 647 bundle.putInt("AccessibilityNodeInfo.CollectionInfo.rowCount", rowCount); 648 bundle.putInt("AccessibilityNodeInfo.CollectionInfo.columnCount", columnCount); 649 bundle.putBoolean("AccessibilityNodeInfo.CollectionInfo.hierarchical", hierarchical); 650 } 651 652 @CalledByNative 653 protected void setAccessibilityEventCollectionItemInfo(AccessibilityEvent event, 654 int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading) { 655 // Backwards compatibility for KitKat AccessibilityNodeInfo fields. 656 Bundle bundle = getOrCreateBundleForAccessibilityEvent(event); 657 bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.rowIndex", rowIndex); 658 bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.rowSpan", rowSpan); 659 bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.columnIndex", columnIndex); 660 bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.columnSpan", columnSpan); 661 bundle.putBoolean("AccessibilityNodeInfo.CollectionItemInfo.heading", heading); 662 } 663 664 @CalledByNative 665 protected void setAccessibilityEventRangeInfo(AccessibilityEvent event, 666 int rangeType, float min, float max, float current) { 667 // Backwards compatibility for KitKat AccessibilityNodeInfo fields. 668 Bundle bundle = getOrCreateBundleForAccessibilityEvent(event); 669 bundle.putInt("AccessibilityNodeInfo.RangeInfo.type", rangeType); 670 bundle.putFloat("AccessibilityNodeInfo.RangeInfo.min", min); 671 bundle.putFloat("AccessibilityNodeInfo.RangeInfo.max", max); 672 bundle.putFloat("AccessibilityNodeInfo.RangeInfo.current", current); 673 } 674 675 private native int nativeGetRootId(long nativeBrowserAccessibilityManagerAndroid); 676 private native boolean nativeIsNodeValid(long nativeBrowserAccessibilityManagerAndroid, int id); 677 private native int nativeHitTest(long nativeBrowserAccessibilityManagerAndroid, int x, int y); 678 private native boolean nativePopulateAccessibilityNodeInfo( 679 long nativeBrowserAccessibilityManagerAndroid, AccessibilityNodeInfo info, int id); 680 private native boolean nativePopulateAccessibilityEvent( 681 long nativeBrowserAccessibilityManagerAndroid, AccessibilityEvent event, int id, 682 int eventType); 683 private native void nativeClick(long nativeBrowserAccessibilityManagerAndroid, int id); 684 private native void nativeFocus(long nativeBrowserAccessibilityManagerAndroid, int id); 685 private native void nativeBlur(long nativeBrowserAccessibilityManagerAndroid); 686 private native void nativeScrollToMakeNodeVisible( 687 long nativeBrowserAccessibilityManagerAndroid, int id); 688 private native int nativeFindElementType(long nativeBrowserAccessibilityManagerAndroid, 689 int startId, String elementType, boolean forwards); 690} 691