aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKenny Root <kenny@the-b.org>2011-08-15 22:15:19 -0700
committerKenny Root <kenny@the-b.org>2011-08-15 22:15:19 -0700
commit4bbb0679673c385abe95f67453a40a4aac95bb6f (patch)
tree166aa945457f4d01621af0759ab2a7c403fb64a7 /src
parent94fd73f9907152a5f1808ff18362b858620ce325 (diff)
parentede09527eb446790ecfa71392fe6a30640b2742e (diff)
downloadconnectbot-4bbb0679673c385abe95f67453a40a4aac95bb6f.tar.gz
connectbot-4bbb0679673c385abe95f67453a40a4aac95bb6f.tar.bz2
connectbot-4bbb0679673c385abe95f67453a40a4aac95bb6f.zip
Merge Casey Burkhardt's accessibility patches
Conflicts: src/org/connectbot/TerminalView.java
Diffstat (limited to 'src')
-rwxr-xr-x[-rw-r--r--]src/org/connectbot/TerminalView.java124
-rw-r--r--src/org/connectbot/service/Relay.java1
-rw-r--r--src/org/connectbot/service/TerminalBridge.java6
3 files changed, 131 insertions, 0 deletions
diff --git a/src/org/connectbot/TerminalView.java b/src/org/connectbot/TerminalView.java
index 0f86ba4..e2f257c 100644..100755
--- a/src/org/connectbot/TerminalView.java
+++ b/src/org/connectbot/TerminalView.java
@@ -17,22 +17,32 @@
package org.connectbot;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import org.connectbot.bean.SelectionArea;
import org.connectbot.service.FontSizeChangedListener;
import org.connectbot.service.TerminalBridge;
import org.connectbot.service.TerminalKeyListener;
import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelXorXfermode;
import android.graphics.RectF;
+import android.net.Uri;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -64,6 +74,17 @@ public class TerminalView extends View implements FontSizeChangedListener {
private String lastNotification = null;
private volatile boolean notifications = true;
+ // Related to Accessibility Features
+ private boolean accessibilityActive = false;
+ private StringBuffer accessibilityBuffer = null;
+ private Pattern controlCodes = null;
+ private Matcher codeMatcher = null;
+ private AccessibilityEventSender eventSender = null;
+ private AccessibilityStateTester stateTester = null;
+ private int ACCESSIBILITY_EVENT_THRESHOLD = 1000;
+ private static final String SCREENREADER_INTENT_ACTION = "android.accessibilityservice.AccessibilityService";
+ private static final String SCREENREADER_INTENT_CATEGORY = "android.accessibilityservice.category.FEEDBACK_SPOKEN";
+
public TerminalView(Context context, TerminalBridge bridge) {
super(context);
@@ -113,6 +134,10 @@ public class TerminalView extends View implements FontSizeChangedListener {
// connect our view up to the bridge
setOnKeyListener(bridge.getKeyHandler());
+
+ // Enable accessibility features if a screen reader is active.
+ stateTester = new AccessibilityStateTester();
+ new Thread(stateTester).start();
}
public void destroy() {
@@ -286,4 +311,103 @@ public class TerminalView extends View implements FontSizeChangedListener {
}
};
}
+
+ public StringBuffer getAccessibilityBuffer() {
+ return accessibilityBuffer;
+ }
+
+ public void propagateConsoleText(char[] rawText, int length) {
+ if (accessibilityActive) {
+ if (accessibilityBuffer == null) {
+ accessibilityBuffer = new StringBuffer();
+ }
+
+ for (int i = 0; i < length; ++i) {
+ accessibilityBuffer.append(rawText[i]);
+ }
+
+ if (eventSender != null) {
+ removeCallbacks(eventSender);
+ } else {
+ eventSender = new AccessibilityEventSender();
+ }
+ postDelayed(eventSender, ACCESSIBILITY_EVENT_THRESHOLD);
+ }
+ }
+
+ private class AccessibilityEventSender implements Runnable {
+ public void run() {
+ synchronized (accessibilityBuffer) {
+ // Strip console codes with regex matching control codes
+ if (controlCodes == null) {
+ controlCodes =
+ Pattern.compile("" + ((char) 27) + (char) 92 + ((char) 91) + "[^m]+[m|:]");
+ }
+ if (codeMatcher == null) {
+ codeMatcher = controlCodes.matcher(accessibilityBuffer);
+ } else {
+ codeMatcher.reset(accessibilityBuffer);
+ }
+ accessibilityBuffer = new StringBuffer(codeMatcher.replaceAll(" "));
+
+ // Apply Backspaces using backspace character sequence
+ String backspaceCode = "" + ((char) 8) + ((char) 27) + ((char) 91) + ((char) 75);
+ int i = accessibilityBuffer.indexOf(backspaceCode);
+ while (i != -1) {
+ if (i == 0) {
+ accessibilityBuffer = accessibilityBuffer.replace(
+ i, i + backspaceCode.length(), "");
+ } else {
+ accessibilityBuffer = accessibilityBuffer.replace(
+ i - 1, i + backspaceCode.length(), "");
+ }
+ i = accessibilityBuffer.indexOf(backspaceCode);
+ }
+
+ if (accessibilityBuffer.length() > 0) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+ event.setFromIndex(0);
+ event.setAddedCount(accessibilityBuffer.length());
+ event.getText().add(accessibilityBuffer);
+
+ sendAccessibilityEventUnchecked(event);
+ accessibilityBuffer.setLength(0);
+ }
+ }
+ }
+ }
+
+ private class AccessibilityStateTester implements Runnable {
+ public void run() {
+ // Restrict the set of intents to only accessibility services that
+ // have the category FEEDBACK_SPOKEN (aka, screen readers).
+ Intent screenReaderIntent = new Intent(SCREENREADER_INTENT_ACTION);
+ screenReaderIntent.addCategory(SCREENREADER_INTENT_CATEGORY);
+ List<ResolveInfo> screenReaders = context.getPackageManager().queryIntentServices(
+ screenReaderIntent, 0);
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = null;
+ int status = 0;
+ for (ResolveInfo screenReader : screenReaders) {
+ // All screen readers are expected to implement a content
+ // provider that responds to:
+ // content://<nameofpackage>.providers.StatusProvider
+ cursor = cr.query(Uri.parse("content://" + screenReader.serviceInfo.packageName
+ + ".providers.StatusProvider"), null, null, null, null);
+ if (cursor != null) {
+ cursor.moveToFirst();
+ // These content providers use a special cursor that only has
+ // one element, an integer that is 1 if the screen reader is running.
+ status = cursor.getInt(0);
+ cursor.close();
+ if (status == 1) {
+ accessibilityActive = true;
+ return;
+ }
+ }
+ }
+ accessibilityActive = false;
+ }
+ }
}
diff --git a/src/org/connectbot/service/Relay.java b/src/org/connectbot/service/Relay.java
index 2f5a55c..36672ec 100644
--- a/src/org/connectbot/service/Relay.java
+++ b/src/org/connectbot/service/Relay.java
@@ -133,6 +133,7 @@ public class Relay implements Runnable {
measurer.measure(charArray, 0, offset, wideAttribute, bridge.defaultPaint, charWidth);
buffer.putString(charArray, wideAttribute, 0, charBuffer.position());
+ bridge.propagateConsoleText(charArray, charBuffer.position());
charBuffer.clear();
bridge.redraw();
}
diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java
index d8e5f79..f25ba5d 100644
--- a/src/org/connectbot/service/TerminalBridge.java
+++ b/src/org/connectbot/service/TerminalBridge.java
@@ -647,6 +647,12 @@ public class TerminalBridge implements VDUDisplay {
return buffer;
}
+ public void propagateConsoleText(char[] rawText, int length) {
+ if (parent != null) {
+ parent.propagateConsoleText(rawText, length);
+ }
+ }
+
public void onDraw() {
int fg, bg;
synchronized (buffer) {