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

Commit 49c771c4 authored by Svetoslav Ganov's avatar Svetoslav Ganov Committed by Android (Google) Code Review
Browse files

Merge "WebView accessibility key bindings do not allow mapping of all meta...

Merge "WebView accessibility key bindings do not allow mapping of all meta keys. Exception when turning off acessibility and having a WebView showing content." into honeycomb
parents 2ea2553b b01c3d2b
Loading
Loading
Loading
Loading
+46 −65
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -41,7 +40,7 @@ import java.util.Stack;
 *       aware of one navigation axis which is in fact the default behavior
 *       of webViews while using the DPAD/TrackBall.
 * </p>
 * In general a key binding is a mapping from meta state + key code to
 * In general a key binding is a mapping from modifiers + key code to
 * a sequence of actions. For more detail how to specify key bindings refer to
 * {@link android.provider.Settings.Secure#ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS}.
 * </p>
@@ -77,8 +76,8 @@ class AccessibilityInjector {
    private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7;

    // these are the same for all instances so make them process wide
    private static SparseArray<AccessibilityWebContentKeyBinding> sBindings =
        new SparseArray<AccessibilityWebContentKeyBinding>();
    private static ArrayList<AccessibilityWebContentKeyBinding> sBindings =
        new ArrayList<AccessibilityWebContentKeyBinding>();

    // handle to the WebView this injector is associated with.
    private final WebView mWebView;
@@ -120,10 +119,15 @@ class AccessibilityInjector {

        mLastDownEventHandled = false;

        int key = event.getMetaState() << AccessibilityWebContentKeyBinding.OFFSET_META_STATE |
            event.getKeyCode() << AccessibilityWebContentKeyBinding.OFFSET_KEY_CODE;
        AccessibilityWebContentKeyBinding binding = null;
        for (AccessibilityWebContentKeyBinding candidate : sBindings) {
            if (event.getKeyCode() == candidate.getKeyCode()
                    && event.hasModifiers(candidate.getModifiers())) {
                binding = candidate;
                break;
            }
        }

        AccessibilityWebContentKeyBinding binding = sBindings.get(key);
        if (binding == null) {
            return false;
        }
@@ -302,7 +306,12 @@ class AccessibilityInjector {
        if (DEBUG) {
            Log.d(LOG_TAG, "Dispatching: " + event);
        }
        AccessibilityManager.getInstance(mWebView.getContext()).sendAccessibilityEvent(event);
        // accessibility may be disabled while waiting for the selection string
        AccessibilityManager accessibilityManager =
            AccessibilityManager.getInstance(mWebView.getContext());
        if (accessibilityManager.isEnabled()) {
            accessibilityManager.sendAccessibilityEvent(event);
        }
    }

    /**
@@ -332,9 +341,6 @@ class AccessibilityInjector {
        SimpleStringSplitter semiColonSplitter = new SimpleStringSplitter(';');
        semiColonSplitter.setString(webContentKeyBindingsString);

        ArrayList<AccessibilityWebContentKeyBinding> bindings =
            new ArrayList<AccessibilityWebContentKeyBinding>();

        while (semiColonSplitter.hasNext()) {
            String bindingString = semiColonSplitter.next();
            if (TextUtils.isEmpty(bindingString)) {
@@ -348,80 +354,58 @@ class AccessibilityInjector {
                continue;
            }
            try {
                int key = Integer.decode(keyValueArray[0].trim());
                long keyCodeAndModifiers = Long.decode(keyValueArray[0].trim());
                String[] actionStrings = keyValueArray[1].split(":");
                int[] actions = new int[actionStrings.length];
                for (int i = 0, count = actions.length; i < count; i++) {
                    actions[i] = Integer.decode(actionStrings[i].trim());
                }
                bindings.add(new AccessibilityWebContentKeyBinding(key, actions));
                sBindings.add(new AccessibilityWebContentKeyBinding(keyCodeAndModifiers, actions));
            } catch (NumberFormatException nfe) {
                Log.e(LOG_TAG, "Disregarding malformed key binding: " + bindingString);
            }
        }

        for (AccessibilityWebContentKeyBinding binding : bindings) {
            sBindings.put(binding.getKey(), binding);
        }
    }

    /**
     * Represents a web content key-binding.
     */
    private class AccessibilityWebContentKeyBinding {

        private static final int OFFSET_META_STATE = 0x00000010;
    private static final class AccessibilityWebContentKeyBinding {

        private static final int MASK_META_STATE = 0xFFFF0000;
        private static final int MODIFIERS_OFFSET = 32;
        private static final long MODIFIERS_MASK = 0xFFFFFFF00000000L;

        private static final int OFFSET_KEY_CODE = 0x00000000;
        private static final int KEY_CODE_OFFSET = 0;
        private static final long KEY_CODE_MASK = 0x00000000FFFFFFFFL;

        private static final int MASK_KEY_CODE = 0x0000FFFF;
        private static final int ACTION_OFFSET = 24;
        private static final int ACTION_MASK = 0xFF000000;

        private static final int OFFSET_ACTION = 0x00000018;
        private static final int FIRST_ARGUMENT_OFFSET = 16;
        private static final int FIRST_ARGUMENT_MASK = 0x00FF0000;

        private static final int MASK_ACTION = 0xFF000000;
        private static final int SECOND_ARGUMENT_OFFSET = 8;
        private static final int SECOND_ARGUMENT_MASK = 0x0000FF00;

        private static final int OFFSET_FIRST_ARGUMENT = 0x00000010;
        private static final int THIRD_ARGUMENT_OFFSET = 0;
        private static final int THIRD_ARGUMENT_MASK = 0x000000FF;

        private static final int MASK_FIRST_ARGUMENT = 0x00FF0000;
        private final long mKeyCodeAndModifiers;

        private static final int OFFSET_SECOND_ARGUMENT = 0x00000008;

        private static final int MASK_SECOND_ARGUMENT = 0x0000FF00;

        private static final int OFFSET_THIRD_ARGUMENT = 0x00000000;

        private static final int MASK_THIRD_ARGUMENT = 0x000000FF;

        private int mKey;

        private int [] mActionSequence;

        /**
         * @return The binding key with key code and meta state.
         *
         * @see #MASK_KEY_CODE
         * @see #MASK_META_STATE
         * @see #OFFSET_KEY_CODE
         * @see #OFFSET_META_STATE
         */
        public int getKey() {
            return mKey;
        }
        private final int [] mActionSequence;

        /**
         * @return The key code of the binding key.
         */
        public int getKeyCode() {
            return (mKey & MASK_KEY_CODE) >> OFFSET_KEY_CODE;
            return (int) ((mKeyCodeAndModifiers & KEY_CODE_MASK) >> KEY_CODE_OFFSET);
        }

        /**
         * @return The meta state of the binding key.
         */
        public int getMetaState() {
            return (mKey & MASK_META_STATE) >> OFFSET_META_STATE;
        public int getModifiers() {
            return (int) ((mKeyCodeAndModifiers & MODIFIERS_MASK) >> MODIFIERS_OFFSET);
        }

        /**
@@ -442,48 +426,45 @@ class AccessibilityInjector {
         * @param index The action code for a given action <code>index</code>.
         */
        public int getActionCode(int index) {
            return (mActionSequence[index] & MASK_ACTION) >> OFFSET_ACTION;
            return (mActionSequence[index] & ACTION_MASK) >> ACTION_OFFSET;
        }

        /**
         * @param index The first argument for a given action <code>index</code>.
         */
        public int getFirstArgument(int index) {
            return (mActionSequence[index] & MASK_FIRST_ARGUMENT) >> OFFSET_FIRST_ARGUMENT;
            return (mActionSequence[index] & FIRST_ARGUMENT_MASK) >> FIRST_ARGUMENT_OFFSET;
        }

        /**
         * @param index The second argument for a given action <code>index</code>.
         */
        public int getSecondArgument(int index) {
            return (mActionSequence[index] & MASK_SECOND_ARGUMENT) >> OFFSET_SECOND_ARGUMENT;
            return (mActionSequence[index] & SECOND_ARGUMENT_MASK) >> SECOND_ARGUMENT_OFFSET;
        }

        /**
         * @param index The third argument for a given action <code>index</code>.
         */
        public int getThirdArgument(int index) {
            return (mActionSequence[index] & MASK_THIRD_ARGUMENT) >> OFFSET_THIRD_ARGUMENT;
            return (mActionSequence[index] & THIRD_ARGUMENT_MASK) >> THIRD_ARGUMENT_OFFSET;
        }

        /**
         * Creates a new instance.
         * @param key The key for the binding (key and meta state)
         * @param keyCodeAndModifiers The key for the binding (key and modifiers).
         * @param actionSequence The sequence of action for the binding.
         * @see #getKey()
         */
        public AccessibilityWebContentKeyBinding(int key, int[] actionSequence) {
            mKey = key;
        public AccessibilityWebContentKeyBinding(long keyCodeAndModifiers, int[] actionSequence) {
            mKeyCodeAndModifiers = keyCodeAndModifiers;
            mActionSequence = actionSequence;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("key: ");
            builder.append(getKey());
            builder.append(", metaState: ");
            builder.append(getMetaState());
            builder.append("modifiers: ");
            builder.append(getModifiers());
            builder.append(", keyCode: ");
            builder.append(getKeyCode());
            builder.append(", actions[");
+44 −20
Original line number Diff line number Diff line
@@ -4545,17 +4545,29 @@ public class WebView extends AbsoluteLayout

        // accessibility support
        if (accessibilityScriptInjected()) {
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                // if an accessibility script is injected we delegate to it the key handling.
                // this script is a screen reader which is a fully fledged solution for blind
                // users to navigate in and interact with web pages.
                mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
                return true;
        } else if (mAccessibilityInjector != null && mAccessibilityInjector.onKeyEvent(event)) {
            // 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.
            } else {
                // Clean up if accessibility was disabled after loading the current URL.
                mAccessibilityScriptInjected = false;
            }
        } else if (mAccessibilityInjector != null) {
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                if (mAccessibilityInjector.onKeyEvent(event)) {
                    // 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 true;
                }
            } else {
                // Clean up if accessibility was disabled after loading the current URL.
                mAccessibilityInjector = null;
            }
        }

        if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
            if (event.hasNoModifiers()) {
@@ -4733,17 +4745,29 @@ public class WebView extends AbsoluteLayout

        // accessibility support
        if (accessibilityScriptInjected()) {
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                // if an accessibility script is injected we delegate to it the key handling.
                // this script is a screen reader which is a fully fledged solution for blind
                // users to navigate in and interact with web pages.
                mWebViewCore.sendMessage(EventHub.KEY_UP, event);
                return true;
        } else if (mAccessibilityInjector != null && mAccessibilityInjector.onKeyEvent(event)) {
            // 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.
            } else {
                // Clean up if accessibility was disabled after loading the current URL.
                mAccessibilityScriptInjected = false;
            }
        } else if (mAccessibilityInjector != null) {
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                if (mAccessibilityInjector.onKeyEvent(event)) {
                    // 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 true;
                }
            } else {
                // Clean up if accessibility was disabled after loading the current URL.
                mAccessibilityInjector = null;
            }
        }

        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
+4 −4
Original line number Diff line number Diff line
@@ -91,16 +91,16 @@
            0x16=0x04010100;
            <!-- Left Alt+DPAD/Trackball UP transitions from an axis to another and sends an event. -->
            <!-- Axis transitions:  2 -> 7; 1 -> 2; 0 -> 1; 3 -> 0; 4 -> 0; 5 -> 0; 6 -> 0; -->
            0x120013=0x03020701:0x03010201:0x03000101:0x03030001:0x03040001:0x03050001:0x03060001;
            0x200000013=0x03020701:0x03010201:0x03000101:0x03030001:0x03040001:0x03050001:0x03060001;
            <!-- Left Alt+DPAD/Trackball DOWN transitions from an axis to another and sends an event. -->
            <!-- Axis transitions: 1 -> 0; 2 -> 1; 7 -> 2; 3 -> 7; 4 -> 7; 5 -> 7; 6 -> 7; -->
            0x120014=0x03010001:0x03020101:0x03070201:0x03030701:0x03040701:0x03050701:0x03060701;
            0x200000014=0x03010001:0x03020101:0x03070201:0x03030701:0x03040701:0x03050701:0x03060701;
            <!-- Left Alt+DPAD/Trackball LEFT transitions from an axis to another and sends an event. -->
            <!-- Axis transitions: 4 -> 3; 5 -> 4; 6 -> 5; 0 -> 6; 1 -> 6; 2 -> 6; 7 -> 6; -->
            0x120015=0x03040301:0x03050401:0x03060501:0x03000601:0x03010601:0x03020601:0x03070601;
            0x200000015=0x03040301:0x03050401:0x03060501:0x03000601:0x03010601:0x03020601:0x03070601;
            <!-- Left Alt+DPAD/Trackball RIGHT transitions from an axis to another and sends an event.  -->
            <!-- Axis transitions: 5 -> 6; 4 -> 5; 3 -> 4; 2 -> 3; 7 -> 3; 1 -> 3; 0 -> 3; -->
            0x120016=0x03050601:0x03040501:0x03030401:0x03020301:0x03070301:0x03010301:0x03000301;
            0x200000016=0x03050601:0x03040501:0x03030401:0x03020301:0x03070301:0x03010301:0x03000301;
    </string>

    <!-- Default for Settings.System.USER_ROTATION -->