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

Unified Diff: content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java

Issue 15741009: Native Android accessibility. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Split RendererAccessibilityFocusOnly fix into separate changelist Created 7 years, 6 months 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 side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index 58ff66c09d8348ca5fb86e39c65c6ffe30f28198..cc31fca27ed24900b02a2dd5c6a3809288f37846 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -9,14 +9,18 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.text.Editable;
import android.util.Log;
import android.util.Pair;
@@ -30,7 +34,10 @@ import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
@@ -44,6 +51,7 @@ import org.chromium.base.WeakContext;
import org.chromium.content.R;
import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
import org.chromium.content.browser.accessibility.AccessibilityInjector;
+import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
import org.chromium.content.browser.input.AdapterInputConnection;
import org.chromium.content.browser.input.HandleView;
import org.chromium.content.browser.input.ImeAdapter;
@@ -58,6 +66,8 @@ import org.chromium.ui.WindowAndroid;
import org.chromium.ui.gfx.DeviceDisplayInfo;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -68,7 +78,9 @@ import java.util.Map;
* being tied to the view system.
*/
@JNINamespace("content")
-public class ContentViewCore implements MotionEventDelegate, NavigationClient {
+ public class ContentViewCore implements MotionEventDelegate,
+ NavigationClient,
+ AccessibilityStateChangeListener {
/**
* Indicates that input events are batched together and delivered just before vsync.
*/
@@ -353,6 +365,12 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
// The AccessibilityInjector that handles loading Accessibility scripts into the web page.
private AccessibilityInjector mAccessibilityInjector;
+ // Handles native accessibility, i.e. without any script injection.
+ private BrowserAccessibilityManager mBrowserAccessibilityManager;
+
+ // System accessibility service.
+ private AccessibilityManager mAccessibilityManager;
bulach 2013/06/19 10:26:53 looks like this can be final.
dmazzoni 2013/06/19 19:54:41 Done.
+
// Temporary notification to tell onSizeChanged to focus a form element,
// because the OSK was just brought up.
private boolean mUnfocusOnNextSizeChanged = false;
@@ -374,6 +392,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
private ViewAndroid mViewAndroid;
+
/**
* Constructs a new ContentViewCore. Embedders must call initialize() after constructing
* a ContentViewCore and before using it.
@@ -393,6 +412,8 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
mStartHandlePoint = mRenderCoordinates.createNormalizedPoint();
mEndHandlePoint = mRenderCoordinates.createNormalizedPoint();
mInsertionHandlePoint = mRenderCoordinates.createNormalizedPoint();
+ mAccessibilityManager = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
}
/**
@@ -638,8 +659,24 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
mContentSettings = new ContentSettings(this, mNativeContentViewCore);
initializeContainerView(internalDispatcher, inputEventDeliveryMode);
+ try {
+ Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
+ field.setAccessible(true);
+ String ACCESSIBILITY_SCRIPT_INJECTION = (String) field.get(null);
+ getContext().getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(ACCESSIBILITY_SCRIPT_INJECTION),
+ false,
+ new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange, Uri uri) {
+ setAccessibilityState(mAccessibilityManager.isEnabled());
+ }
+ });
+ } catch (Exception e) {
+ Log.e("chromium", "Could not add listener for script injection preference. " +
+ "Defaulting to native accessibility.\n" + e.toString());
+ }
+
mAccessibilityInjector = AccessibilityInjector.newInstance(this);
- mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
String contentDescription = "Web View";
if (R.string.accessibility_content_view == 0) {
@@ -1293,7 +1330,6 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
TraceEvent.begin();
hidePopupDialog();
nativeOnHide(mNativeContentViewCore);
- setAccessibilityState(false);
TraceEvent.end();
}
@@ -1302,7 +1338,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
*/
public void onActivityResume() {
nativeOnShow(mNativeContentViewCore);
- setAccessibilityState(true);
+ setAccessibilityState(mAccessibilityManager.isEnabled());
}
/**
@@ -1310,7 +1346,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
*/
public void onShow() {
nativeOnShow(mNativeContentViewCore);
- setAccessibilityState(true);
+ setAccessibilityState(mAccessibilityManager.isEnabled());
}
/**
@@ -1318,7 +1354,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
*/
public void onHide() {
hidePopupDialog();
- setAccessibilityState(false);
+ setInjectedAccessibility(false);
nativeOnHide(mNativeContentViewCore);
}
@@ -1371,7 +1407,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
ChildProcessLauncher.bindAsHighPriority(pid);
}
}
- setAccessibilityState(true);
+ setAccessibilityState(mAccessibilityManager.isEnabled());
}
/**
@@ -1386,7 +1422,8 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
ChildProcessLauncher.unbindAsHighPriority(pid);
}
}
- setAccessibilityState(false);
+ // TODO: correct behaviour for AndroidVox
bulach 2013/06/19 10:26:53 nit: TODO(dmazzoni):
dmazzoni 2013/06/19 19:54:41 Actually, I can delete this. I wasn't sure when I
+ setInjectedAccessibility(false);
hidePopupDialog();
mZoomControlsDelegate.dismissZoomPicker();
}
@@ -1651,6 +1688,18 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
}
/**
+ * @see View#dispatchHoverEvent(MotionEvent)
+ */
+ public boolean dispatchHoverEvent(MotionEvent event) {
+ if (mBrowserAccessibilityManager != null) {
+ return mBrowserAccessibilityManager.dispatchHoverEvent(event);
+ } else {
+ // ContentView.dispatchHoverEvent will call super.
+ return false;
+ }
+ }
+
+ /**
* @see View#scrollBy(int, int)
* Currently the ContentView scrolling happens in the native side. In
* the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
@@ -2184,6 +2233,8 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
mPendingRendererFrame = true;
+ if (mBrowserAccessibilityManager != null)
+ mBrowserAccessibilityManager.notifyFrameInfoInitialized();
}
@SuppressWarnings("unused")
@@ -2566,6 +2617,11 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
getContentViewClient().onStartContentIntent(getContext(), contentUrl);
}
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ setAccessibilityState(enabled);
+ }
+
/**
* Determines whether or not this ContentViewCore can handle this accessibility action.
* @param action The action to perform.
@@ -2594,9 +2650,39 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
}
/**
+ * Set the BrowserAccessibilityManager, used for native accessibility
+ * (not script injection). This is only set when system accessibility
+ * has been enabled.
+ * @param manager The new BrowserAccessibilityManager.
+ */
+ public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
+ mBrowserAccessibilityManager = manager;
+ }
+
+ /**
+ * Get the BrowserAccessibilityManager, used for native accessibility
+ * (not script injection). This will return null when system accessibility
+ * is not enabled.
+ * @return This view's BrowserAccessibilityManager.
+ */
+ public BrowserAccessibilityManager getBrowserAccessibilityManager() {
+ return mBrowserAccessibilityManager;
+ }
+
+ /**
+ * @see View#getAccessibilityNodeProvider(View host)
+ */
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ // Note: this is only used for native accessibility, i.e. without
+ // script injection.
+ return mBrowserAccessibilityManager;
+ }
+
+ /**
* @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
*/
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ // Note: this is only used by the script-injecting accessibility code.
mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
}
@@ -2604,6 +2690,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
* @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
*/
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ // Note: this is only used by the script-injecting accessibility code.
event.setClassName(this.getClass().getName());
// Identify where the top-left of the screen currently points to.
@@ -2625,6 +2712,26 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
}
/**
+ * Returns whether accessibility script injection is enabled on the device
+ */
+ public boolean onDeviceAccessibilityScriptInjectionEnabled() throws NoSuchFieldException,
bulach 2013/06/19 10:26:53 perhaps swallow these exceptions here rather than
dmazzoni 2013/06/19 19:54:41 Swallowed exceptions and renamed to isDeviceAccess
+ IllegalAccessException {
+ int result = getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERNET);
+ if (result != PackageManager.PERMISSION_GRANTED)
+ return false;
+
+ Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
+ field.setAccessible(true);
+ String ACCESSIBILITY_SCRIPT_INJECTION = (String) field.get(null);
+
+ boolean onDeviceScriptInjectionEnabled =
+ Settings.Secure.getInt(getContext().getContentResolver(),
+ ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1;
+ return onDeviceScriptInjectionEnabled;
+ }
+
+ /**
* Returns whether or not accessibility injection is being used.
*/
public boolean isInjectingAccessibilityScript() {
@@ -2632,10 +2739,45 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
}
/**
- * Enable or disable accessibility features.
+ * Turns browser accessibility on or off.
+ * If |state| is |false|, this turns off both native and injected accessibility.
+ * Otherwise, if accessibility script injection is enabled, this will enable the injected
+ * accessibility scripts, and if it is disabled this will enable the native accessibility.
*/
public void setAccessibilityState(boolean state) {
- mAccessibilityInjector.setScriptEnabled(state);
+ if (!state) {
+ setNativeAccessibilityState(false);
+ setInjectedAccessibility(false);
+ return;
+ }
+ boolean onDeviceScriptInjectionEnabled = false;
+ try {
+ onDeviceScriptInjectionEnabled = onDeviceAccessibilityScriptInjectionEnabled();
+ } catch (NoSuchFieldException e) {
+ } catch (IllegalAccessException e) {
+ }
+ if (onDeviceScriptInjectionEnabled) {
+ setNativeAccessibilityState(false);
+ setInjectedAccessibility(true);
+ } else {
+ setNativeAccessibilityState(true);
+ setInjectedAccessibility(false);
+ }
bulach 2013/06/19 10:26:53 these would probably be simpler as: boolean nativ
dmazzoni 2013/06/19 19:54:41 Done.
+ }
+
+ /**
+ * Enable or disable native accessibility features.
+ */
+ public void setNativeAccessibilityState(boolean enabled) {
+ nativeSetAccessibilityEnabled(mNativeContentViewCore, enabled);
+ }
+
+ /**
+ * Enable or disable injected accessibility features
+ */
+ public void setInjectedAccessibility(boolean enabled) {
+ mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
+ mAccessibilityInjector.setScriptEnabled(enabled);
}
/**
@@ -2968,4 +3110,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
private native void nativeDetachExternalVideoSurface(
int nativeContentViewCoreImpl, int playerId);
+
+ private native void nativeSetAccessibilityEnabled(
+ int nativeContentViewCoreImpl, boolean enabled);
bulach 2013/06/19 10:26:53 nit: indent
dmazzoni 2013/06/19 19:54:41 Done.
}

Powered by Google App Engine
This is Rietveld 408576698