Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(506)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java

Issue 2465403003: Android accessibility: automatically focus links (Closed)
Patch Set: Rebase Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/browser/accessibility/browser_accessibility_manager_android.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser.accessibility; 5 package org.chromium.content.browser.accessibility;
6 6
7 import android.annotation.SuppressLint; 7 import android.annotation.SuppressLint;
8 import android.content.Context; 8 import android.content.Context;
9 import android.graphics.Rect; 9 import android.graphics.Rect;
10 import android.os.Build; 10 import android.os.Build;
(...skipping 19 matching lines...) Expand all
30 import java.util.List; 30 import java.util.List;
31 import java.util.Locale; 31 import java.util.Locale;
32 32
33 /** 33 /**
34 * Native accessibility for a {@link ContentViewCore}. 34 * Native accessibility for a {@link ContentViewCore}.
35 */ 35 */
36 @JNINamespace("content") 36 @JNINamespace("content")
37 public class BrowserAccessibilityManager { 37 public class BrowserAccessibilityManager {
38 private static final String TAG = "BrowserAccessibilityManager"; 38 private static final String TAG = "BrowserAccessibilityManager";
39 39
40 private static final int WINDOW_CONTENT_CHANGED_DELAY_MS = 500;
41 private static final int ACCESSIBILITY_FOCUS_LOCATION_CHANGED_DELAY_MS = 100 ;
42
40 // Constants from AccessibilityNodeInfo defined in the K SDK. 43 // Constants from AccessibilityNodeInfo defined in the K SDK.
41 private static final int ACTION_COLLAPSE = 0x00080000; 44 private static final int ACTION_COLLAPSE = 0x00080000;
42 private static final int ACTION_EXPAND = 0x00040000; 45 private static final int ACTION_EXPAND = 0x00040000;
43 46
44 // Constants from AccessibilityNodeInfo defined in the L SDK. 47 // Constants from AccessibilityNodeInfo defined in the L SDK.
45 private static final int ACTION_SET_TEXT = 0x200000; 48 private static final int ACTION_SET_TEXT = 0x200000;
46 private static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = 49 private static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
47 "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; 50 "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
48 private static final int WINDOW_CONTENT_CHANGED_DELAY_MS = 500;
49 51
50 // Constants from AccessibilityNodeInfo defined in the M SDK. 52 // Constants from AccessibilityNodeInfo defined in the M SDK.
51 // Source: https://developer.android.com/reference/android/R.id.html 53 // Source: https://developer.android.com/reference/android/R.id.html
52 protected static final int ACTION_CONTEXT_CLICK = 0x0102003c; 54 protected static final int ACTION_CONTEXT_CLICK = 0x0102003c;
53 protected static final int ACTION_SHOW_ON_SCREEN = 0x01020036; 55 protected static final int ACTION_SHOW_ON_SCREEN = 0x01020036;
54 protected static final int ACTION_SCROLL_UP = 0x01020038; 56 protected static final int ACTION_SCROLL_UP = 0x01020038;
55 protected static final int ACTION_SCROLL_DOWN = 0x0102003a; 57 protected static final int ACTION_SCROLL_DOWN = 0x0102003a;
56 protected static final int ACTION_SCROLL_LEFT = 0x01020039; 58 protected static final int ACTION_SCROLL_LEFT = 0x01020039;
57 protected static final int ACTION_SCROLL_RIGHT = 0x0102003b; 59 protected static final int ACTION_SCROLL_RIGHT = 0x0102003b;
58 60
59 private final AccessibilityNodeProvider mAccessibilityNodeProvider; 61 private final AccessibilityNodeProvider mAccessibilityNodeProvider;
60 private ContentViewCore mContentViewCore; 62 private ContentViewCore mContentViewCore;
61 private final AccessibilityManager mAccessibilityManager; 63 private final AccessibilityManager mAccessibilityManager;
62 private final RenderCoordinates mRenderCoordinates; 64 private final RenderCoordinates mRenderCoordinates;
63 private long mNativeObj; 65 private long mNativeObj;
64 private Rect mAccessibilityFocusRect; 66 private Rect mAccessibilityFocusRect;
65 private boolean mIsHovering; 67 private boolean mIsHovering;
66 private int mLastHoverId = View.NO_ID; 68 private int mLastHoverId = View.NO_ID;
67 protected int mCurrentRootId; 69 protected int mCurrentRootId;
68 private final int[] mTempLocation = new int[2]; 70 private final int[] mTempLocation = new int[2];
69 private final ViewGroup mView; 71 private final ViewGroup mView;
70 private boolean mUserHasTouchExplored; 72 private boolean mUserHasTouchExplored;
71 private boolean mPendingScrollToMakeNodeVisible; 73 private boolean mPendingScrollToMakeNodeVisible;
72 private boolean mNotifyFrameInfoInitializedCalled; 74 private boolean mNotifyFrameInfoInitializedCalled;
73 private int mSelectionGranularity; 75 private int mSelectionGranularity;
74 private int mSelectionStartIndex; 76 private int mSelectionStartIndex;
75 private int mSelectionEndIndex; 77 private int mSelectionEndIndex;
76 protected int mAccessibilityFocusId; 78 protected int mAccessibilityFocusId;
77 private Runnable mSendWindowContentChangedRunnable; 79 private Runnable mSendWindowContentChangedRunnable;
80 private Runnable mAccessibilityFocusLocationChangedRunnable;
78 81
79 /** 82 /**
80 * Create a BrowserAccessibilityManager object, which is owned by the C++ 83 * Create a BrowserAccessibilityManager object, which is owned by the C++
81 * BrowserAccessibilityManagerAndroid instance, and connects to the content view. 84 * BrowserAccessibilityManagerAndroid instance, and connects to the content view.
82 * @param nativeBrowserAccessibilityManagerAndroid A pointer to the counterp art native 85 * @param nativeBrowserAccessibilityManagerAndroid A pointer to the counterp art native
83 * C++ object that owns this object. 86 * C++ object that owns this object.
84 * @param contentViewCore The content view that this object provides accessi bility for. 87 * @param contentViewCore The content view that this object provides accessi bility for.
85 */ 88 */
86 @CalledByNative 89 @CalledByNative
87 private static BrowserAccessibilityManager create(long nativeBrowserAccessib ilityManagerAndroid, 90 private static BrowserAccessibilityManager create(long nativeBrowserAccessib ilityManagerAndroid,
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 } 233 }
231 return true; 234 return true;
232 case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: 235 case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
233 // ALWAYS respond with TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED whe ther we thought 236 // ALWAYS respond with TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED whe ther we thought
234 // it had focus or not, so that the Android framework cache is c orrect. 237 // it had focus or not, so that the Android framework cache is c orrect.
235 sendAccessibilityEvent(virtualViewId, 238 sendAccessibilityEvent(virtualViewId,
236 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED ); 239 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED );
237 if (mAccessibilityFocusId == virtualViewId) { 240 if (mAccessibilityFocusId == virtualViewId) {
238 mAccessibilityFocusId = View.NO_ID; 241 mAccessibilityFocusId = View.NO_ID;
239 mAccessibilityFocusRect = null; 242 mAccessibilityFocusRect = null;
243 // If we had a pending callback to update the location of th e previous object
244 // with accessibility focus, remove it.
245 if (mAccessibilityFocusLocationChangedRunnable != null) {
246 mView.removeCallbacks(mAccessibilityFocusLocationChanged Runnable);
247 mAccessibilityFocusLocationChangedRunnable = null;
248 }
240 } 249 }
241 return true; 250 return true;
242 case AccessibilityNodeInfo.ACTION_CLICK: 251 case AccessibilityNodeInfo.ACTION_CLICK:
243 nativeClick(mNativeObj, virtualViewId); 252 nativeClick(mNativeObj, virtualViewId);
244 return true; 253 return true;
245 case AccessibilityNodeInfo.ACTION_FOCUS: 254 case AccessibilityNodeInfo.ACTION_FOCUS:
246 nativeFocus(mNativeObj, virtualViewId); 255 nativeFocus(mNativeObj, virtualViewId);
247 return true; 256 return true;
248 case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: 257 case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS:
249 nativeBlur(mNativeObj); 258 nativeBlur(mNativeObj);
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after
530 539
531 private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) { 540 private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) {
532 if (newAccessibilityFocusId == mAccessibilityFocusId) return false; 541 if (newAccessibilityFocusId == mAccessibilityFocusId) return false;
533 542
534 mAccessibilityFocusId = newAccessibilityFocusId; 543 mAccessibilityFocusId = newAccessibilityFocusId;
535 mAccessibilityFocusRect = null; 544 mAccessibilityFocusRect = null;
536 mSelectionGranularity = 0; 545 mSelectionGranularity = 0;
537 mSelectionStartIndex = 0; 546 mSelectionStartIndex = 0;
538 mSelectionEndIndex = 0; 547 mSelectionEndIndex = 0;
539 548
549 // If we had a pending callback to update the location of the previous o bject with
550 // accessibility focus, remove it.
551 if (mAccessibilityFocusLocationChangedRunnable != null) {
552 mView.removeCallbacks(mAccessibilityFocusLocationChangedRunnable);
553 mAccessibilityFocusLocationChangedRunnable = null;
554 }
555
556 // Call nativeSetAccessibilityFocus. For the most part Chrome doesn't ha ve a
557 // concept of accessibility focus, but we do two things: (1) auto-focus certain
558 // roles like links when they get accessibility focus and (2) load inlin e text boxes
559 // for nodes when they get accessibility focus since inline text boxes a re expensive
560 // to load and on Android they're only needed for nodes that have input focus or
561 // accessibility focus.
562 //
540 // Calling nativeSetAccessibilityFocus will asynchronously load inline t ext boxes for 563 // Calling nativeSetAccessibilityFocus will asynchronously load inline t ext boxes for
541 // this node and its subtree. If accessibility focus is on anything othe r than 564 // this node and its subtree. If accessibility focus is on anything othe r than
542 // the root, do it - otherwise set it to -1 so we don't load inline text boxes 565 // the root, do it - otherwise set it to -1 so we don't load inline text boxes
543 // for the whole subtree of the root. 566 // for the whole subtree of the root.
544 if (mAccessibilityFocusId == mCurrentRootId) { 567 if (mAccessibilityFocusId == mCurrentRootId) {
545 nativeSetAccessibilityFocus(mNativeObj, -1); 568 nativeSetAccessibilityFocus(mNativeObj, -1);
546 } else { 569 } else {
547 nativeSetAccessibilityFocus(mNativeObj, mAccessibilityFocusId); 570 nativeSetAccessibilityFocus(mNativeObj, mAccessibilityFocusId);
548 } 571 }
549 572
550 sendAccessibilityEvent(mAccessibilityFocusId, 573 sendAccessibilityEvent(mAccessibilityFocusId,
551 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 574 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
552 return true; 575 return true;
553 } 576 }
554 577
555 private void moveAccessibilityFocusToIdAndRefocusIfNeeded(int newAccessibili tyFocusId) { 578 private void moveAccessibilityFocusToIdAndRefocusIfNeeded(int newAccessibili tyFocusId) {
556 // Work around a bug in the Android framework where it doesn't fully upd ate the object 579 // Work around a bug in the Android framework where it doesn't fully upd ate the object
557 // with accessibility focus even if you send it a WINDOW_CONTENT_CHANGED . To work around 580 // with accessibility focus even if you send it a WINDOW_CONTENT_CHANGED . To work around
558 // this, clear focus and then set focus again. 581 // this, clear focus and then set focus again.
559 if (newAccessibilityFocusId == mAccessibilityFocusId) { 582 if (newAccessibilityFocusId == mAccessibilityFocusId) {
560 sendAccessibilityEvent(newAccessibilityFocusId, 583 sendAccessibilityEvent(newAccessibilityFocusId,
561 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); 584 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
562 mAccessibilityFocusId = View.NO_ID; 585 mAccessibilityFocusId = View.NO_ID;
563 } 586 }
564 587
565 moveAccessibilityFocusToId(newAccessibilityFocusId); 588 moveAccessibilityFocusToId(newAccessibilityFocusId);
566 } 589 }
567 590
568 /** 591 /**
592 * Work around a bug in the Android framework where if the object with acces sibility
593 * focus moves, the accessibility focus rect is not updated - both the visu al highlight,
594 * and the location on the screen that's clicked if you double-tap. To work around this,
595 * when we know the object with accessibility focus moved, move focus away a nd then
596 * move focus right back to it, which tricks Android into updating its bound s.
597 *
598 * Do this after a short delay because sometimes the change to the object wi th accessibility
599 * focus happens just before navigating somewhere else.
600 */
601 private void updateAccessibilityFocusLocationAfterDelay() {
602 if (mNativeObj == 0) return;
603
604 if (mAccessibilityFocusLocationChangedRunnable != null) return;
605
606 mAccessibilityFocusLocationChangedRunnable = new Runnable() {
607 @Override
608 public void run() {
609 updateAccessibilityFocusLocation();
610 }
611 };
612
613 mView.postDelayed(mAccessibilityFocusLocationChangedRunnable,
614 ACCESSIBILITY_FOCUS_LOCATION_CHANGED_DELAY_MS);
615 }
616
617 /**
618 * See updateAccessibilityFocusLocationAfterDelay for details.
619 */
620 private void updateAccessibilityFocusLocation() {
621 // This can be called from a timeout, so we need to make sure we're stil l valid.
622 if (mNativeObj == 0 || mContentViewCore == null || mView == null) return ;
623
624 if (mAccessibilityFocusLocationChangedRunnable != null) {
625 mView.removeCallbacks(mAccessibilityFocusLocationChangedRunnable);
626 mAccessibilityFocusLocationChangedRunnable = null;
627 }
628
629 moveAccessibilityFocusToIdAndRefocusIfNeeded(mAccessibilityFocusId);
630 }
631
632 /**
569 * Send a WINDOW_CONTENT_CHANGED event after a short delay. This helps throt tle such 633 * Send a WINDOW_CONTENT_CHANGED event after a short delay. This helps throt tle such
570 * events from firing too quickly during animations, for example. 634 * events from firing too quickly during animations, for example.
571 */ 635 */
572 @CalledByNative 636 @CalledByNative
573 private void sendDelayedWindowContentChangedEvent() { 637 private void sendDelayedWindowContentChangedEvent() {
574 if (mNativeObj == 0) return; 638 if (mNativeObj == 0) return;
575 639
576 if (mSendWindowContentChangedRunnable != null) return; 640 if (mSendWindowContentChangedRunnable != null) return;
577 641
578 mSendWindowContentChangedRunnable = new Runnable() { 642 mSendWindowContentChangedRunnable = new Runnable() {
(...skipping 415 matching lines...) Expand 10 before | Expand all | Expand 10 after
994 rect.offset(viewLocation[0], viewLocation[1]); 1058 rect.offset(viewLocation[0], viewLocation[1]);
995 1059
996 // Clip the node's bounding rect to the viewport bounds. 1060 // Clip the node's bounding rect to the viewport bounds.
997 int viewportRectTop = viewLocation[1] + (int) mRenderCoordinates.getCont entOffsetYPix(); 1061 int viewportRectTop = viewLocation[1] + (int) mRenderCoordinates.getCont entOffsetYPix();
998 int viewportRectBottom = viewportRectTop + mContentViewCore.getViewportH eightPix(); 1062 int viewportRectBottom = viewportRectTop + mContentViewCore.getViewportH eightPix();
999 if (rect.top < viewportRectTop) rect.top = viewportRectTop; 1063 if (rect.top < viewportRectTop) rect.top = viewportRectTop;
1000 if (rect.bottom > viewportRectBottom) rect.bottom = viewportRectBottom; 1064 if (rect.bottom > viewportRectBottom) rect.bottom = viewportRectBottom;
1001 1065
1002 node.setBoundsInScreen(rect); 1066 node.setBoundsInScreen(rect);
1003 1067
1004 // Work around a bug in the Android framework where if the object with a ccessibility 1068 // If this is the node with accessibility focus, ensure that its locatio n on-screen
1005 // focus moves, the accessibility focus rect is not updated - both the v isual highlight, 1069 // is up-to-date.
1006 // and the location on the screen that's clicked if you double-tap. To w ork around this,
1007 // when we know the object with accessibility focus moved, move focus aw ay and then
1008 // move focus right back to it, which tricks Android into updating its b ounds.
1009 if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentR ootId) { 1070 if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentR ootId) {
1010 if (mAccessibilityFocusRect == null) { 1071 if (mAccessibilityFocusRect == null) {
1011 mAccessibilityFocusRect = rect; 1072 mAccessibilityFocusRect = rect;
1012 } else if (!mAccessibilityFocusRect.equals(rect)) { 1073 } else if (!mAccessibilityFocusRect.equals(rect)) {
1013 mAccessibilityFocusRect = rect; 1074 mAccessibilityFocusRect = rect;
1014 moveAccessibilityFocusToIdAndRefocusIfNeeded(virtualViewId); 1075 updateAccessibilityFocusLocationAfterDelay();
1015 } 1076 }
1016 } 1077 }
1017 } 1078 }
1018 1079
1019 @CalledByNative 1080 @CalledByNative
1020 protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInf o node, 1081 protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInf o node,
1021 boolean isRoot, boolean isEditableText, String roleDescription) { 1082 boolean isRoot, boolean isEditableText, String roleDescription) {
1022 // Requires KitKat or higher. 1083 // Requires KitKat or higher.
1023 } 1084 }
1024 1085
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
1209 long nativeBrowserAccessibilityManagerAndroid, int id); 1270 long nativeBrowserAccessibilityManagerAndroid, int id);
1210 private native boolean nativeIsSlider( 1271 private native boolean nativeIsSlider(
1211 long nativeBrowserAccessibilityManagerAndroid, int id); 1272 long nativeBrowserAccessibilityManagerAndroid, int id);
1212 private native boolean nativeScroll( 1273 private native boolean nativeScroll(
1213 long nativeBrowserAccessibilityManagerAndroid, int id, int direction ); 1274 long nativeBrowserAccessibilityManagerAndroid, int id, int direction );
1214 protected native String nativeGetSupportedHtmlElementTypes( 1275 protected native String nativeGetSupportedHtmlElementTypes(
1215 long nativeBrowserAccessibilityManagerAndroid); 1276 long nativeBrowserAccessibilityManagerAndroid);
1216 private native void nativeShowContextMenu( 1277 private native void nativeShowContextMenu(
1217 long nativeBrowserAccessibilityManagerAndroid, int id); 1278 long nativeBrowserAccessibilityManagerAndroid, int id);
1218 } 1279 }
OLDNEW
« no previous file with comments | « content/browser/accessibility/browser_accessibility_manager_android.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698