am 49c771c4: 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
* commit '49c771c4da8bb7cf135e6afed3a901b1744beb67': WebView accessibility key bindings do not allow mapping of all meta keys. Exception when turning off acessibility and having a WebView showing content.
This commit is contained in:
@@ -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 class AccessibilityWebContentKeyBinding {
|
||||
|
||||
private static final int OFFSET_META_STATE = 0x00000010;
|
||||
private static final int MODIFIERS_OFFSET = 32;
|
||||
private static final long MODIFIERS_MASK = 0xFFFFFFF00000000L;
|
||||
|
||||
private static final int MASK_META_STATE = 0xFFFF0000;
|
||||
private static final int KEY_CODE_OFFSET = 0;
|
||||
private static final long KEY_CODE_MASK = 0x00000000FFFFFFFFL;
|
||||
|
||||
private static final int OFFSET_KEY_CODE = 0x00000000;
|
||||
private static final int ACTION_OFFSET = 24;
|
||||
private static final int ACTION_MASK = 0xFF000000;
|
||||
|
||||
private static final int MASK_KEY_CODE = 0x0000FFFF;
|
||||
private static final int FIRST_ARGUMENT_OFFSET = 16;
|
||||
private static final int FIRST_ARGUMENT_MASK = 0x00FF0000;
|
||||
|
||||
private static final int OFFSET_ACTION = 0x00000018;
|
||||
private static final int SECOND_ARGUMENT_OFFSET = 8;
|
||||
private static final int SECOND_ARGUMENT_MASK = 0x0000FF00;
|
||||
|
||||
private static final int MASK_ACTION = 0xFF000000;
|
||||
private static final int THIRD_ARGUMENT_OFFSET = 0;
|
||||
private static final int THIRD_ARGUMENT_MASK = 0x000000FF;
|
||||
|
||||
private static final int OFFSET_FIRST_ARGUMENT = 0x00000010;
|
||||
private final long mKeyCodeAndModifiers;
|
||||
|
||||
private static final int MASK_FIRST_ARGUMENT = 0x00FF0000;
|
||||
|
||||
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[");
|
||||
|
||||
@@ -4545,16 +4545,28 @@ public class WebView extends AbsoluteLayout
|
||||
|
||||
// accessibility support
|
||||
if (accessibilityScriptInjected()) {
|
||||
// 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.
|
||||
return true;
|
||||
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 {
|
||||
// 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) {
|
||||
@@ -4733,16 +4745,28 @@ public class WebView extends AbsoluteLayout
|
||||
|
||||
// accessibility support
|
||||
if (accessibilityScriptInjected()) {
|
||||
// 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.
|
||||
return true;
|
||||
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 {
|
||||
// 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
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
Reference in New Issue
Block a user