• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ViewGroup;
16 import android.view.ViewParent;
17 import android.view.accessibility.AccessibilityEvent;
18 import android.view.accessibility.AccessibilityManager;
19 import android.view.accessibility.AccessibilityNodeInfo;
20 import android.view.accessibility.AccessibilityNodeProvider;
21 
22 import org.chromium.base.CalledByNative;
23 import org.chromium.base.JNINamespace;
24 import org.chromium.content.browser.ContentViewCore;
25 import org.chromium.content.browser.RenderCoordinates;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 import 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")
40 public 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
create(long nativeBrowserAccessibilityManagerAndroid, ContentViewCore contentViewCore)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 
BrowserAccessibilityManager(long nativeBrowserAccessibilityManagerAndroid, ContentViewCore contentViewCore)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
onNativeObjectDestroyed()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      */
getAccessibilityNodeProvider()108     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
109         return null;
110     }
111 
112     /**
113      * @see AccessibilityNodeProvider#createAccessibilityNodeInfo(int)
114      */
createAccessibilityNodeInfo(int virtualViewId)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      */
findAccessibilityNodeInfosByText(String text, int virtualViewId)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      */
performAction(int virtualViewId, int action, Bundle arguments)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      */
onHoverEvent(MotionEvent event)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         int cssY = (int) (mRenderCoordinates.fromPixToLocalCss(y));
253 
254         // This sends an IPC to the render process to do the hit testing.
255         // The response is handled by handleHover.
256         nativeHitTest(mNativeObj, cssX, cssY);
257         return true;
258     }
259 
260     /**
261      * Called by ContentViewCore to notify us when the frame info is initialized,
262      * the first time, since until that point, we can't use mRenderCoordinates to transform
263      * web coordinates to screen coordinates.
264      */
notifyFrameInfoInitialized()265     public void notifyFrameInfoInitialized() {
266         // Invalidate the container view, since the chrome accessibility tree is now
267         // ready and listed as the child of the container view.
268         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
269 
270         // (Re-) focus focused element, since we weren't able to create an
271         // AccessibilityNodeInfo for this element before.
272         if (mAccessibilityFocusId != View.NO_ID) {
273             sendAccessibilityEvent(mAccessibilityFocusId,
274                                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
275         }
276     }
277 
jumpToElementType(String elementType, boolean forwards)278     private boolean jumpToElementType(String elementType, boolean forwards) {
279         int id = nativeFindElementType(mNativeObj, mAccessibilityFocusId, elementType, forwards);
280         if (id == 0)
281             return false;
282 
283         mAccessibilityFocusId = id;
284         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
285         return true;
286     }
287 
sendAccessibilityEvent(int virtualViewId, int eventType)288     private void sendAccessibilityEvent(int virtualViewId, int eventType) {
289         // If we don't have any frame info, then the virtual hierarchy
290         // doesn't exist in the view of the Android framework, so should
291         // never send any events.
292         if (!mAccessibilityManager.isEnabled() || mNativeObj == 0
293                 || !isFrameInfoInitialized()) {
294             return;
295         }
296 
297         // This is currently needed if we want Android to draw the yellow box around
298         // the item that has accessibility focus. In practice, this doesn't seem to slow
299         // things down, because it's only called when the accessibility focus moves.
300         // TODO(dmazzoni): remove this if/when Android framework fixes bug.
301         mView.postInvalidate();
302 
303         // The container view is indicated by a virtualViewId of NO_ID; post these events directly
304         // since there's no web-specific information to attach.
305         if (virtualViewId == View.NO_ID) {
306             mView.sendAccessibilityEvent(eventType);
307             return;
308         }
309 
310         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
311         event.setPackageName(mContentViewCore.getContext().getPackageName());
312         event.setSource(mView, virtualViewId);
313         if (!nativePopulateAccessibilityEvent(mNativeObj, event, virtualViewId, eventType)) {
314             event.recycle();
315             return;
316         }
317 
318         mView.requestSendAccessibilityEvent(mView, event);
319     }
320 
getOrCreateBundleForAccessibilityEvent(AccessibilityEvent event)321     private Bundle getOrCreateBundleForAccessibilityEvent(AccessibilityEvent event) {
322         Bundle bundle = (Bundle) event.getParcelableData();
323         if (bundle == null) {
324             bundle = new Bundle();
325             event.setParcelableData(bundle);
326         }
327         return bundle;
328     }
329 
createNodeForHost(int rootId)330     private AccessibilityNodeInfo createNodeForHost(int rootId) {
331         // Since we don't want the parent to be focusable, but we can't remove
332         // actions from a node, copy over the necessary fields.
333         final AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(mView);
334         final AccessibilityNodeInfo source = AccessibilityNodeInfo.obtain(mView);
335         mView.onInitializeAccessibilityNodeInfo(source);
336 
337         // Copy over parent and screen bounds.
338         Rect rect = new Rect();
339         source.getBoundsInParent(rect);
340         result.setBoundsInParent(rect);
341         source.getBoundsInScreen(rect);
342         result.setBoundsInScreen(rect);
343 
344         // Set up the parent view, if applicable.
345         final ViewParent parent = mView.getParentForAccessibility();
346         if (parent instanceof View) {
347             result.setParent((View) parent);
348         }
349 
350         // Populate the minimum required fields.
351         result.setVisibleToUser(source.isVisibleToUser());
352         result.setEnabled(source.isEnabled());
353         result.setPackageName(source.getPackageName());
354         result.setClassName(source.getClassName());
355 
356         // Add the Chrome root node.
357         if (isFrameInfoInitialized()) {
358             result.addChild(mView, rootId);
359         }
360 
361         return result;
362     }
363 
isFrameInfoInitialized()364     private boolean isFrameInfoInitialized() {
365         return mRenderCoordinates.getContentWidthCss() != 0.0 ||
366                mRenderCoordinates.getContentHeightCss() != 0.0;
367     }
368 
369     @CalledByNative
handlePageLoaded(int id)370     private void handlePageLoaded(int id) {
371         if (mUserHasTouchExplored) return;
372 
373         if (mContentViewCore.shouldSetAccessibilityFocusOnPageLoad()) {
374             mAccessibilityFocusId = id;
375             sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
376         }
377     }
378 
379     @CalledByNative
handleFocusChanged(int id)380     private void handleFocusChanged(int id) {
381         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_FOCUSED);
382 
383         // Update accessibility focus if not already set to this node.
384         if (mAccessibilityFocusId != id) {
385             sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
386             mAccessibilityFocusId = id;
387         }
388     }
389 
390     @CalledByNative
handleCheckStateChanged(int id)391     private void handleCheckStateChanged(int id) {
392         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
393     }
394 
395     @CalledByNative
handleTextSelectionChanged(int id)396     private void handleTextSelectionChanged(int id) {
397         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
398     }
399 
400     @CalledByNative
handleEditableTextChanged(int id)401     private void handleEditableTextChanged(int id) {
402         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
403     }
404 
405     @CalledByNative
handleContentChanged(int id)406     private void handleContentChanged(int id) {
407         int rootId = nativeGetRootId(mNativeObj);
408         if (rootId != mCurrentRootId) {
409             mCurrentRootId = rootId;
410             mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
411         } else {
412             sendAccessibilityEvent(id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
413         }
414     }
415 
416     @CalledByNative
handleNavigate()417     private void handleNavigate() {
418         mAccessibilityFocusId = View.NO_ID;
419         mUserHasTouchExplored = false;
420         // Invalidate the host, since its child is now gone.
421         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
422     }
423 
424     @CalledByNative
handleScrollPositionChanged(int id)425     private void handleScrollPositionChanged(int id) {
426         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_SCROLLED);
427     }
428 
429     @CalledByNative
handleScrolledToAnchor(int id)430     private void handleScrolledToAnchor(int id) {
431         if (mAccessibilityFocusId == id) {
432             return;
433         }
434 
435         mAccessibilityFocusId = id;
436         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
437     }
438 
439     @CalledByNative
handleHover(int id)440     private void handleHover(int id) {
441         if (mLastHoverId == id) return;
442 
443         // Always send the ENTER and then the EXIT event, to match a standard Android View.
444         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
445         sendAccessibilityEvent(mLastHoverId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
446         mLastHoverId = id;
447     }
448 
449     @CalledByNative
announceLiveRegionText(String text)450     private void announceLiveRegionText(String text) {
451         mView.announceForAccessibility(text);
452     }
453 
454     @CalledByNative
setAccessibilityNodeInfoParent(AccessibilityNodeInfo node, int parentId)455     private void setAccessibilityNodeInfoParent(AccessibilityNodeInfo node, int parentId) {
456         node.setParent(mView, parentId);
457     }
458 
459     @CalledByNative
addAccessibilityNodeInfoChild(AccessibilityNodeInfo node, int childId)460     private void addAccessibilityNodeInfoChild(AccessibilityNodeInfo node, int childId) {
461         node.addChild(mView, childId);
462     }
463 
464     @CalledByNative
setAccessibilityNodeInfoBooleanAttributes(AccessibilityNodeInfo node, int virtualViewId, boolean checkable, boolean checked, boolean clickable, boolean enabled, boolean focusable, boolean focused, boolean password, boolean scrollable, boolean selected, boolean visibleToUser)465     private void setAccessibilityNodeInfoBooleanAttributes(AccessibilityNodeInfo node,
466             int virtualViewId, boolean checkable, boolean checked, boolean clickable,
467             boolean enabled, boolean focusable, boolean focused, boolean password,
468             boolean scrollable, boolean selected, boolean visibleToUser) {
469         node.setCheckable(checkable);
470         node.setChecked(checked);
471         node.setClickable(clickable);
472         node.setEnabled(enabled);
473         node.setFocusable(focusable);
474         node.setFocused(focused);
475         node.setPassword(password);
476         node.setScrollable(scrollable);
477         node.setSelected(selected);
478         node.setVisibleToUser(visibleToUser);
479 
480         node.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
481         node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
482 
483         if (focusable) {
484             if (focused) {
485                 node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
486             } else {
487                 node.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
488             }
489         }
490 
491         if (mAccessibilityFocusId == virtualViewId) {
492             node.setAccessibilityFocused(true);
493             node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
494         } else {
495             node.setAccessibilityFocused(false);
496             node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
497         }
498 
499         if (clickable) {
500             node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
501         }
502     }
503 
504     @CalledByNative
setAccessibilityNodeInfoClassName(AccessibilityNodeInfo node, String className)505     private void setAccessibilityNodeInfoClassName(AccessibilityNodeInfo node,
506             String className) {
507         node.setClassName(className);
508     }
509 
510     @CalledByNative
setAccessibilityNodeInfoContentDescription( AccessibilityNodeInfo node, String contentDescription, boolean annotateAsLink)511     private void setAccessibilityNodeInfoContentDescription(
512             AccessibilityNodeInfo node, String contentDescription, boolean annotateAsLink) {
513         if (annotateAsLink) {
514             SpannableString spannable = new SpannableString(contentDescription);
515             spannable.setSpan(new URLSpan(""), 0, spannable.length(), 0);
516             node.setContentDescription(spannable);
517         } else {
518             node.setContentDescription(contentDescription);
519         }
520     }
521 
522     @CalledByNative
setAccessibilityNodeInfoLocation(AccessibilityNodeInfo node, int absoluteLeft, int absoluteTop, int parentRelativeLeft, int parentRelativeTop, int width, int height, boolean isRootNode)523     private void setAccessibilityNodeInfoLocation(AccessibilityNodeInfo node,
524             int absoluteLeft, int absoluteTop, int parentRelativeLeft, int parentRelativeTop,
525             int width, int height, boolean isRootNode) {
526         // First set the bounds in parent.
527         Rect boundsInParent = new Rect(parentRelativeLeft, parentRelativeTop,
528                 parentRelativeLeft + width, parentRelativeTop + height);
529         if (isRootNode) {
530             // Offset of the web content relative to the View.
531             boundsInParent.offset(0, (int) mRenderCoordinates.getContentOffsetYPix());
532         }
533         node.setBoundsInParent(boundsInParent);
534 
535         // Now set the absolute rect, which requires several transformations.
536         Rect rect = new Rect(absoluteLeft, absoluteTop, absoluteLeft + width, absoluteTop + height);
537 
538         // Offset by the scroll position.
539         rect.offset(-(int) mRenderCoordinates.getScrollX(),
540                     -(int) mRenderCoordinates.getScrollY());
541 
542         // Convert CSS (web) pixels to Android View pixels
543         rect.left = (int) mRenderCoordinates.fromLocalCssToPix(rect.left);
544         rect.top = (int) mRenderCoordinates.fromLocalCssToPix(rect.top);
545         rect.bottom = (int) mRenderCoordinates.fromLocalCssToPix(rect.bottom);
546         rect.right = (int) mRenderCoordinates.fromLocalCssToPix(rect.right);
547 
548         // Offset by the location of the web content within the view.
549         rect.offset(0,
550                     (int) mRenderCoordinates.getContentOffsetYPix());
551 
552         // Finally offset by the location of the view within the screen.
553         final int[] viewLocation = new int[2];
554         mView.getLocationOnScreen(viewLocation);
555         rect.offset(viewLocation[0], viewLocation[1]);
556 
557         node.setBoundsInScreen(rect);
558     }
559 
560     @CalledByNative
setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node, boolean canOpenPopup, boolean contentInvalid, boolean dismissable, boolean multiLine, int inputType, int liveRegion)561     protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
562             boolean canOpenPopup,
563             boolean contentInvalid,
564             boolean dismissable,
565             boolean multiLine,
566             int inputType,
567             int liveRegion) {
568         // Requires KitKat or higher.
569     }
570 
571     @CalledByNative
setAccessibilityNodeInfoCollectionInfo(AccessibilityNodeInfo node, int rowCount, int columnCount, boolean hierarchical)572     protected void setAccessibilityNodeInfoCollectionInfo(AccessibilityNodeInfo node,
573             int rowCount, int columnCount, boolean hierarchical) {
574         // Requires KitKat or higher.
575     }
576 
577     @CalledByNative
setAccessibilityNodeInfoCollectionItemInfo(AccessibilityNodeInfo node, int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading)578     protected void setAccessibilityNodeInfoCollectionItemInfo(AccessibilityNodeInfo node,
579             int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading) {
580         // Requires KitKat or higher.
581     }
582 
583     @CalledByNative
setAccessibilityNodeInfoRangeInfo(AccessibilityNodeInfo node, int rangeType, float min, float max, float current)584     protected void setAccessibilityNodeInfoRangeInfo(AccessibilityNodeInfo node,
585             int rangeType, float min, float max, float current) {
586         // Requires KitKat or higher.
587     }
588 
589     @CalledByNative
setAccessibilityEventBooleanAttributes(AccessibilityEvent event, boolean checked, boolean enabled, boolean password, boolean scrollable)590     private void setAccessibilityEventBooleanAttributes(AccessibilityEvent event,
591             boolean checked, boolean enabled, boolean password, boolean scrollable) {
592         event.setChecked(checked);
593         event.setEnabled(enabled);
594         event.setPassword(password);
595         event.setScrollable(scrollable);
596     }
597 
598     @CalledByNative
setAccessibilityEventClassName(AccessibilityEvent event, String className)599     private void setAccessibilityEventClassName(AccessibilityEvent event, String className) {
600         event.setClassName(className);
601     }
602 
603     @CalledByNative
setAccessibilityEventListAttributes(AccessibilityEvent event, int currentItemIndex, int itemCount)604     private void setAccessibilityEventListAttributes(AccessibilityEvent event,
605             int currentItemIndex, int itemCount) {
606         event.setCurrentItemIndex(currentItemIndex);
607         event.setItemCount(itemCount);
608     }
609 
610     @CalledByNative
setAccessibilityEventScrollAttributes(AccessibilityEvent event, int scrollX, int scrollY, int maxScrollX, int maxScrollY)611     private void setAccessibilityEventScrollAttributes(AccessibilityEvent event,
612             int scrollX, int scrollY, int maxScrollX, int maxScrollY) {
613         event.setScrollX(scrollX);
614         event.setScrollY(scrollY);
615         event.setMaxScrollX(maxScrollX);
616         event.setMaxScrollY(maxScrollY);
617     }
618 
619     @CalledByNative
setAccessibilityEventTextChangedAttrs(AccessibilityEvent event, int fromIndex, int addedCount, int removedCount, String beforeText, String text)620     private void setAccessibilityEventTextChangedAttrs(AccessibilityEvent event,
621             int fromIndex, int addedCount, int removedCount, String beforeText, String text) {
622         event.setFromIndex(fromIndex);
623         event.setAddedCount(addedCount);
624         event.setRemovedCount(removedCount);
625         event.setBeforeText(beforeText);
626         event.getText().add(text);
627     }
628 
629     @CalledByNative
setAccessibilityEventSelectionAttrs(AccessibilityEvent event, int fromIndex, int addedCount, int itemCount, String text)630     private void setAccessibilityEventSelectionAttrs(AccessibilityEvent event,
631             int fromIndex, int addedCount, int itemCount, String text) {
632         event.setFromIndex(fromIndex);
633         event.setAddedCount(addedCount);
634         event.setItemCount(itemCount);
635         event.getText().add(text);
636     }
637 
638     @CalledByNative
setAccessibilityEventKitKatAttributes(AccessibilityEvent event, boolean canOpenPopup, boolean contentInvalid, boolean dismissable, boolean multiLine, int inputType, int liveRegion)639     protected void setAccessibilityEventKitKatAttributes(AccessibilityEvent event,
640             boolean canOpenPopup,
641             boolean contentInvalid,
642             boolean dismissable,
643             boolean multiLine,
644             int inputType,
645             int liveRegion) {
646         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
647         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
648         bundle.putBoolean("AccessibilityNodeInfo.canOpenPopup", canOpenPopup);
649         bundle.putBoolean("AccessibilityNodeInfo.contentInvalid", contentInvalid);
650         bundle.putBoolean("AccessibilityNodeInfo.dismissable", dismissable);
651         bundle.putBoolean("AccessibilityNodeInfo.multiLine", multiLine);
652         bundle.putInt("AccessibilityNodeInfo.inputType", inputType);
653         bundle.putInt("AccessibilityNodeInfo.liveRegion", liveRegion);
654     }
655 
656     @CalledByNative
setAccessibilityEventCollectionInfo(AccessibilityEvent event, int rowCount, int columnCount, boolean hierarchical)657     protected void setAccessibilityEventCollectionInfo(AccessibilityEvent event,
658             int rowCount, int columnCount, boolean hierarchical) {
659         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
660         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
661         bundle.putInt("AccessibilityNodeInfo.CollectionInfo.rowCount", rowCount);
662         bundle.putInt("AccessibilityNodeInfo.CollectionInfo.columnCount", columnCount);
663         bundle.putBoolean("AccessibilityNodeInfo.CollectionInfo.hierarchical", hierarchical);
664     }
665 
666     @CalledByNative
setAccessibilityEventHeadingFlag(AccessibilityEvent event, boolean heading)667     protected void setAccessibilityEventHeadingFlag(AccessibilityEvent event,
668             boolean heading) {
669         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
670         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
671         bundle.putBoolean("AccessibilityNodeInfo.CollectionItemInfo.heading", heading);
672     }
673 
674     @CalledByNative
setAccessibilityEventCollectionItemInfo(AccessibilityEvent event, int rowIndex, int rowSpan, int columnIndex, int columnSpan)675     protected void setAccessibilityEventCollectionItemInfo(AccessibilityEvent event,
676             int rowIndex, int rowSpan, int columnIndex, int columnSpan) {
677         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
678         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
679         bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.rowIndex", rowIndex);
680         bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.rowSpan", rowSpan);
681         bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.columnIndex", columnIndex);
682         bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.columnSpan", columnSpan);
683     }
684 
685     @CalledByNative
setAccessibilityEventRangeInfo(AccessibilityEvent event, int rangeType, float min, float max, float current)686     protected void setAccessibilityEventRangeInfo(AccessibilityEvent event,
687             int rangeType, float min, float max, float current) {
688         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
689         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
690         bundle.putInt("AccessibilityNodeInfo.RangeInfo.type", rangeType);
691         bundle.putFloat("AccessibilityNodeInfo.RangeInfo.min", min);
692         bundle.putFloat("AccessibilityNodeInfo.RangeInfo.max", max);
693         bundle.putFloat("AccessibilityNodeInfo.RangeInfo.current", current);
694     }
695 
nativeGetRootId(long nativeBrowserAccessibilityManagerAndroid)696     private native int nativeGetRootId(long nativeBrowserAccessibilityManagerAndroid);
nativeIsNodeValid(long nativeBrowserAccessibilityManagerAndroid, int id)697     private native boolean nativeIsNodeValid(long nativeBrowserAccessibilityManagerAndroid, int id);
nativeHitTest(long nativeBrowserAccessibilityManagerAndroid, int x, int y)698     private native void nativeHitTest(long nativeBrowserAccessibilityManagerAndroid, int x, int y);
nativePopulateAccessibilityNodeInfo( long nativeBrowserAccessibilityManagerAndroid, AccessibilityNodeInfo info, int id)699     private native boolean nativePopulateAccessibilityNodeInfo(
700         long nativeBrowserAccessibilityManagerAndroid, AccessibilityNodeInfo info, int id);
nativePopulateAccessibilityEvent( long nativeBrowserAccessibilityManagerAndroid, AccessibilityEvent event, int id, int eventType)701     private native boolean nativePopulateAccessibilityEvent(
702         long nativeBrowserAccessibilityManagerAndroid, AccessibilityEvent event, int id,
703         int eventType);
nativeClick(long nativeBrowserAccessibilityManagerAndroid, int id)704     private native void nativeClick(long nativeBrowserAccessibilityManagerAndroid, int id);
nativeFocus(long nativeBrowserAccessibilityManagerAndroid, int id)705     private native void nativeFocus(long nativeBrowserAccessibilityManagerAndroid, int id);
nativeBlur(long nativeBrowserAccessibilityManagerAndroid)706     private native void nativeBlur(long nativeBrowserAccessibilityManagerAndroid);
nativeScrollToMakeNodeVisible( long nativeBrowserAccessibilityManagerAndroid, int id)707     private native void nativeScrollToMakeNodeVisible(
708             long nativeBrowserAccessibilityManagerAndroid, int id);
nativeFindElementType(long nativeBrowserAccessibilityManagerAndroid, int startId, String elementType, boolean forwards)709     private native int nativeFindElementType(long nativeBrowserAccessibilityManagerAndroid,
710             int startId, String elementType, boolean forwards);
711 }
712