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