Loading core/java/android/webkit/AccessibilityInjector.java +11 −12 Original line number Diff line number Diff line Loading @@ -63,7 +63,7 @@ class AccessibilityInjector { // Lazily loaded helper objects. private AccessibilityManager mAccessibilityManager; private AccessibilityInjectorFallback mAccessibilityInjector; private AccessibilityInjectorFallback mAccessibilityInjectorFallback; private JSONObject mAccessibilityJSONObject; // Whether the accessibility script has been injected into the current page. Loading Loading @@ -201,9 +201,8 @@ class AccessibilityInjector { return sendActionToAndroidVox(action, arguments); } if (mAccessibilityInjector != null) { // TODO: Implement actions for non-JS handler. return false; if (mAccessibilityInjectorFallback != null) { return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments); } return false; Loading Loading @@ -238,11 +237,11 @@ class AccessibilityInjector { return true; } if (mAccessibilityInjector != null) { if (mAccessibilityInjectorFallback != null) { // if an accessibility injector is present (no JavaScript enabled or // the site opts out injecting our JavaScript screen reader) we let // it decide whether to act on and consume the event. return mAccessibilityInjector.onKeyEvent(event); return mAccessibilityInjectorFallback.onKeyEvent(event); } return false; Loading @@ -255,8 +254,8 @@ class AccessibilityInjector { * @param selectionString The selection string. */ public void handleSelectionChangedIfNecessary(String selectionString) { if (mAccessibilityInjector != null) { mAccessibilityInjector.onSelectionStringChange(selectionString); if (mAccessibilityInjectorFallback != null) { mAccessibilityInjectorFallback.onSelectionStringChange(selectionString); } } Loading Loading @@ -304,10 +303,10 @@ class AccessibilityInjector { * {@code false} to disable it. */ private void toggleFallbackAccessibilityInjector(boolean enabled) { if (enabled && (mAccessibilityInjector == null)) { mAccessibilityInjector = new AccessibilityInjectorFallback(mWebViewClassic); if (enabled && (mAccessibilityInjectorFallback == null)) { mAccessibilityInjectorFallback = new AccessibilityInjectorFallback(mWebViewClassic); } else { mAccessibilityInjector = null; mAccessibilityInjectorFallback = null; } } Loading core/java/android/webkit/AccessibilityInjectorFallback.java +88 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.webkit; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; Loading @@ -23,6 +24,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.webkit.WebViewCore.EventHub; import java.util.ArrayList; Loading @@ -48,7 +50,7 @@ import java.util.Stack; * {@link #setCurrentAxis(int, boolean, String)}, or * {@link #traverseCurrentAxis(int, boolean, String)} * {@link #traverseGivenAxis(int, int, boolean, String)} * {@link #prefromAxisTransition(int, int, boolean, String)} * {@link #performAxisTransition(int, int, boolean, String)} * referred via the values of: * {@link #ACTION_SET_CURRENT_AXIS}, * {@link #ACTION_TRAVERSE_CURRENT_AXIS}, Loading @@ -72,15 +74,30 @@ class AccessibilityInjectorFallback { private static final int ACTION_PERFORM_AXIS_TRANSITION = 3; private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4; // the default WebView behavior abstracted as a navigation axis // WebView navigation axes from WebViewCore.h, plus an additional axis for // the default behavior. private static final int NAVIGATION_AXIS_CHARACTER = 0; private static final int NAVIGATION_AXIS_WORD = 1; private static final int NAVIGATION_AXIS_SENTENCE = 2; @SuppressWarnings("unused") private static final int NAVIGATION_AXIS_HEADING = 3; private static final int NAVIGATION_AXIS_SIBLING = 5; @SuppressWarnings("unused") private static final int NAVIGATION_AXIS_PARENT_FIRST_CHILD = 5; private static final int NAVIGATION_AXIS_DOCUMENT = 6; private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7; // WebView navigation directions from WebViewCore.h. private static final int NAVIGATION_DIRECTION_BACKWARD = 0; private static final int NAVIGATION_DIRECTION_FORWARD = 1; // these are the same for all instances so make them process wide private static ArrayList<AccessibilityWebContentKeyBinding> sBindings = new ArrayList<AccessibilityWebContentKeyBinding>(); // handle to the WebViewClassic this injector is associated with. private final WebViewClassic mWebView; private final WebView mWebViewInternal; // events scheduled for sending as soon as we receive the selected text private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>(); Loading @@ -104,6 +121,7 @@ class AccessibilityInjectorFallback { */ public AccessibilityInjectorFallback(WebViewClassic webView) { mWebView = webView; mWebViewInternal = mWebView.getWebView(); ensureWebContentKeyBindings(); } Loading Loading @@ -176,7 +194,7 @@ class AccessibilityInjectorFallback { int fromAxis = binding.getFirstArgument(i); int toAxis = binding.getSecondArgument(i); sendEvent = (binding.getThirdArgument(i) == 1); prefromAxisTransition(fromAxis, toAxis, sendEvent, contentDescription); performAxisTransition(fromAxis, toAxis, sendEvent, contentDescription); mLastDownEventHandled = true; break; case ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS: Loading Loading @@ -214,7 +232,8 @@ class AccessibilityInjectorFallback { private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) { mCurrentAxis = axis; if (sendEvent) { AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent(); final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent( AccessibilityEvent.TYPE_ANNOUNCEMENT); event.getText().add(String.valueOf(axis)); event.setContentDescription(contentDescription); sendAccessibilityEvent(event); Loading @@ -229,7 +248,7 @@ class AccessibilityInjectorFallback { * @param sendEvent Flag if to send an event to announce successful transition. * @param contentDescription A description of the performed action. */ private void prefromAxisTransition(int fromAxis, int toAxis, boolean sendEvent, private void performAxisTransition(int fromAxis, int toAxis, boolean sendEvent, String contentDescription) { if (mCurrentAxis == fromAxis) { setCurrentAxis(toAxis, sendEvent, contentDescription); Loading @@ -250,6 +269,62 @@ class AccessibilityInjectorFallback { return traverseGivenAxis(direction, mCurrentAxis, sendEvent, contentDescription); } boolean performAccessibilityAction(int action, Bundle arguments) { switch (action) { case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: final int direction = getDirectionForAction(action); final int axis = getAxisForGranularity(arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT)); return traverseGivenAxis(direction, axis, true, null); default: return false; } } /** * Returns the {@link WebView}-defined direction for the given * {@link AccessibilityNodeInfo}-defined action. * * @param action An accessibility action identifier. * @return A web view navigation direction. */ private static int getDirectionForAction(int action) { switch (action) { case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: return NAVIGATION_DIRECTION_FORWARD; case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: return NAVIGATION_DIRECTION_BACKWARD; default: return -1; } } /** * Returns the {@link WebView}-defined axis for the given * {@link AccessibilityNodeInfo}-defined granularity. * * @param granularity An accessibility granularity identifier. * @return A web view navigation axis. */ private static int getAxisForGranularity(int granularity) { switch (granularity) { case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER: return NAVIGATION_AXIS_CHARACTER; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD: return NAVIGATION_AXIS_WORD; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE: return NAVIGATION_AXIS_SENTENCE; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH: // TODO: Figure out what nextSibling() actually means. return NAVIGATION_AXIS_SIBLING; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE: return NAVIGATION_AXIS_DOCUMENT; default: return -1; } } /** * Traverse the document along the given navigation axis. * Loading @@ -268,7 +343,8 @@ class AccessibilityInjectorFallback { AccessibilityEvent event = null; if (sendEvent) { event = getPartialyPopulatedAccessibilityEvent(); event = getPartialyPopulatedAccessibilityEvent( AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); // the text will be set upon receiving the selection string event.setContentDescription(contentDescription); } Loading Loading @@ -296,8 +372,10 @@ class AccessibilityInjectorFallback { return; } AccessibilityEvent event = mScheduledEventStack.pop(); if (event != null) { if ((event != null) && (selectionString != null)) { event.getText().add(selectionString); event.setFromIndex(0); event.setToIndex(selectionString.length()); sendAccessibilityEvent(event); } } Loading @@ -323,11 +401,9 @@ class AccessibilityInjectorFallback { * @return An accessibility event whose members are populated except its * text and content description. */ private AccessibilityEvent getPartialyPopulatedAccessibilityEvent() { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SELECTED); event.setClassName(mWebView.getClass().getName()); event.setPackageName(mWebView.getContext().getPackageName()); event.setEnabled(mWebView.getWebView().isEnabled()); private AccessibilityEvent getPartialyPopulatedAccessibilityEvent(int eventType) { AccessibilityEvent event = AccessibilityEvent.obtain(eventType); mWebViewInternal.onInitializeAccessibilityEvent(event); return event; } Loading Loading
core/java/android/webkit/AccessibilityInjector.java +11 −12 Original line number Diff line number Diff line Loading @@ -63,7 +63,7 @@ class AccessibilityInjector { // Lazily loaded helper objects. private AccessibilityManager mAccessibilityManager; private AccessibilityInjectorFallback mAccessibilityInjector; private AccessibilityInjectorFallback mAccessibilityInjectorFallback; private JSONObject mAccessibilityJSONObject; // Whether the accessibility script has been injected into the current page. Loading Loading @@ -201,9 +201,8 @@ class AccessibilityInjector { return sendActionToAndroidVox(action, arguments); } if (mAccessibilityInjector != null) { // TODO: Implement actions for non-JS handler. return false; if (mAccessibilityInjectorFallback != null) { return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments); } return false; Loading Loading @@ -238,11 +237,11 @@ class AccessibilityInjector { return true; } if (mAccessibilityInjector != null) { if (mAccessibilityInjectorFallback != null) { // if an accessibility injector is present (no JavaScript enabled or // the site opts out injecting our JavaScript screen reader) we let // it decide whether to act on and consume the event. return mAccessibilityInjector.onKeyEvent(event); return mAccessibilityInjectorFallback.onKeyEvent(event); } return false; Loading @@ -255,8 +254,8 @@ class AccessibilityInjector { * @param selectionString The selection string. */ public void handleSelectionChangedIfNecessary(String selectionString) { if (mAccessibilityInjector != null) { mAccessibilityInjector.onSelectionStringChange(selectionString); if (mAccessibilityInjectorFallback != null) { mAccessibilityInjectorFallback.onSelectionStringChange(selectionString); } } Loading Loading @@ -304,10 +303,10 @@ class AccessibilityInjector { * {@code false} to disable it. */ private void toggleFallbackAccessibilityInjector(boolean enabled) { if (enabled && (mAccessibilityInjector == null)) { mAccessibilityInjector = new AccessibilityInjectorFallback(mWebViewClassic); if (enabled && (mAccessibilityInjectorFallback == null)) { mAccessibilityInjectorFallback = new AccessibilityInjectorFallback(mWebViewClassic); } else { mAccessibilityInjector = null; mAccessibilityInjectorFallback = null; } } Loading
core/java/android/webkit/AccessibilityInjectorFallback.java +88 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.webkit; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; Loading @@ -23,6 +24,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.webkit.WebViewCore.EventHub; import java.util.ArrayList; Loading @@ -48,7 +50,7 @@ import java.util.Stack; * {@link #setCurrentAxis(int, boolean, String)}, or * {@link #traverseCurrentAxis(int, boolean, String)} * {@link #traverseGivenAxis(int, int, boolean, String)} * {@link #prefromAxisTransition(int, int, boolean, String)} * {@link #performAxisTransition(int, int, boolean, String)} * referred via the values of: * {@link #ACTION_SET_CURRENT_AXIS}, * {@link #ACTION_TRAVERSE_CURRENT_AXIS}, Loading @@ -72,15 +74,30 @@ class AccessibilityInjectorFallback { private static final int ACTION_PERFORM_AXIS_TRANSITION = 3; private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4; // the default WebView behavior abstracted as a navigation axis // WebView navigation axes from WebViewCore.h, plus an additional axis for // the default behavior. private static final int NAVIGATION_AXIS_CHARACTER = 0; private static final int NAVIGATION_AXIS_WORD = 1; private static final int NAVIGATION_AXIS_SENTENCE = 2; @SuppressWarnings("unused") private static final int NAVIGATION_AXIS_HEADING = 3; private static final int NAVIGATION_AXIS_SIBLING = 5; @SuppressWarnings("unused") private static final int NAVIGATION_AXIS_PARENT_FIRST_CHILD = 5; private static final int NAVIGATION_AXIS_DOCUMENT = 6; private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7; // WebView navigation directions from WebViewCore.h. private static final int NAVIGATION_DIRECTION_BACKWARD = 0; private static final int NAVIGATION_DIRECTION_FORWARD = 1; // these are the same for all instances so make them process wide private static ArrayList<AccessibilityWebContentKeyBinding> sBindings = new ArrayList<AccessibilityWebContentKeyBinding>(); // handle to the WebViewClassic this injector is associated with. private final WebViewClassic mWebView; private final WebView mWebViewInternal; // events scheduled for sending as soon as we receive the selected text private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>(); Loading @@ -104,6 +121,7 @@ class AccessibilityInjectorFallback { */ public AccessibilityInjectorFallback(WebViewClassic webView) { mWebView = webView; mWebViewInternal = mWebView.getWebView(); ensureWebContentKeyBindings(); } Loading Loading @@ -176,7 +194,7 @@ class AccessibilityInjectorFallback { int fromAxis = binding.getFirstArgument(i); int toAxis = binding.getSecondArgument(i); sendEvent = (binding.getThirdArgument(i) == 1); prefromAxisTransition(fromAxis, toAxis, sendEvent, contentDescription); performAxisTransition(fromAxis, toAxis, sendEvent, contentDescription); mLastDownEventHandled = true; break; case ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS: Loading Loading @@ -214,7 +232,8 @@ class AccessibilityInjectorFallback { private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) { mCurrentAxis = axis; if (sendEvent) { AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent(); final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent( AccessibilityEvent.TYPE_ANNOUNCEMENT); event.getText().add(String.valueOf(axis)); event.setContentDescription(contentDescription); sendAccessibilityEvent(event); Loading @@ -229,7 +248,7 @@ class AccessibilityInjectorFallback { * @param sendEvent Flag if to send an event to announce successful transition. * @param contentDescription A description of the performed action. */ private void prefromAxisTransition(int fromAxis, int toAxis, boolean sendEvent, private void performAxisTransition(int fromAxis, int toAxis, boolean sendEvent, String contentDescription) { if (mCurrentAxis == fromAxis) { setCurrentAxis(toAxis, sendEvent, contentDescription); Loading @@ -250,6 +269,62 @@ class AccessibilityInjectorFallback { return traverseGivenAxis(direction, mCurrentAxis, sendEvent, contentDescription); } boolean performAccessibilityAction(int action, Bundle arguments) { switch (action) { case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: final int direction = getDirectionForAction(action); final int axis = getAxisForGranularity(arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT)); return traverseGivenAxis(direction, axis, true, null); default: return false; } } /** * Returns the {@link WebView}-defined direction for the given * {@link AccessibilityNodeInfo}-defined action. * * @param action An accessibility action identifier. * @return A web view navigation direction. */ private static int getDirectionForAction(int action) { switch (action) { case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: return NAVIGATION_DIRECTION_FORWARD; case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: return NAVIGATION_DIRECTION_BACKWARD; default: return -1; } } /** * Returns the {@link WebView}-defined axis for the given * {@link AccessibilityNodeInfo}-defined granularity. * * @param granularity An accessibility granularity identifier. * @return A web view navigation axis. */ private static int getAxisForGranularity(int granularity) { switch (granularity) { case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER: return NAVIGATION_AXIS_CHARACTER; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD: return NAVIGATION_AXIS_WORD; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE: return NAVIGATION_AXIS_SENTENCE; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH: // TODO: Figure out what nextSibling() actually means. return NAVIGATION_AXIS_SIBLING; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE: return NAVIGATION_AXIS_DOCUMENT; default: return -1; } } /** * Traverse the document along the given navigation axis. * Loading @@ -268,7 +343,8 @@ class AccessibilityInjectorFallback { AccessibilityEvent event = null; if (sendEvent) { event = getPartialyPopulatedAccessibilityEvent(); event = getPartialyPopulatedAccessibilityEvent( AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); // the text will be set upon receiving the selection string event.setContentDescription(contentDescription); } Loading Loading @@ -296,8 +372,10 @@ class AccessibilityInjectorFallback { return; } AccessibilityEvent event = mScheduledEventStack.pop(); if (event != null) { if ((event != null) && (selectionString != null)) { event.getText().add(selectionString); event.setFromIndex(0); event.setToIndex(selectionString.length()); sendAccessibilityEvent(event); } } Loading @@ -323,11 +401,9 @@ class AccessibilityInjectorFallback { * @return An accessibility event whose members are populated except its * text and content description. */ private AccessibilityEvent getPartialyPopulatedAccessibilityEvent() { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SELECTED); event.setClassName(mWebView.getClass().getName()); event.setPackageName(mWebView.getContext().getPackageName()); event.setEnabled(mWebView.getWebView().isEnabled()); private AccessibilityEvent getPartialyPopulatedAccessibilityEvent(int eventType) { AccessibilityEvent event = AccessibilityEvent.obtain(eventType); mWebViewInternal.onInitializeAccessibilityEvent(event); return event; } Loading