Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6015df1 authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Add actions to non-JavaScript accessibility handler." into jb-dev

parents 9a9a041c 6a62b77d
Loading
Loading
Loading
Loading
+11 −12
Original line number Diff line number Diff line
@@ -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.
@@ -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;
@@ -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;
@@ -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);
        }
    }

@@ -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;
        }
    }

+88 −12
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.webkit;

import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
@@ -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;
@@ -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},
@@ -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>();
@@ -104,6 +121,7 @@ class AccessibilityInjectorFallback {
     */
    public AccessibilityInjectorFallback(WebViewClassic webView) {
        mWebView = webView;
        mWebViewInternal = mWebView.getWebView();
        ensureWebContentKeyBindings();
    }

@@ -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:
@@ -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);
@@ -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);
@@ -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.
     *
@@ -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);
        }
@@ -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);
        }
    }
@@ -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;
    }