This source file includes following definitions.
- newInstance
- injectAccessibilityScriptIntoPage
- addOrRemoveAccessibilityApisIfNecessary
- accessibilityIsAvailable
- setScriptEnabled
- didStartLoading
- didStopLoading
- onPageLostFocus
- onInitializeAccessibilityNodeInfo
- supportsAccessibilityAction
- performAccessibilityAction
- addAccessibilityApis
- removeAccessibilityApis
- getAxsUrlParameterValue
- getScreenReaderInjectingJs
- getAccessibilityManager
- SuppressWarnings
- hasVibrator
- SuppressWarnings
- vibrate
- SuppressWarnings
- vibrate
- SuppressWarnings
- cancel
- SuppressWarnings
- isSpeaking
- SuppressWarnings
- speak
- SuppressWarnings
- stop
- SuppressWarnings
- braille
- SuppressWarnings
- shutdownInternal
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.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import com.googlecode.eyesfree.braille.selfbraille.SelfBrailleClient;
import com.googlecode.eyesfree.braille.selfbraille.WriteData;
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.browser.WebContentsObserverAndroid;
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;
public class AccessibilityInjector extends WebContentsObserverAndroid {
private static final String TAG = "AccessibilityInjector";
protected ContentViewCore mContentViewCore;
private TextToSpeechWrapper mTextToSpeech;
private VibratorWrapper mVibrator;
private final boolean mHasVibratePermission;
private AccessibilityManager mAccessibilityManager;
protected boolean mInjectedScriptEnabled;
protected boolean mScriptInjected;
private final String mAccessibilityScreenReaderUrl;
private static final int FEEDBACK_BRAILLE = 0x00000020;
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";
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);" +
" })();";
private static final String TOGGLE_CHROME_VOX_JAVASCRIPT =
"(function() {" +
" if (typeof cvox !== 'undefined') {" +
" cvox.ChromeVox.host.activateOrDeactivateChromeVox(%1s);" +
" }" +
" })();";
public static AccessibilityInjector newInstance(ContentViewCore view) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return new AccessibilityInjector(view);
} else {
return new JellyBeanAccessibilityInjector(view);
}
}
protected AccessibilityInjector(ContentViewCore view) {
super(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;
}
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.evaluateJavaScript(js, null);
mInjectedScriptEnabled = true;
mScriptInjected = true;
}
}
public void addOrRemoveAccessibilityApisIfNecessary() {
if (accessibilityIsAvailable()) {
addAccessibilityApis();
} else {
removeAccessibilityApis();
}
}
public boolean accessibilityIsAvailable() {
if (!getAccessibilityManager().isEnabled() ||
mContentViewCore.getContentSettings() == null ||
!mContentViewCore.getContentSettings().getJavaScriptEnabled()) {
return false;
}
try {
List<AccessibilityServiceInfo> services =
getAccessibilityManager().getEnabledAccessibilityServiceList(
FEEDBACK_BRAILLE | AccessibilityServiceInfo.FEEDBACK_SPOKEN);
return services.size() > 0;
} catch (NullPointerException e) {
return false;
}
}
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.evaluateJavaScript(js, null);
if (!mInjectedScriptEnabled) {
onPageLostFocus();
}
}
}
@Override
public void didStartLoading(String url) {
mScriptInjected = false;
}
@Override
public void didStopLoading(String url) {
injectAccessibilityScriptIntoPage();
}
public void onPageLostFocus() {
if (mContentViewCore.isAlive()) {
if (mTextToSpeech != null) mTextToSpeech.stop();
if (mVibrator != null) mVibrator.cancel();
}
}
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { }
public boolean supportsAccessibilityAction(int action) {
return false;
}
public boolean performAccessibilityAction(int action, Bundle arguments) {
return false;
}
protected void addAccessibilityApis() {
Context context = mContentViewCore.getContext();
if (context != null) {
if (mTextToSpeech == null) {
mTextToSpeech = new TextToSpeechWrapper(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.getUrl() == null) return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
try {
List<NameValuePair> params = URLEncodedUtils.parse(new URI(mContentViewCore.getUrl()),
null);
for (NameValuePair param : params) {
if ("axs".equals(param.getName())) {
return Integer.parseInt(param.getValue());
}
}
} catch (URISyntaxException ex) {
} catch (NumberFormatException ex) {
} catch (IllegalArgumentException ex) {
}
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;
}
private static class VibratorWrapper {
private static final long MAX_VIBRATE_DURATION_MS = 5000;
private 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();
}
}
private static class TextToSpeechWrapper {
private TextToSpeech mTextToSpeech;
private SelfBrailleClient mSelfBrailleClient;
private View mView;
public TextToSpeechWrapper(View view, Context context) {
mView = view;
mTextToSpeech = new TextToSpeech(context, null, null);
mSelfBrailleClient = new SelfBrailleClient(context, CommandLine.getInstance().hasSwitch(
ContentSwitches.ACCESSIBILITY_DEBUG_BRAILLE_SERVICE));
}
@JavascriptInterface
@SuppressWarnings("unused")
public boolean isSpeaking() {
return mTextToSpeech.isSpeaking();
}
@JavascriptInterface
@SuppressWarnings("unused")
public int speak(String text, int queueMode, String jsonParams) {
HashMap<String, String> params = null;
try {
if (jsonParams != null) {
params = new HashMap<String, String>();
JSONObject json = new JSONObject(jsonParams);
@SuppressWarnings("unchecked")
Iterator<String> keyIt = json.keys();
while (keyIt.hasNext()) {
String key = keyIt.next();
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) {
try {
JSONObject jsonObj = new JSONObject(jsonString);
WriteData data = WriteData.forView(mView);
data.setText(jsonObj.getString("text"));
data.setSelectionStart(jsonObj.getInt("startIndex"));
data.setSelectionEnd(jsonObj.getInt("endIndex"));
mSelfBrailleClient.write(data);
} catch (JSONException ex) {
Log.w(TAG, "Error parsing JS JSON object", ex);
}
}
@SuppressWarnings("unused")
protected void shutdownInternal() {
mTextToSpeech.shutdown();
mSelfBrailleClient.shutdown();
}
}
}