aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java22
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java39
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java28
-rw-r--r--java/src/com/android/inputmethod/latin/AccessibilityUtils.java211
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java14
6 files changed, 305 insertions, 13 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index de5e097e3..86e80ecac 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -336,7 +336,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
// state when shift key is pressed to go to normal mode.
// On the other hand, on distinct multi touch panel device, turning off the shift locked
// state with shift key pressing is handled by onReleaseShift().
- if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) {
+ if ((!hasDistinctMultitouch() || isAccessibilityEnabled())
+ && !shifted && latinKeyboard.isShiftLocked()) {
latinKeyboard.setShiftLocked(false);
}
if (latinKeyboard.setShifted(shifted)) {
@@ -434,6 +435,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
public void onPressShift() {
if (!isKeyboardAvailable())
return;
+ // If accessibility is enabled, disable momentary shift lock.
+ if (isAccessibilityEnabled())
+ return;
ShiftKeyState shiftKeyState = mShiftKeyState;
if (DEBUG_STATE)
Log.d(TAG, "onPressShift:"
@@ -469,6 +473,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
public void onReleaseShift() {
if (!isKeyboardAvailable())
return;
+ // If accessibility is enabled, disable momentary shift lock.
+ if (isAccessibilityEnabled())
+ return;
ShiftKeyState shiftKeyState = mShiftKeyState;
if (DEBUG_STATE)
Log.d(TAG, "onReleaseShift:"
@@ -494,6 +501,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
}
public void onPressSymbol() {
+ // If accessibility is enabled, disable momentary symbol lock.
+ if (isAccessibilityEnabled())
+ return;
if (DEBUG_STATE)
Log.d(TAG, "onPressSymbol:"
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
@@ -504,6 +514,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
}
public void onReleaseSymbol() {
+ // If accessibility is enabled, disable momentary symbol lock.
+ if (isAccessibilityEnabled())
+ return;
if (DEBUG_STATE)
Log.d(TAG, "onReleaseSymbol:"
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
@@ -516,6 +529,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
}
public void onOtherKeyPressed() {
+ // If accessibility is enabled, disable momentary mode locking.
+ if (isAccessibilityEnabled())
+ return;
if (DEBUG_STATE)
Log.d(TAG, "onOtherKeyPressed:"
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
@@ -574,6 +590,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
}
}
+ public boolean isAccessibilityEnabled() {
+ return mInputView != null && mInputView.isAccessibilityEnabled();
+ }
+
public boolean hasDistinctMultitouch() {
return mInputView != null && mInputView.hasDistinctMultitouch();
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 7fee022e0..d9526e4d6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -37,6 +37,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
@@ -146,6 +147,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
private final boolean mHasDistinctMultitouch;
private int mOldPointerCount = 1;
+ // Accessibility
+ private boolean mIsAccessibilityEnabled;
+
protected KeyDetector mKeyDetector = new ProximityKeyDetector();
// Swipe gesture detector
@@ -523,7 +527,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
}
/**
- * Return whether the device has distinct multi-touch panel.
+ * Returns whether the device has distinct multi-touch panel.
* @return true if the device has distinct multi-touch panel.
*/
@Override
@@ -532,6 +536,28 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
}
/**
+ * Enables or disables accessibility.
+ * @param accessibilityEnabled whether or not to enable accessibility
+ */
+ public void setAccessibilityEnabled(boolean accessibilityEnabled) {
+ mIsAccessibilityEnabled = accessibilityEnabled;
+
+ // Propagate this change to all existing pointer trackers.
+ for (PointerTracker tracker : mPointerTrackers) {
+ tracker.setAccessibilityEnabled(accessibilityEnabled);
+ }
+ }
+
+ /**
+ * Returns whether the device has accessibility enabled.
+ * @return true if the device has accessibility enabled.
+ */
+ @Override
+ public boolean isAccessibilityEnabled() {
+ return mIsAccessibilityEnabled;
+ }
+
+ /**
* Enables or disables the key feedback popup. This is a popup that shows a magnified
* version of the depressed key. By default the preview is enabled.
* @param previewEnabled whether or not to enable the key feedback popup
@@ -1210,15 +1236,18 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
// TODO: cleanup this code into a multi-touch to single-touch event converter class?
// If the device does not have distinct multi-touch support panel, ignore all multi-touch
// events except a transition from/to single-touch.
- if (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
+ if ((!mHasDistinctMultitouch || mIsAccessibilityEnabled)
+ && pointerCount > 1 && oldPointerCount > 1) {
return true;
}
// Track the last few movements to look for spurious swipes.
mSwipeTracker.addMovement(me);
- // Gesture detector must be enabled only when mini-keyboard is not on the screen.
- if (mMiniKeyboardView == null
+ // Gesture detector must be enabled only when mini-keyboard is not on the screen and
+ // accessibility is not enabled.
+ // TODO: Reconcile gesture detection and accessibility features.
+ if (mMiniKeyboardView == null && !mIsAccessibilityEnabled
&& mGestureDetector != null && mGestureDetector.onTouchEvent(me)) {
dismissKeyPreview();
mHandler.cancelKeyTimers();
@@ -1263,7 +1292,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
// TODO: cleanup this code into a multi-touch to single-touch event converter class?
// Translate mutli-touch event to single-touch events on the device that has no distinct
// multi-touch panel.
- if (!mHasDistinctMultitouch) {
+ if (!mHasDistinctMultitouch || mIsAccessibilityEnabled) {
// Use only main (id=0) pointer tracker.
PointerTracker tracker = getPointerTracker(0);
if (pointerCount == 1 && oldPointerCount == 2) {
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 6c3ad5edb..77e9caecc 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -145,6 +145,10 @@ public class LatinKeyboardView extends KeyboardView {
// If device has distinct multi touch panel, there is no need to check sudden jump.
if (hasDistinctMultitouch())
return false;
+ // If accessibiliy is enabled, stop looking for sudden jumps because it interferes
+ // with touch exploration of the keyboard.
+ if (isAccessibilityEnabled())
+ return false;
final int action = me.getAction();
final int x = (int) me.getX();
final int y = (int) me.getY();
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index ac375473a..8e9d02247 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -38,6 +38,7 @@ public class PointerTracker {
public void invalidateKey(Key key);
public void showPreview(int keyIndex, PointerTracker tracker);
public boolean hasDistinctMultitouch();
+ public boolean isAccessibilityEnabled();
}
public final int mPointerId;
@@ -68,6 +69,9 @@ public class PointerTracker {
private final PointerTrackerKeyState mKeyState;
+ // true if accessibility is enabled in the parent keyboard
+ private boolean mIsAccessibilityEnabled;
+
// true if keyboard layout has been changed.
private boolean mKeyboardLayoutHasBeenChanged;
@@ -112,6 +116,7 @@ public class PointerTracker {
mKeyDetector = keyDetector;
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
mKeyState = new PointerTrackerKeyState(keyDetector);
+ mIsAccessibilityEnabled = proxy.isAccessibilityEnabled();
mHasDistinctMultitouch = proxy.hasDistinctMultitouch();
mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
@@ -128,6 +133,10 @@ public class PointerTracker {
mListener = listener;
}
+ public void setAccessibilityEnabled(boolean accessibilityEnabled) {
+ mIsAccessibilityEnabled = accessibilityEnabled;
+ }
+
// Returns true if keyboard has been changed by this callback.
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
if (DEBUG_LISTENER)
@@ -312,9 +321,10 @@ public class PointerTracker {
private void onDownEventInternal(int x, int y, long eventTime) {
int keyIndex = mKeyState.onDownKey(x, y, eventTime);
// Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
- // from modifier key, or 3) this pointer is on mini-keyboard.
+ // from modifier key, 3) this pointer is on mini-keyboard, or 4) accessibility is enabled.
mIsAllowedSlidingKeyInput = mConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
- || mKeyDetector instanceof MiniKeyboardKeyDetector;
+ || mKeyDetector instanceof MiniKeyboardKeyDetector
+ || mIsAccessibilityEnabled;
mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false;
mIsRepeatableKey = false;
@@ -327,7 +337,9 @@ public class PointerTracker {
keyIndex = mKeyState.onDownKey(x, y, eventTime);
}
if (isValidKeyIndex(keyIndex)) {
- if (mKeys[keyIndex].mRepeatable) {
+ // Accessibility disables key repeat because users may need to pause on a key to hear
+ // its spoken description.
+ if (mKeys[keyIndex].mRepeatable && !mIsAccessibilityEnabled) {
repeatKey(keyIndex);
mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this);
mIsRepeatableKey = true;
@@ -517,8 +529,9 @@ public class PointerTracker {
updateKeyGraphics(keyIndex);
// The modifier key, such as shift key, should not be shown as preview when multi-touch is
// supported. On the other hand, if multi-touch is not supported, the modifier key should
- // be shown as preview.
- if (mHasDistinctMultitouch && isModifier()) {
+ // be shown as preview. If accessibility is turned on, the modifier key should be shown as
+ // preview.
+ if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled) {
mProxy.showPreview(NOT_A_KEY, this);
} else {
mProxy.showPreview(keyIndex, this);
@@ -526,6 +539,11 @@ public class PointerTracker {
}
private void startLongPressTimer(int keyIndex) {
+ // Accessibility disables long press because users are likely to need to pause on a key
+ // for an unspecified duration in order to hear the key's spoken description.
+ if (mIsAccessibilityEnabled) {
+ return;
+ }
Key key = getKey(keyIndex);
if (key.mCode == Keyboard.CODE_SHIFT) {
mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this);
diff --git a/java/src/com/android/inputmethod/latin/AccessibilityUtils.java b/java/src/com/android/inputmethod/latin/AccessibilityUtils.java
new file mode 100644
index 000000000..cd3f9e0ad
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/AccessibilityUtils.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.TypedArray;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility functions for accessibility support.
+ */
+public class AccessibilityUtils {
+ /** Shared singleton instance. */
+ private static final AccessibilityUtils sInstance = new AccessibilityUtils();
+ private /* final */ LatinIME mService;
+ private /* final */ AccessibilityManager mAccessibilityManager;
+ private /* final */ Map<Integer, CharSequence> mDescriptions;
+
+ /**
+ * Returns a shared instance of AccessibilityUtils.
+ *
+ * @return A shared instance of AccessibilityUtils.
+ */
+ public static AccessibilityUtils getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * Initializes (or re-initializes) the shared instance of AccessibilityUtils
+ * with the specified parent service and preferences.
+ *
+ * @param service The parent input method service.
+ * @param prefs The parent preferences.
+ */
+ public static void init(LatinIME service, SharedPreferences prefs) {
+ sInstance.initialize(service, prefs);
+ }
+
+ private AccessibilityUtils() {
+ // This class is not publicly instantiable.
+ }
+
+ /**
+ * Initializes (or re-initializes) with the specified parent service and
+ * preferences.
+ *
+ * @param service The parent input method service.
+ * @param prefs The parent preferences.
+ */
+ private void initialize(LatinIME service, SharedPreferences prefs) {
+ mService = service;
+ mAccessibilityManager = (AccessibilityManager) service.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ mDescriptions = null;
+ }
+
+ /**
+ * Returns true if accessibility is enabled.
+ *
+ * @return {@code true} if accessibility is enabled.
+ */
+ public boolean isAccessibilityEnabled() {
+ return mAccessibilityManager.isEnabled();
+ }
+
+ /**
+ * Speaks a key's action after it has been released. Does not speak letter
+ * keys since typed keys are already spoken aloud by TalkBack.
+ * <p>
+ * No-op if accessibility is not enabled.
+ * </p>
+ *
+ * @param primaryCode The primary code of the released key.
+ * @param switcher The input method's {@link KeyboardSwitcher}.
+ */
+ public void onRelease(int primaryCode, KeyboardSwitcher switcher) {
+ if (!isAccessibilityEnabled()) {
+ return;
+ }
+
+ int resId = -1;
+
+ switch (primaryCode) {
+ case Keyboard.CODE_SHIFT: {
+ if (switcher.isShiftedOrShiftLocked()) {
+ resId = R.string.description_shift_on;
+ } else {
+ resId = R.string.description_shift_off;
+ }
+ break;
+ }
+
+ case Keyboard.CODE_SWITCH_ALPHA_SYMBOL: {
+ if (switcher.isAlphabetMode()) {
+ resId = R.string.description_symbols_off;
+ } else {
+ resId = R.string.description_symbols_on;
+ }
+ break;
+ }
+ }
+
+ if (resId >= 0) {
+ speakDescription(mService.getResources().getText(resId));
+ }
+ }
+
+ /**
+ * Speaks a key's description for accessibility. If a key has an explicit
+ * description defined in keycodes.xml, that will be used. Otherwise, if the
+ * key is a Unicode character, then its character will be used.
+ * <p>
+ * No-op if accessibility is not enabled.
+ * </p>
+ *
+ * @param primaryCode The primary code of the pressed key.
+ * @param switcher The input method's {@link KeyboardSwitcher}.
+ */
+ public void onPress(int primaryCode, KeyboardSwitcher switcher) {
+ if (!isAccessibilityEnabled()) {
+ return;
+ }
+
+ // TODO Use the current keyboard state to read "Switch to symbols"
+ // instead of just "Symbols" (and similar for shift key).
+ CharSequence description = describeKey(primaryCode);
+ if (description == null && Character.isDefined((char) primaryCode)) {
+ description = Character.toString((char) primaryCode);
+ }
+
+ if (description != null) {
+ speakDescription(description);
+ }
+ }
+
+ /**
+ * Returns a text description for a given key code. If the key does not have
+ * an explicit description, returns <code>null</code>.
+ *
+ * @param keyCode An integer key code.
+ * @return A {@link CharSequence} describing the key or <code>null</code> if
+ * no description is available.
+ */
+ private CharSequence describeKey(int keyCode) {
+ // If not loaded yet, load key descriptions from XML file.
+ if (mDescriptions == null) {
+ mDescriptions = loadDescriptions();
+ }
+
+ return mDescriptions.get(keyCode);
+ }
+
+ /**
+ * Loads key descriptions from resources.
+ */
+ private Map<Integer, CharSequence> loadDescriptions() {
+ final Map<Integer, CharSequence> descriptions = new HashMap<Integer, CharSequence>();
+ final TypedArray array = mService.getResources().obtainTypedArray(R.array.key_descriptions);
+
+ // Key descriptions are stored as a key code followed by a string.
+ for (int i = 0; i < array.length() - 1; i += 2) {
+ int code = array.getInteger(i, 0);
+ CharSequence desc = array.getText(i + 1);
+
+ descriptions.put(code, desc);
+ }
+
+ array.recycle();
+
+ return descriptions;
+ }
+
+ /**
+ * Sends a character sequence to be read aloud.
+ *
+ * @param description The {@link CharSequence} to be read aloud.
+ */
+ private void speakDescription(CharSequence description) {
+ // TODO We need to add an AccessibilityEvent type for IMEs.
+ final AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+ event.setPackageName(mService.getPackageName());
+ event.setClassName(getClass().getName());
+ event.setAddedCount(description.length());
+ event.getText().add(description);
+
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 40cebb395..1e6238e69 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -152,6 +152,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private boolean mIsSettingsSuggestionStripOn;
private boolean mApplicationSpecifiedCompletionOn;
+ private AccessibilityUtils mAccessibilityUtils;
+
private final StringBuilder mComposing = new StringBuilder();
private WordComposer mWord = new WordComposer();
private CharSequence mBestWord;
@@ -357,6 +359,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
LatinImeLogger.init(this, prefs);
SubtypeSwitcher.init(this, prefs);
KeyboardSwitcher.init(this, prefs);
+ AccessibilityUtils.init(this, prefs);
super.onCreate();
@@ -364,6 +367,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputMethodId = Utils.getInputMethodId(mImm, getPackageName());
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
+ mAccessibilityUtils = AccessibilityUtils.getInstance();
final Resources res = getResources();
mResources = res;
@@ -546,8 +550,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
updateCorrectionMode();
+ final boolean accessibilityEnabled = mAccessibilityUtils.isAccessibilityEnabled();
+
inputView.setPreviewEnabled(mPopupOn);
inputView.setProximityCorrectionEnabled(true);
+ inputView.setAccessibilityEnabled(accessibilityEnabled);
// If we just entered a text field, maybe it has some old text that requires correction
checkReCorrectionOnStart();
inputView.setForeground(true);
@@ -1073,6 +1080,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
mLastKeyTime = when;
KeyboardSwitcher switcher = mKeyboardSwitcher;
+ final boolean accessibilityEnabled = switcher.isAccessibilityEnabled();
final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
switch (primaryCode) {
case Keyboard.CODE_DELETE:
@@ -1082,12 +1090,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
break;
case Keyboard.CODE_SHIFT:
// Shift key is handled in onPress() when device has distinct multi-touch panel.
- if (!distinctMultiTouch)
+ if (!distinctMultiTouch || accessibilityEnabled)
switcher.toggleShift();
break;
case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
// Symbol key is handled in onPress() when device has distinct multi-touch panel.
- if (!distinctMultiTouch)
+ if (!distinctMultiTouch || accessibilityEnabled)
switcher.changeKeyboardMode();
break;
case Keyboard.CODE_CANCEL:
@@ -1913,6 +1921,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
switcher.onOtherKeyPressed();
}
+ mAccessibilityUtils.onPress(primaryCode, switcher);
}
@Override
@@ -1926,6 +1935,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
switcher.onReleaseSymbol();
}
+ mAccessibilityUtils.onRelease(primaryCode, switcher);
}