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