| Index: content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java
|
| deleted file mode 100644
|
| index d07c4931dfb152ea992b1ae7d7e0052a1d153ba3..0000000000000000000000000000000000000000
|
| --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java
|
| +++ /dev/null
|
| @@ -1,457 +0,0 @@
|
| -// Copyright 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -package org.chromium.content.browser.accessibility;
|
| -
|
| -import android.accessibilityservice.AccessibilityServiceInfo;
|
| -import android.content.Context;
|
| -import android.content.pm.PackageManager;
|
| -import android.os.Build;
|
| -import android.os.Bundle;
|
| -import android.os.Vibrator;
|
| -import android.speech.tts.TextToSpeech;
|
| -import android.view.View;
|
| -import android.view.accessibility.AccessibilityManager;
|
| -import android.view.accessibility.AccessibilityNodeInfo;
|
| -
|
| -import org.apache.http.NameValuePair;
|
| -import org.apache.http.client.utils.URLEncodedUtils;
|
| -import org.chromium.base.CommandLine;
|
| -import org.chromium.content.browser.ContentViewCore;
|
| -import org.chromium.content.browser.JavascriptInterface;
|
| -import org.chromium.content.common.ContentSwitches;
|
| -import org.json.JSONException;
|
| -import org.json.JSONObject;
|
| -
|
| -import java.net.URI;
|
| -import java.net.URISyntaxException;
|
| -import java.util.HashMap;
|
| -import java.util.Iterator;
|
| -import java.util.List;
|
| -
|
| -/**
|
| - * Responsible for accessibility injection and management of a {@link ContentViewCore}.
|
| - */
|
| -public class AccessibilityInjector {
|
| - // The ContentView this injector is responsible for managing.
|
| - protected ContentViewCore mContentViewCore;
|
| -
|
| - // The Java objects that are exposed to JavaScript
|
| - private TextToSpeechWrapper mTextToSpeech;
|
| - private VibratorWrapper mVibrator;
|
| - private final boolean mHasVibratePermission;
|
| -
|
| - // Lazily loaded helper objects.
|
| - private AccessibilityManager mAccessibilityManager;
|
| -
|
| - // Whether or not we should be injecting the script.
|
| - protected boolean mInjectedScriptEnabled;
|
| - protected boolean mScriptInjected;
|
| -
|
| - private final String mAccessibilityScreenReaderUrl;
|
| -
|
| - // To support building against the JELLY_BEAN and not JELLY_BEAN_MR1 SDK we need to add this
|
| - // constant here.
|
| - private static final int FEEDBACK_BRAILLE = 0x00000020;
|
| -
|
| - // constants for determining script injection strategy
|
| - private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
|
| - private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
|
| - private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
|
| - private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
|
| - private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE_2 = "accessibility2";
|
| -
|
| - // Template for JavaScript that injects a screen-reader.
|
| - private static final String DEFAULT_ACCESSIBILITY_SCREEN_READER_URL =
|
| - "https://ssl.gstatic.com/accessibility/javascript/android/chromeandroidvox.js";
|
| -
|
| - private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
|
| - "(function() {"
|
| - + " var chooser = document.createElement('script');"
|
| - + " chooser.type = 'text/javascript';"
|
| - + " chooser.src = '%1s';"
|
| - + " document.getElementsByTagName('head')[0].appendChild(chooser);"
|
| - + " })();";
|
| -
|
| - // JavaScript call to turn ChromeVox on or off.
|
| - private static final String TOGGLE_CHROME_VOX_JAVASCRIPT =
|
| - "(function() {"
|
| - + " if (typeof cvox !== 'undefined') {"
|
| - + " cvox.ChromeVox.host.activateOrDeactivateChromeVox(%1s);"
|
| - + " }"
|
| - + " })();";
|
| -
|
| - /**
|
| - * Returns an instance of the {@link AccessibilityInjector} based on the SDK version.
|
| - * @param view The ContentViewCore that this AccessibilityInjector manages.
|
| - * @return An instance of a {@link AccessibilityInjector}.
|
| - */
|
| - public static AccessibilityInjector newInstance(ContentViewCore view) {
|
| - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
| - return new LollipopAccessibilityInjector(view);
|
| - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
| - return new JellyBeanAccessibilityInjector(view);
|
| - } else {
|
| - return new AccessibilityInjector(view);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Creates an instance of the IceCreamSandwichAccessibilityInjector.
|
| - * @param view The ContentViewCore that this AccessibilityInjector manages.
|
| - */
|
| - protected AccessibilityInjector(ContentViewCore view) {
|
| - mContentViewCore = view;
|
| -
|
| - mAccessibilityScreenReaderUrl = CommandLine.getInstance().getSwitchValue(
|
| - ContentSwitches.ACCESSIBILITY_JAVASCRIPT_URL,
|
| - DEFAULT_ACCESSIBILITY_SCREEN_READER_URL);
|
| -
|
| - mHasVibratePermission = mContentViewCore.getContext().checkCallingOrSelfPermission(
|
| - android.Manifest.permission.VIBRATE) == PackageManager.PERMISSION_GRANTED;
|
| - }
|
| -
|
| - /**
|
| - * Injects a <script> tag into the current web site that pulls in the ChromeVox script for
|
| - * accessibility support. Only injects if accessibility is turned on by
|
| - * {@link AccessibilityManager#isEnabled()}, accessibility script injection is turned on, and
|
| - * javascript is enabled on this page.
|
| - *
|
| - * @see AccessibilityManager#isEnabled()
|
| - */
|
| - public void injectAccessibilityScriptIntoPage() {
|
| - if (!accessibilityIsAvailable()) return;
|
| -
|
| - int axsParameterValue = getAxsUrlParameterValue();
|
| - if (axsParameterValue != ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) {
|
| - return;
|
| - }
|
| -
|
| - String js = getScreenReaderInjectingJs();
|
| - if (mContentViewCore.isDeviceAccessibilityScriptInjectionEnabled()
|
| - && js != null && mContentViewCore.isAlive()) {
|
| - addOrRemoveAccessibilityApisIfNecessary();
|
| - mContentViewCore.getWebContents().evaluateJavaScript(js, null);
|
| - mInjectedScriptEnabled = true;
|
| - mScriptInjected = true;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Handles adding or removing accessibility related Java objects ({@link TextToSpeech} and
|
| - * {@link Vibrator}) interfaces from Javascript. This method should be called at a time when it
|
| - * is safe to add or remove these interfaces, specifically when the {@link ContentViewCore} is
|
| - * first initialized or right before the {@link ContentViewCore} is about to navigate to a URL
|
| - * or reload.
|
| - * <p>
|
| - * If this method is called at other times, the interfaces might not be correctly removed,
|
| - * meaning that Javascript can still access these Java objects that may have been already
|
| - * shut down.
|
| - */
|
| - public void addOrRemoveAccessibilityApisIfNecessary() {
|
| - if (accessibilityIsAvailable()) {
|
| - addAccessibilityApis();
|
| - } else {
|
| - removeAccessibilityApis();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Checks whether or not touch to explore is enabled on the system.
|
| - */
|
| - public boolean accessibilityIsAvailable() {
|
| - if (!getAccessibilityManager().isEnabled()
|
| - || !mContentViewCore.getContentViewClient().isJavascriptEnabled()) {
|
| - return false;
|
| - }
|
| -
|
| - try {
|
| - // Check that there is actually a service running that requires injecting this script.
|
| - List<AccessibilityServiceInfo> services =
|
| - getAccessibilityManager().getEnabledAccessibilityServiceList(
|
| - FEEDBACK_BRAILLE | AccessibilityServiceInfo.FEEDBACK_SPOKEN);
|
| - return services.size() > 0;
|
| - } catch (NullPointerException e) {
|
| - // getEnabledAccessibilityServiceList() can throw an NPE due to a bad
|
| - // AccessibilityService.
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Sets whether or not the script is enabled. If the script is disabled, we also stop any
|
| - * we output that is occurring. If the script has not yet been injected, injects it.
|
| - * @param enabled Whether or not to enable the script.
|
| - */
|
| - public void setScriptEnabled(boolean enabled) {
|
| - if (enabled && !mScriptInjected) injectAccessibilityScriptIntoPage();
|
| - if (!accessibilityIsAvailable() || mInjectedScriptEnabled == enabled) return;
|
| -
|
| - mInjectedScriptEnabled = enabled;
|
| - if (mContentViewCore.isAlive()) {
|
| - String js = String.format(TOGGLE_CHROME_VOX_JAVASCRIPT, Boolean.toString(
|
| - mInjectedScriptEnabled));
|
| - mContentViewCore.getWebContents().evaluateJavaScript(js, null);
|
| -
|
| - if (!mInjectedScriptEnabled) {
|
| - // Stop any TTS/Vibration right now.
|
| - onPageLostFocus();
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Notifies this handler that a page load has started, which means we should mark the
|
| - * accessibility script as not being injected. This way we can properly ignore incoming
|
| - * accessibility gesture events.
|
| - */
|
| - public void onPageLoadStarted() {
|
| - mScriptInjected = false;
|
| - }
|
| -
|
| - /**
|
| - * Notifies this handler that a page load has stopped, which means we can now inject the
|
| - * accessibility script.
|
| - */
|
| - public void onPageLoadStopped() {
|
| - injectAccessibilityScriptIntoPage();
|
| - }
|
| -
|
| - /**
|
| - * Stop any notifications that are currently going on (e.g. Text-to-Speech).
|
| - */
|
| - public void onPageLostFocus() {
|
| - if (mContentViewCore.isAlive()) {
|
| - if (mTextToSpeech != null) mTextToSpeech.stop();
|
| - if (mVibrator != null) mVibrator.cancel();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Initializes an {@link AccessibilityNodeInfo} with the actions and movement granularity
|
| - * levels supported by this {@link AccessibilityInjector}.
|
| - * <p>
|
| - * If an action identifier is added in this method, this {@link AccessibilityInjector} should
|
| - * also return {@code true} from {@link #supportsAccessibilityAction(int)}.
|
| - * </p>
|
| - *
|
| - * @param info The info to initialize.
|
| - * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
|
| - */
|
| - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { }
|
| -
|
| - /**
|
| - * Returns {@code true} if this {@link AccessibilityInjector} should handle the specified
|
| - * action.
|
| - *
|
| - * @param action An accessibility action identifier.
|
| - * @return {@code true} if this {@link AccessibilityInjector} should handle the specified
|
| - * action.
|
| - */
|
| - public boolean supportsAccessibilityAction(int action) {
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Performs the specified accessibility action.
|
| - *
|
| - * @param action The identifier of the action to perform.
|
| - * @param arguments The action arguments, or {@code null} if no arguments.
|
| - * @return {@code true} if the action was successful.
|
| - * @see View#performAccessibilityAction(int, Bundle)
|
| - */
|
| - public boolean performAccessibilityAction(int action, Bundle arguments) {
|
| - return false;
|
| - }
|
| -
|
| - protected void addAccessibilityApis() {
|
| - Context context = mContentViewCore.getContext();
|
| - if (context != null) {
|
| - // Enabled, we should try to add if we have to.
|
| - if (mTextToSpeech == null) {
|
| - mTextToSpeech = createTextToSpeechWrapper(mContentViewCore.getContainerView(),
|
| - context);
|
| - mContentViewCore.addJavascriptInterface(mTextToSpeech,
|
| - ALIAS_ACCESSIBILITY_JS_INTERFACE);
|
| - }
|
| -
|
| - if (mVibrator == null && mHasVibratePermission) {
|
| - mVibrator = new VibratorWrapper(context);
|
| - mContentViewCore.addJavascriptInterface(mVibrator,
|
| - ALIAS_ACCESSIBILITY_JS_INTERFACE_2);
|
| - }
|
| - }
|
| - }
|
| -
|
| - protected void removeAccessibilityApis() {
|
| - if (mTextToSpeech != null) {
|
| - mContentViewCore.removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
|
| - mTextToSpeech.stop();
|
| - mTextToSpeech.shutdownInternal();
|
| - mTextToSpeech = null;
|
| - }
|
| -
|
| - if (mVibrator != null) {
|
| - mContentViewCore.removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE_2);
|
| - mVibrator.cancel();
|
| - mVibrator = null;
|
| - }
|
| - }
|
| -
|
| - private int getAxsUrlParameterValue() {
|
| - if (mContentViewCore.getWebContents().getUrl() == null) {
|
| - return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
|
| - }
|
| -
|
| - try {
|
| - List<NameValuePair> params = URLEncodedUtils.parse(
|
| - new URI(mContentViewCore.getWebContents().getUrl()), null);
|
| -
|
| - for (NameValuePair param : params) {
|
| - if ("axs".equals(param.getName())) {
|
| - return Integer.parseInt(param.getValue());
|
| - }
|
| - }
|
| - } catch (URISyntaxException ex) {
|
| - // Intentional no-op.
|
| - } catch (NumberFormatException ex) {
|
| - // Intentional no-op.
|
| - } catch (IllegalArgumentException ex) {
|
| - // Intentional no-op.
|
| - }
|
| -
|
| - return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
|
| - }
|
| -
|
| - private String getScreenReaderInjectingJs() {
|
| - return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE,
|
| - mAccessibilityScreenReaderUrl);
|
| - }
|
| -
|
| - private AccessibilityManager getAccessibilityManager() {
|
| - if (mAccessibilityManager == null) {
|
| - mAccessibilityManager = (AccessibilityManager) mContentViewCore.getContext()
|
| - .getSystemService(Context.ACCESSIBILITY_SERVICE);
|
| - }
|
| -
|
| - return mAccessibilityManager;
|
| - }
|
| -
|
| - /**
|
| - * Used to protect how long JavaScript can vibrate for. This isn't a good comprehensive
|
| - * protection, just used to cover mistakes and protect against long vibrate durations/repeats.
|
| - *
|
| - * Also only exposes methods we *want* to expose, no others for the class.
|
| - */
|
| - private static class VibratorWrapper {
|
| - private static final long MAX_VIBRATE_DURATION_MS = 5000;
|
| -
|
| - private final Vibrator mVibrator;
|
| -
|
| - public VibratorWrapper(Context context) {
|
| - mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
| - }
|
| -
|
| - @JavascriptInterface
|
| - @SuppressWarnings("unused")
|
| - public boolean hasVibrator() {
|
| - return mVibrator.hasVibrator();
|
| - }
|
| -
|
| - @JavascriptInterface
|
| - @SuppressWarnings("unused")
|
| - public void vibrate(long milliseconds) {
|
| - milliseconds = Math.min(milliseconds, MAX_VIBRATE_DURATION_MS);
|
| - mVibrator.vibrate(milliseconds);
|
| - }
|
| -
|
| - @JavascriptInterface
|
| - @SuppressWarnings("unused")
|
| - public void vibrate(long[] pattern, int repeat) {
|
| - for (int i = 0; i < pattern.length; ++i) {
|
| - pattern[i] = Math.min(pattern[i], MAX_VIBRATE_DURATION_MS);
|
| - }
|
| -
|
| - repeat = -1;
|
| -
|
| - mVibrator.vibrate(pattern, repeat);
|
| - }
|
| -
|
| - @JavascriptInterface
|
| - @SuppressWarnings("unused")
|
| - public void cancel() {
|
| - mVibrator.cancel();
|
| - }
|
| - }
|
| -
|
| - protected TextToSpeechWrapper createTextToSpeechWrapper(View view, Context context) {
|
| - return new TextToSpeechWrapper(view, context);
|
| - }
|
| -
|
| - /**
|
| - * Used to protect the TextToSpeech class, only exposing the methods we want to expose.
|
| - */
|
| - protected static class TextToSpeechWrapper {
|
| - protected final TextToSpeech mTextToSpeech;
|
| - private final View mView;
|
| -
|
| - protected TextToSpeechWrapper(View view, Context context) {
|
| - mView = view;
|
| - mTextToSpeech = new TextToSpeech(context, null, null);
|
| - }
|
| -
|
| - @JavascriptInterface
|
| - @SuppressWarnings("unused")
|
| - public boolean isSpeaking() {
|
| - return mTextToSpeech.isSpeaking();
|
| - }
|
| -
|
| - @JavascriptInterface
|
| - @SuppressWarnings({"unused", "deprecation"})
|
| - public int speak(String text, int queueMode, String jsonParams) {
|
| - // Try to pull the params from the JSON string.
|
| - HashMap<String, String> params = null;
|
| - try {
|
| - if (jsonParams != null) {
|
| - params = new HashMap<String, String>();
|
| - JSONObject json = new JSONObject(jsonParams);
|
| -
|
| - // Using legacy API here.
|
| - @SuppressWarnings("unchecked")
|
| - Iterator<String> keyIt = json.keys();
|
| -
|
| - while (keyIt.hasNext()) {
|
| - String key = keyIt.next();
|
| - // Only add parameters that are raw data types.
|
| - if (json.optJSONObject(key) == null && json.optJSONArray(key) == null) {
|
| - params.put(key, json.getString(key));
|
| - }
|
| - }
|
| - }
|
| - } catch (JSONException e) {
|
| - params = null;
|
| - }
|
| -
|
| - return mTextToSpeech.speak(text, queueMode, params);
|
| - }
|
| -
|
| - @JavascriptInterface
|
| - @SuppressWarnings("unused")
|
| - public int stop() {
|
| - return mTextToSpeech.stop();
|
| - }
|
| -
|
| - @JavascriptInterface
|
| - @SuppressWarnings("unused")
|
| - public void braille(String jsonString) {
|
| - // This is here because AndroidVox depends on the existence
|
| - // of this method.
|
| - }
|
| -
|
| - @SuppressWarnings("unused")
|
| - protected void shutdownInternal() {
|
| - mTextToSpeech.shutdown();
|
| - }
|
| - }
|
| -}
|
|
|