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