diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java index a51a8f67cc646..d6576e16279f8 100644 --- a/core/java/android/webkit/AccessibilityInjector.java +++ b/core/java/android/webkit/AccessibilityInjector.java @@ -21,6 +21,10 @@ import android.os.Bundle; import android.os.SystemClock; import android.provider.Settings; import android.speech.tts.TextToSpeech; +import android.speech.tts.TextToSpeech.Engine; +import android.speech.tts.TextToSpeech.OnInitListener; +import android.speech.tts.UtteranceProgressListener; +import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; @@ -44,6 +48,10 @@ import java.util.concurrent.atomic.AtomicInteger; * APIs. */ class AccessibilityInjector { + private static final String TAG = AccessibilityInjector.class.getSimpleName(); + + private static boolean DEBUG = false; + // The WebViewClassic this injector is responsible for managing. private final WebViewClassic mWebViewClassic; @@ -90,6 +98,10 @@ class AccessibilityInjector { private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE = "cvox.AndroidVox.performAction('%1s')"; + // JS code used to shut down an active AndroidVox instance. + private static final String TOGGLE_CVOX_TEMPLATE = + "javascript:(function() { cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b); })();"; + /** * Creates an instance of the AccessibilityInjector based on * {@code webViewClassic}. @@ -117,6 +129,7 @@ class AccessibilityInjector { addTtsApis(); addCallbackApis(); + toggleAndroidVox(true); } /** @@ -126,10 +139,20 @@ class AccessibilityInjector { *
*/ public void removeAccessibilityApisIfNecessary() { + toggleAndroidVox(false); removeTtsApis(); removeCallbackApis(); } + private void toggleAndroidVox(boolean state) { + if (!mAccessibilityScriptInjected) { + return; + } + + final String code = String.format(TOGGLE_CVOX_TEMPLATE, state); + mWebView.loadUrl(code); + } + /** * Initializes an {@link AccessibilityNodeInfo} with the actions and * movement granularity levels supported by this @@ -196,7 +219,7 @@ class AccessibilityInjector { if (mAccessibilityScriptInjected) { return sendActionToAndroidVox(action, arguments); } - + if (mAccessibilityInjectorFallback != null) { return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments); } @@ -262,6 +285,9 @@ class AccessibilityInjector { */ public void onPageStarted(String url) { mAccessibilityScriptInjected = false; + if (DEBUG) + Log.w(TAG, "[" + mWebView.hashCode() + "] Started loading new page"); + addAccessibilityApisIfNecessary(); } /** @@ -282,15 +308,23 @@ class AccessibilityInjector { if (!shouldInjectJavaScript(url)) { mAccessibilityScriptInjected = false; toggleFallbackAccessibilityInjector(true); + if (DEBUG) + Log.d(TAG, "[" + mWebView.hashCode() + "] Using fallback accessibility support"); return; } toggleFallbackAccessibilityInjector(false); - final String injectionUrl = getScreenReaderInjectionUrl(); - mWebView.loadUrl(injectionUrl); - - mAccessibilityScriptInjected = true; + if (!mAccessibilityScriptInjected) { + mAccessibilityScriptInjected = true; + final String injectionUrl = getScreenReaderInjectionUrl(); + mWebView.loadUrl(injectionUrl); + if (DEBUG) + Log.d(TAG, "[" + mWebView.hashCode() + "] Loading screen reader into WebView"); + } else { + if (DEBUG) + Log.w(TAG, "[" + mWebView.hashCode() + "] Attempted to inject screen reader twice"); + } } /** @@ -368,6 +402,8 @@ class AccessibilityInjector { if (mTextToSpeech != null) { return; } + if (DEBUG) + Log.d(TAG, "[" + mWebView.hashCode() + "] Adding TTS APIs into WebView"); mTextToSpeech = new TextToSpeechWrapper(mContext); mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE); } @@ -381,6 +417,8 @@ class AccessibilityInjector { return; } + if (DEBUG) + Log.d(TAG, "[" + mWebView.hashCode() + "] Removing TTS APIs from WebView"); mWebView.removeJavascriptInterface(ALIAS_TTS_JS_INTERFACE); mTextToSpeech.stop(); mTextToSpeech.shutdown(); @@ -527,35 +565,141 @@ class AccessibilityInjector { * Used to protect the TextToSpeech class, only exposing the methods we want to expose. */ private static class TextToSpeechWrapper { - private TextToSpeech mTextToSpeech; + private static final String WRAP_TAG = TextToSpeechWrapper.class.getSimpleName(); + + private final HashMap