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