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