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(); |
- } |
- } |
-} |