aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/accessibility
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/accessibility')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java376
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java36
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java96
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java18
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java250
-rw-r--r--java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java6
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java151
7 files changed, 651 insertions, 282 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
new file mode 100644
index 000000000..dd43166af
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2012 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.accessibility;
+
+import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
+import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardView;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Exposes a virtual view sub-tree for {@link KeyboardView} and generates
+ * {@link AccessibilityEvent}s for individual {@link Key}s.
+ * <p>
+ * A virtual sub-tree is composed of imaginary {@link View}s that are reported
+ * as a part of the view hierarchy for accessibility purposes. This enables
+ * custom views that draw complex content to report them selves as a tree of
+ * virtual views, thus conveying their logical structure.
+ * </p>
+ */
+public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat {
+ private static final String TAG = AccessibilityEntityProvider.class.getSimpleName();
+
+ private final KeyboardView mKeyboardView;
+ private final InputMethodService mInputMethodService;
+ private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper;
+ private final AccessibilityUtils mAccessibilityUtils;
+
+ /** A map of integer IDs to {@link Key}s. */
+ private final SparseArray<Key> mVirtualViewIdToKey = new SparseArray<Key>();
+
+ /** Temporary rect used to calculate in-screen bounds. */
+ private final Rect mTempBoundsInScreen = new Rect();
+
+ /** The parent view's cached on-screen location. */
+ private final int[] mParentLocation = new int[2];
+
+ public AccessibilityEntityProvider(KeyboardView keyboardView, InputMethodService inputMethod) {
+ mKeyboardView = keyboardView;
+ mInputMethodService = inputMethod;
+
+ mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance();
+ mAccessibilityUtils = AccessibilityUtils.getInstance();
+
+ assignVirtualViewIds();
+ updateParentLocation();
+
+ // Ensure that the on-screen bounds are cleared when the layout changes.
+ mKeyboardView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
+ }
+
+ /**
+ * Creates and populates an {@link AccessibilityEvent} for the specified key
+ * and event type.
+ *
+ * @param key A key on the host keyboard view.
+ * @param eventType The event type to create.
+ * @return A populated {@link AccessibilityEvent} for the key.
+ * @see AccessibilityEvent
+ */
+ public AccessibilityEvent createAccessibilityEvent(Key key, int eventType) {
+ final int virtualViewId = generateVirtualViewIdForKey(key);
+ final String keyDescription = getKeyDescription(key);
+
+ final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ event.setPackageName(mKeyboardView.getContext().getPackageName());
+ event.setClassName(key.getClass().getName());
+ event.getText().add(keyDescription);
+
+ final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event);
+ record.setSource(mKeyboardView, virtualViewId);
+
+ return event;
+ }
+
+ /**
+ * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual
+ * view, i.e. a descendant of the host View, with the given <code>virtualViewId</code> or
+ * the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of
+ * the view hierarchy for accessibility purposes. This enables custom views
+ * that draw complex content to report them selves as a tree of virtual
+ * views, thus conveying their logical structure.
+ * </p>
+ * <p>
+ * The implementer is responsible for obtaining an accessibility node info
+ * from the pool of reusable instances and setting the desired properties of
+ * the node info before returning it.
+ * </p>
+ *
+ * @param virtualViewId A client defined virtual view id.
+ * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual
+ * descendant or the host View.
+ * @see AccessibilityNodeInfoCompat
+ */
+ @Override
+ public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
+ AccessibilityNodeInfoCompat info = null;
+
+ if (virtualViewId == View.NO_ID) {
+ // We are requested to create an AccessibilityNodeInfo describing
+ // this View, i.e. the root of the virtual sub-tree.
+ info = AccessibilityNodeInfoCompat.obtain(mKeyboardView);
+ ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, info);
+
+ // Add the virtual children of the root View.
+ // TODO: Need to assign a unique ID to each key.
+ final Keyboard keyboard = mKeyboardView.getKeyboard();
+ final Key[] keys = keyboard.mKeys;
+ for (Key key : keys) {
+ final int childVirtualViewId = generateVirtualViewIdForKey(key);
+ info.addChild(mKeyboardView, childVirtualViewId);
+ }
+ } else {
+ // Find the view that corresponds to the given id.
+ final Key key = mVirtualViewIdToKey.get(virtualViewId);
+ if (key == null) {
+ Log.e(TAG, "Invalid virtual view ID: " + virtualViewId);
+ return null;
+ }
+
+ final String keyDescription = getKeyDescription(key);
+ final Rect boundsInParent = key.mHitBox;
+
+ // Calculate the key's in-screen bounds.
+ mTempBoundsInScreen.set(boundsInParent);
+ mTempBoundsInScreen.offset(mParentLocation[0], mParentLocation[1]);
+
+ final Rect boundsInScreen = mTempBoundsInScreen;
+
+ // Obtain and initialize an AccessibilityNodeInfo with
+ // information about the virtual view.
+ info = AccessibilityNodeInfoCompat.obtain();
+ info.addAction(AccessibilityNodeInfoCompat.ACTION_SELECT);
+ info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION);
+ info.setPackageName(mKeyboardView.getContext().getPackageName());
+ info.setClassName(key.getClass().getName());
+ info.setBoundsInParent(boundsInParent);
+ info.setBoundsInScreen(boundsInScreen);
+ info.setParent(mKeyboardView);
+ info.setSource(mKeyboardView, virtualViewId);
+ info.setBoundsInScreen(boundsInScreen);
+ info.setText(keyDescription);
+ }
+
+ return info;
+ }
+
+ /**
+ * Performs an accessibility action on a virtual view, i.e. a descendant of
+ * the host View, with the given <code>virtualViewId</code> or the host View itself if
+ * <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ *
+ * @param action The action to perform.
+ * @param virtualViewId A client defined virtual view id.
+ * @return True if the action was performed.
+ * @see #createAccessibilityNodeInfo(int)
+ * @see AccessibilityNodeInfoCompat
+ */
+ @Override
+ public boolean performAccessibilityAction(int action, int virtualViewId) {
+ if (virtualViewId == View.NO_ID) {
+ // Perform the action on the host View.
+ switch (action) {
+ case AccessibilityNodeInfoCompat.ACTION_SELECT:
+ if (!mKeyboardView.isSelected()) {
+ mKeyboardView.setSelected(true);
+ return mKeyboardView.isSelected();
+ }
+ break;
+ case AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION:
+ if (mKeyboardView.isSelected()) {
+ mKeyboardView.setSelected(false);
+ return !mKeyboardView.isSelected();
+ }
+ break;
+ }
+ } else {
+ // Find the view that corresponds to the given id.
+ final Key child = mVirtualViewIdToKey.get(virtualViewId);
+ if (child == null)
+ return false;
+
+ // Perform the action on a virtual view.
+ switch (action) {
+ case AccessibilityNodeInfoCompat.ACTION_SELECT:
+ // TODO: Provide some focus indicator.
+ return true;
+ case AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION:
+ // TODO: Provide some clear focus indicator.
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Finds {@link AccessibilityNodeInfoCompat}s by text. The match is case
+ * insensitive containment. The search is relative to the virtual view, i.e.
+ * a descendant of the host View, with the given <code>virtualViewId</code> or the host
+ * View itself <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ *
+ * @param virtualViewId A client defined virtual view id which defined the
+ * root of the tree in which to perform the search.
+ * @param text The searched text.
+ * @return A list of node info.
+ * @see #createAccessibilityNodeInfo(int)
+ * @see AccessibilityNodeInfoCompat
+ */
+ @Override
+ public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(
+ String text, int virtualViewId) {
+ final String searchedLowerCase = text.toLowerCase();
+ final Keyboard keyboard = mKeyboardView.getKeyboard();
+
+ List<AccessibilityNodeInfoCompat> results = null;
+
+ if (virtualViewId == View.NO_ID) {
+ for (Key key : keyboard.mKeys) {
+ results = findByTextAndPopulate(searchedLowerCase, key, results);
+ }
+ } else {
+ final Key key = mVirtualViewIdToKey.get(virtualViewId);
+
+ results = findByTextAndPopulate(searchedLowerCase, key, results);
+ }
+
+ if (results == null) {
+ return Collections.emptyList();
+ }
+
+ return results;
+ }
+
+ /**
+ * Helper method for {@link #findAccessibilityNodeInfosByText(String, int)}.
+ * Takes a current set of results and matches a specified key against a
+ * lower-case search string. Returns an updated list of results.
+ *
+ * @param searchedLowerCase The lower-case search string.
+ * @param key The key to compare against.
+ * @param results The current list of results, or {@code null} if no results
+ * found.
+ * @return An updated list of results, or {@code null} if no results found.
+ */
+ private List<AccessibilityNodeInfoCompat> findByTextAndPopulate(String searchedLowerCase,
+ Key key, List<AccessibilityNodeInfoCompat> results) {
+ if (!keyContainsText(key, searchedLowerCase)) {
+ return results;
+ }
+
+ final int childVirtualViewId = generateVirtualViewIdForKey(key);
+ final AccessibilityNodeInfoCompat nodeInfo = createAccessibilityNodeInfo(
+ childVirtualViewId);
+
+ if (results == null) {
+ results = new LinkedList<AccessibilityNodeInfoCompat>();
+ }
+
+ results.add(nodeInfo);
+
+ return results;
+ }
+
+ /**
+ * Returns whether a key's current description contains the lower-case
+ * search text.
+ *
+ * @param key The key to compare against.
+ * @param textLowerCase The lower-case search string.
+ * @return {@code true} if the key contains the search text.
+ */
+ private boolean keyContainsText(Key key, String textLowerCase) {
+ if (key == null) {
+ return false;
+ }
+
+ final String description = getKeyDescription(key);
+
+ if (description == null) {
+ return false;
+ }
+
+ return description.toLowerCase().contains(textLowerCase);
+ }
+
+ /**
+ * Returns the context-specific description for a {@link Key}.
+ *
+ * @param key The key to describe.
+ * @return The context-specific description of the key.
+ */
+ private String getKeyDescription(Key key) {
+ final EditorInfo editorInfo = mInputMethodService.getCurrentInputEditorInfo();
+ final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo);
+ final String keyDescription = mKeyCodeDescriptionMapper.getDescriptionForKey(
+ mKeyboardView.getContext(), mKeyboardView.getKeyboard(), key, shouldObscure);
+
+ return keyDescription;
+ }
+
+ /**
+ * Assigns virtual view IDs to keyboard keys and populates the related maps.
+ */
+ private void assignVirtualViewIds() {
+ final Keyboard keyboard = mKeyboardView.getKeyboard();
+ if (keyboard == null) {
+ return;
+ }
+
+ mVirtualViewIdToKey.clear();
+
+ final Key[] keys = keyboard.mKeys;
+ for (Key key : keys) {
+ final int virtualViewId = generateVirtualViewIdForKey(key);
+ mVirtualViewIdToKey.put(virtualViewId, key);
+ }
+ }
+
+ /**
+ * Updates the parent's on-screen location.
+ */
+ private void updateParentLocation() {
+ mKeyboardView.getLocationOnScreen(mParentLocation);
+ }
+
+ /**
+ * Generates a virtual view identifier for the specified key.
+ *
+ * @param key The key to identify.
+ * @return A virtual view identifier.
+ */
+ private static int generateVirtualViewIdForKey(Key key) {
+ // The key code is unique within an instance of a Keyboard.
+ return key.mCode;
+ }
+
+ private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ assignVirtualViewIds();
+ updateParentLocation();
+ }
+ };
+}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index 46663327d..41da2aa4c 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -17,18 +17,18 @@
package com.android.inputmethod.accessibility;
import android.content.Context;
-import android.content.SharedPreferences;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.os.SystemClock;
import android.provider.Settings;
+import android.support.v4.view.MotionEventCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.EditorInfo;
-import com.android.inputmethod.compat.AccessibilityManagerCompatWrapper;
+import com.android.inputmethod.compat.AccessibilityManagerCompatUtils;
import com.android.inputmethod.compat.AudioManagerCompatWrapper;
import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.compat.MotionEventCompatUtils;
@@ -45,7 +45,6 @@ public class AccessibilityUtils {
private Context mContext;
private AccessibilityManager mAccessibilityManager;
- private AccessibilityManagerCompatWrapper mCompatManager;
private AudioManagerCompatWrapper mAudioManager;
/*
@@ -55,15 +54,15 @@ public class AccessibilityUtils {
*/
private static final boolean ENABLE_ACCESSIBILITY = true;
- public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
+ public static void init(InputMethodService inputMethod) {
if (!ENABLE_ACCESSIBILITY)
return;
// These only need to be initialized if the kill switch is off.
- sInstance.initInternal(inputMethod, prefs);
- KeyCodeDescriptionMapper.init(inputMethod, prefs);
- AccessibleInputMethodServiceProxy.init(inputMethod, prefs);
- AccessibleKeyboardViewProxy.init(inputMethod, prefs);
+ sInstance.initInternal(inputMethod);
+ KeyCodeDescriptionMapper.init();
+ AccessibleInputMethodServiceProxy.init(inputMethod);
+ AccessibleKeyboardViewProxy.init(inputMethod);
}
public static AccessibilityUtils getInstance() {
@@ -74,11 +73,10 @@ public class AccessibilityUtils {
// This class is not publicly instantiable.
}
- private void initInternal(Context context, SharedPreferences prefs) {
+ private void initInternal(Context context) {
mContext = context;
mAccessibilityManager = (AccessibilityManager) context
.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mCompatManager = new AccessibilityManagerCompatWrapper(mAccessibilityManager);
final AudioManager audioManager = (AudioManager) context
.getSystemService(Context.AUDIO_SERVICE);
@@ -95,7 +93,7 @@ public class AccessibilityUtils {
public boolean isTouchExplorationEnabled() {
return ENABLE_ACCESSIBILITY
&& mAccessibilityManager.isEnabled()
- && mCompatManager.isTouchExplorationEnabled();
+ && AccessibilityManagerCompatUtils.isTouchExplorationEnabled(mAccessibilityManager);
}
/**
@@ -111,17 +109,17 @@ public class AccessibilityUtils {
return action == MotionEventCompatUtils.ACTION_HOVER_ENTER
|| action == MotionEventCompatUtils.ACTION_HOVER_EXIT
- || action == MotionEventCompatUtils.ACTION_HOVER_MOVE;
+ || action == MotionEventCompat.ACTION_HOVER_MOVE;
}
/**
* Returns whether the device should obscure typed password characters.
* Typically this means speaking "dot" in place of non-control characters.
- *
+ *
* @return {@code true} if the device should obscure password characters.
*/
- public boolean shouldObscureInput(EditorInfo attribute) {
- if (attribute == null)
+ public boolean shouldObscureInput(EditorInfo editorInfo) {
+ if (editorInfo == null)
return false;
// The user can optionally force speaking passwords.
@@ -137,7 +135,7 @@ public class AccessibilityUtils {
return false;
// Don't speak if the IME is connected to a password field.
- return InputTypeCompatUtils.isPasswordInputType(attribute.inputType);
+ return InputTypeCompatUtils.isPasswordInputType(editorInfo.inputType);
}
/**
@@ -171,11 +169,11 @@ public class AccessibilityUtils {
* Handles speaking the "connect a headset to hear passwords" notification
* when connecting to a password field.
*
- * @param attribute The input connection's editor info attribute.
+ * @param editorInfo The input connection's editor info attribute.
* @param restarting Whether the connection is being restarted.
*/
- public void onStartInputViewInternal(EditorInfo attribute, boolean restarting) {
- if (shouldObscureInput(attribute)) {
+ public void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
+ if (shouldObscureInput(editorInfo)) {
final CharSequence text = mContext.getText(R.string.spoken_use_headphones);
speak(text);
}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
index 4ab9cb898..961176bb8 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
@@ -17,31 +17,15 @@
package com.android.inputmethod.accessibility;
import android.content.Context;
-import android.content.SharedPreferences;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
-import android.os.Looper;
-import android.os.Message;
import android.os.Vibrator;
-import android.text.TextUtils;
import android.view.KeyEvent;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActionListener {
private static final AccessibleInputMethodServiceProxy sInstance =
new AccessibleInputMethodServiceProxy();
- /*
- * Delay for the handler event that's fired when Accessibility is on and the
- * user hovers outside of any valid keys. This is used to let the user know
- * that if they lift their finger, nothing will be typed.
- */
- private static final long DELAY_NO_HOVER_SELECTION = 250;
-
/**
* Duration of the key click vibration in milliseconds.
*/
@@ -52,38 +36,9 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
private InputMethodService mInputMethod;
private Vibrator mVibrator;
private AudioManager mAudioManager;
- private AccessibilityHandler mAccessibilityHandler;
- private static class AccessibilityHandler
- extends StaticInnerHandlerWrapper<AccessibleInputMethodServiceProxy> {
- private static final int MSG_NO_HOVER_SELECTION = 0;
-
- public AccessibilityHandler(AccessibleInputMethodServiceProxy outerInstance,
- Looper looper) {
- super(outerInstance, looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_NO_HOVER_SELECTION:
- getOuterInstance().notifyNoHoverSelection();
- break;
- }
- }
-
- public void postNoHoverSelection() {
- removeMessages(MSG_NO_HOVER_SELECTION);
- sendEmptyMessageDelayed(MSG_NO_HOVER_SELECTION, DELAY_NO_HOVER_SELECTION);
- }
-
- public void cancelNoHoverSelection() {
- removeMessages(MSG_NO_HOVER_SELECTION);
- }
- }
-
- public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
- sInstance.initInternal(inputMethod, prefs);
+ public static void init(InputMethodService inputMethod) {
+ sInstance.initInternal(inputMethod);
}
public static AccessibleInputMethodServiceProxy getInstance() {
@@ -94,30 +49,10 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
// Not publicly instantiable.
}
- private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) {
+ private void initInternal(InputMethodService inputMethod) {
mInputMethod = inputMethod;
mVibrator = (Vibrator) inputMethod.getSystemService(Context.VIBRATOR_SERVICE);
mAudioManager = (AudioManager) inputMethod.getSystemService(Context.AUDIO_SERVICE);
- mAccessibilityHandler = new AccessibilityHandler(this, inputMethod.getMainLooper());
- }
-
- /**
- * If touch exploration is enabled, cancels the event sent by
- * {@link AccessibleInputMethodServiceProxy#onHoverExit(int)} because the
- * user is currently hovering above a key.
- */
- @Override
- public void onHoverEnter(int primaryCode) {
- mAccessibilityHandler.cancelNoHoverSelection();
- }
-
- /**
- * If touch exploration is enabled, sends a delayed event to notify the user
- * that they are not currently hovering above a key.
- */
- @Override
- public void onHoverExit(int primaryCode) {
- mAccessibilityHandler.postNoHoverSelection();
}
/**
@@ -125,8 +60,6 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
*/
@Override
public void onFlickGesture(int direction) {
- final int keyEventCode;
-
switch (direction) {
case FlickGestureDetector.FLICK_LEFT:
sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
@@ -148,27 +81,4 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, FX_VOLUME);
mInputMethod.sendDownUpKeyEvents(keyCode);
}
-
- /**
- * When Accessibility is turned on, notifies the user that they are not
- * currently hovering above a key. By default this will speak the currently
- * entered text.
- */
- private void notifyNoHoverSelection() {
- final ExtractedText extracted = mInputMethod.getCurrentInputConnection().getExtractedText(
- new ExtractedTextRequest(), 0);
-
- if (extracted == null)
- return;
-
- final CharSequence text;
-
- if (TextUtils.isEmpty(extracted.text)) {
- text = mInputMethod.getString(R.string.spoken_no_text_entered);
- } else {
- text = mInputMethod.getString(R.string.spoken_current_text_is, extracted.text);
- }
-
- AccessibilityUtils.getInstance().speak(text);
- }
}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java
index c1e92bec8..31d17d09f 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java
@@ -18,24 +18,6 @@ package com.android.inputmethod.accessibility;
public interface AccessibleKeyboardActionListener {
/**
- * Called when the user hovers inside a key. This is sent only when
- * Accessibility is turned on. For keys that repeat, this is only called
- * once.
- *
- * @param primaryCode the code of the key that was hovered over
- */
- public void onHoverEnter(int primaryCode);
-
- /**
- * Called when the user hovers outside a key. This is sent only when
- * Accessibility is turned on. For keys that repeat, this is only called
- * once.
- *
- * @param primaryCode the code of the key that was hovered over
- */
- public void onHoverExit(int primaryCode);
-
- /**
* @param direction the direction of the flick gesture, one of
* <ul>
* <li>{@link FlickGestureDetector#FLICK_UP}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index cef82267f..2401d93c6 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -17,35 +17,39 @@
package com.android.inputmethod.accessibility;
import android.content.Context;
-import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Paint;
import android.inputmethodservice.InputMethodService;
-import android.util.Log;
+import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.view.MotionEvent;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import android.view.inputmethod.EditorInfo;
-import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
import com.android.inputmethod.compat.MotionEventCompatUtils;
+import com.android.inputmethod.compat.ViewParentCompatUtils;
import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.LatinKeyboardView;
import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.latin.R;
-public class AccessibleKeyboardViewProxy {
- private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName();
+public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
private InputMethodService mInputMethod;
private FlickGestureDetector mGestureDetector;
private LatinKeyboardView mView;
private AccessibleKeyboardActionListener mListener;
+ private AccessibilityEntityProvider mAccessibilityNodeProvider;
- private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
+ private Key mLastHoverKey = null;
- public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
- sInstance.initInternal(inputMethod, prefs);
+ public static void init(InputMethodService inputMethod) {
+ sInstance.initInternal(inputMethod);
sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance();
}
@@ -53,15 +57,11 @@ public class AccessibleKeyboardViewProxy {
return sInstance;
}
- public static void setView(LatinKeyboardView view) {
- sInstance.mView = view;
- }
-
private AccessibleKeyboardViewProxy() {
// Not publicly instantiable.
}
- private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) {
+ private void initInternal(InputMethodService inputMethod) {
final Paint paint = new Paint();
paint.setTextAlign(Paint.Align.LEFT);
paint.setTextSize(14.0f);
@@ -72,35 +72,39 @@ public class AccessibleKeyboardViewProxy {
mGestureDetector = new KeyboardFlickGestureDetector(inputMethod);
}
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event,
- PointerTracker tracker) {
- if (mView == null) {
- Log.e(TAG, "No keyboard view set!");
- return false;
+ /**
+ * Sets the view wrapped by this proxy.
+ *
+ * @param view The view to wrap.
+ */
+ public void setView(LatinKeyboardView view) {
+ if (view == null) {
+ // Ignore null views.
+ return;
}
- switch (event.getEventType()) {
- case AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER:
- final Key key = tracker.getKey(mLastHoverKeyIndex);
-
- if (key == null)
- break;
-
- final EditorInfo info = mInputMethod.getCurrentInputEditorInfo();
- final boolean shouldObscure = AccessibilityUtils.getInstance().shouldObscureInput(info);
- final CharSequence description = KeyCodeDescriptionMapper.getInstance()
- .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key,
- shouldObscure);
+ mView = view;
- if (description == null)
- return false;
-
- event.getText().add(description);
+ // Ensure that the view has an accessibility delegate.
+ ViewCompat.setAccessibilityDelegate(view, this);
+ }
- break;
+ /**
+ * Proxy method for View.getAccessibilityNodeProvider(). This method is
+ * called in SDK version 15 and higher to obtain the virtual node hierarchy
+ * provider.
+ *
+ * @return The accessibility node provider for the current keyboard.
+ */
+ @Override
+ public AccessibilityEntityProvider getAccessibilityNodeProvider(View host) {
+ // Instantiate the provide only when requested. Since the system
+ // will call this method multiple times it is a good practice to
+ // cache the provider instance.
+ if (mAccessibilityNodeProvider == null) {
+ mAccessibilityNodeProvider = new AccessibilityEntityProvider(mView, mInputMethod);
}
-
- return true;
+ return mAccessibilityNodeProvider;
}
/**
@@ -123,53 +127,94 @@ public class AccessibleKeyboardViewProxy {
* @param event The touch exploration hover event.
* @return {@code true} if the event was handled
*/
- /*package*/ boolean onHoverEventInternal(MotionEvent event, PointerTracker tracker) {
+ /* package */boolean onHoverEventInternal(MotionEvent event, PointerTracker tracker) {
final int x = (int) event.getX();
final int y = (int) event.getY();
+ final Key key = tracker.getKeyOn(x, y);
+ final Key previousKey = mLastHoverKey;
+
+ mLastHoverKey = key;
switch (event.getAction()) {
case MotionEventCompatUtils.ACTION_HOVER_ENTER:
- case MotionEventCompatUtils.ACTION_HOVER_MOVE:
- final int keyIndex = tracker.getKeyIndexOn(x, y);
-
- if (keyIndex != mLastHoverKeyIndex) {
- fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false);
- mLastHoverKeyIndex = keyIndex;
- fireKeyHoverEvent(tracker, mLastHoverKeyIndex, true);
+ case MotionEventCompatUtils.ACTION_HOVER_EXIT:
+ return onHoverKey(key, event);
+ case MotionEventCompat.ACTION_HOVER_MOVE:
+ if (key != previousKey) {
+ return onTransitionKey(key, previousKey, event);
+ } else {
+ return onHoverKey(key, event);
}
-
- return true;
}
return false;
}
- private void fireKeyHoverEvent(PointerTracker tracker, int keyIndex, boolean entering) {
- if (mListener == null) {
- Log.e(TAG, "No accessible keyboard action listener set!");
- return;
- }
+ /**
+ * Simulates a transition between two {@link Key}s by sending a HOVER_EXIT
+ * on the previous key, a HOVER_ENTER on the current key, and a HOVER_MOVE
+ * on the current key.
+ *
+ * @param currentKey The currently hovered key.
+ * @param previousKey The previously hovered key.
+ * @param event The event that triggered the transition.
+ * @return {@code true} if the event was handled.
+ */
+ private boolean onTransitionKey(Key currentKey, Key previousKey, MotionEvent event) {
+ final int savedAction = event.getAction();
- if (mView == null) {
- Log.e(TAG, "No keyboard view set!");
- return;
- }
+ event.setAction(MotionEventCompatUtils.ACTION_HOVER_EXIT);
+ onHoverKey(previousKey, event);
- if (keyIndex == KeyDetector.NOT_A_KEY)
- return;
+ event.setAction(MotionEventCompatUtils.ACTION_HOVER_ENTER);
+ onHoverKey(currentKey, event);
- final Key key = tracker.getKey(keyIndex);
+ event.setAction(MotionEventCompat.ACTION_HOVER_MOVE);
+ final boolean handled = onHoverKey(currentKey, event);
- if (key == null)
- return;
+ event.setAction(savedAction);
+
+ return handled;
+ }
+
+ /**
+ * Handles a hover event on a key. If {@link Key} extended View, this would
+ * be analogous to calling View.onHoverEvent(MotionEvent).
+ *
+ * @param key The currently hovered key.
+ * @param event The hover event.
+ * @return {@code true} if the event was handled.
+ */
+ private boolean onHoverKey(Key key, MotionEvent event) {
+ // Null keys can't receive events.
+ if (key == null) {
+ return false;
+ }
- if (entering) {
- mListener.onHoverEnter(key.mCode);
- mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER);
- } else {
- mListener.onHoverExit(key.mCode);
- mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_EXIT);
+ switch (event.getAction()) {
+ case MotionEventCompatUtils.ACTION_HOVER_ENTER:
+ sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER);
+ break;
+ case MotionEventCompatUtils.ACTION_HOVER_EXIT:
+ sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT);
+ break;
}
+
+ return true;
+ }
+
+ /**
+ * Populates and sends an {@link AccessibilityEvent} for the specified key.
+ *
+ * @param key The key to send an event for.
+ * @param eventType The type of event to send.
+ */
+ private void sendAccessibilityEventForKey(Key key, int eventType) {
+ final AccessibilityEntityProvider nodeProvider = getAccessibilityNodeProvider(null);
+ final AccessibilityEvent event = nodeProvider.createAccessibilityEvent(key, eventType);
+
+ // Propagates the event up the view hierarchy.
+ ViewParentCompatUtils.requestSendAccessibilityEvent(mView.getParent(), mView, event);
}
private class KeyboardFlickGestureDetector extends FlickGestureDetector {
@@ -185,4 +230,71 @@ public class AccessibleKeyboardViewProxy {
return true;
}
}
+
+ /**
+ * Notifies the user of changes in the keyboard shift state.
+ */
+ public void notifyShiftState() {
+ final Keyboard keyboard = mView.getKeyboard();
+ final KeyboardId keyboardId = keyboard.mId;
+ final int elementId = keyboardId.mElementId;
+ final Context context = mView.getContext();
+ final CharSequence text;
+
+ switch (elementId) {
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
+ text = context.getText(R.string.spoken_description_shiftmode_locked);
+ break;
+ case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+ case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
+ text = context.getText(R.string.spoken_description_shiftmode_on);
+ break;
+ default:
+ text = context.getText(R.string.spoken_description_shiftmode_off);
+ }
+
+ AccessibilityUtils.getInstance().speak(text);
+ }
+
+ /**
+ * Notifies the user of changes in the keyboard symbols state.
+ */
+ public void notifySymbolsState() {
+ final Keyboard keyboard = mView.getKeyboard();
+ final Context context = mView.getContext();
+ final KeyboardId keyboardId = keyboard.mId;
+ final int elementId = keyboardId.mElementId;
+ final int resId;
+
+ switch (elementId) {
+ case KeyboardId.ELEMENT_ALPHABET:
+ case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
+ resId = R.string.spoken_description_mode_alpha;
+ break;
+ case KeyboardId.ELEMENT_SYMBOLS:
+ case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
+ resId = R.string.spoken_description_mode_symbol;
+ break;
+ case KeyboardId.ELEMENT_PHONE:
+ resId = R.string.spoken_description_mode_phone;
+ break;
+ case KeyboardId.ELEMENT_PHONE_SYMBOLS:
+ resId = R.string.spoken_description_mode_phone_shift;
+ break;
+ default:
+ resId = -1;
+ }
+
+ if (resId < 0) {
+ return;
+ }
+
+ final String text = context.getString(resId);
+ AccessibilityUtils.getInstance().speak(text);
+ }
}
diff --git a/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java b/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java
index 9d99e3131..eaa4ddff6 100644
--- a/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java
+++ b/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.accessibility;
import android.content.Context;
import android.os.Message;
+import android.support.v4.view.MotionEventCompat;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -32,7 +33,7 @@ import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
* properties:
* <ul>
* <li>Begins with a {@link MotionEventCompatUtils#ACTION_HOVER_ENTER} event
- * <li>Contains any number of {@link MotionEventCompatUtils#ACTION_HOVER_MOVE}
+ * <li>Contains any number of {@link MotionEventCompat#ACTION_HOVER_MOVE}
* events
* <li>Ends with a {@link MotionEventCompatUtils#ACTION_HOVER_EXIT} event
* <li>Maximum duration of 250 milliseconds
@@ -126,10 +127,9 @@ public abstract class FlickGestureDetector {
}
final float distanceSquare = calculateDistanceSquare(mCachedHoverEnter, event);
- final long timeout = event.getEventTime() - mCachedHoverEnter.getEventTime();
switch (event.getAction()) {
- case MotionEventCompatUtils.ACTION_HOVER_MOVE:
+ case MotionEventCompat.ACTION_HOVER_MOVE:
// Consume all valid move events before timeout.
return true;
case MotionEventCompatUtils.ACTION_HOVER_EXIT:
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 7302830d4..3d861c231 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -17,8 +17,8 @@
package com.android.inputmethod.accessibility;
import android.content.Context;
-import android.content.SharedPreferences;
import android.text.TextUtils;
+import android.util.Log;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
@@ -28,6 +28,8 @@ import com.android.inputmethod.latin.R;
import java.util.HashMap;
public class KeyCodeDescriptionMapper {
+ private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName();
+
// The resource ID of the string spoken for obscured keys
private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot;
@@ -39,14 +41,8 @@ public class KeyCodeDescriptionMapper {
// Map of key codes to spoken description resource IDs
private final HashMap<Integer, Integer> mKeyCodeMap;
- // Map of shifted key codes to spoken description resource IDs
- private final HashMap<Integer, Integer> mShiftedKeyCodeMap;
-
- // Map of shift-locked key codes to spoken description resource IDs
- private final HashMap<Integer, Integer> mShiftLockedKeyCodeMap;
-
- public static void init(Context context, SharedPreferences prefs) {
- sInstance.initInternal(context, prefs);
+ public static void init() {
+ sInstance.initInternal();
}
public static KeyCodeDescriptionMapper getInstance() {
@@ -56,40 +52,15 @@ public class KeyCodeDescriptionMapper {
private KeyCodeDescriptionMapper() {
mKeyLabelMap = new HashMap<CharSequence, Integer>();
mKeyCodeMap = new HashMap<Integer, Integer>();
- mShiftedKeyCodeMap = new HashMap<Integer, Integer>();
- mShiftLockedKeyCodeMap = new HashMap<Integer, Integer>();
}
- private void initInternal(Context context, SharedPreferences prefs) {
+ private void initInternal() {
// Manual label substitutions for key labels with no string resource
mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
// Symbols that most TTS engines can't speak
- mKeyCodeMap.put((int) '.', R.string.spoken_description_period);
- mKeyCodeMap.put((int) ',', R.string.spoken_description_comma);
- mKeyCodeMap.put((int) '(', R.string.spoken_description_left_parenthesis);
- mKeyCodeMap.put((int) ')', R.string.spoken_description_right_parenthesis);
- mKeyCodeMap.put((int) ':', R.string.spoken_description_colon);
- mKeyCodeMap.put((int) ';', R.string.spoken_description_semicolon);
- mKeyCodeMap.put((int) '!', R.string.spoken_description_exclamation_mark);
- mKeyCodeMap.put((int) '?', R.string.spoken_description_question_mark);
- mKeyCodeMap.put((int) '\"', R.string.spoken_description_double_quote);
- mKeyCodeMap.put((int) '\'', R.string.spoken_description_single_quote);
- mKeyCodeMap.put((int) '*', R.string.spoken_description_star);
- mKeyCodeMap.put((int) '#', R.string.spoken_description_pound);
mKeyCodeMap.put((int) ' ', R.string.spoken_description_space);
- // Non-ASCII symbols (must use escape codes!)
- mKeyCodeMap.put((int) '\u2022', R.string.spoken_description_dot);
- mKeyCodeMap.put((int) '\u221A', R.string.spoken_description_square_root);
- mKeyCodeMap.put((int) '\u03C0', R.string.spoken_description_pi);
- mKeyCodeMap.put((int) '\u0394', R.string.spoken_description_delta);
- mKeyCodeMap.put((int) '\u2122', R.string.spoken_description_trademark);
- mKeyCodeMap.put((int) '\u2105', R.string.spoken_description_care_of);
- mKeyCodeMap.put((int) '\u2026', R.string.spoken_description_ellipsis);
- mKeyCodeMap.put((int) '\u201E', R.string.spoken_description_low_double_quote);
- mKeyCodeMap.put((int) '\uFF0A', R.string.spoken_description_star);
-
// Special non-character codes defined in Keyboard
mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete);
mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return);
@@ -98,12 +69,6 @@ public class KeyCodeDescriptionMapper {
mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic);
mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab);
-
- // Shifted versions of non-character codes defined in Keyboard
- mShiftedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift_shifted);
-
- // Shift-locked versions of non-character codes defined in Keyboard
- mShiftLockedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_caps_lock);
}
/**
@@ -125,27 +90,31 @@ public class KeyCodeDescriptionMapper {
* @return a character sequence describing the action performed by pressing
* the key
*/
- public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key,
+ public String getDescriptionForKey(Context context, Keyboard keyboard, Key key,
boolean shouldObscure) {
- if (key.mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
- final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
+ final int code = key.mCode;
+
+ if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+ final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
if (description != null)
return description;
}
+ if (code == Keyboard.CODE_SHIFT) {
+ return getDescriptionForShiftKey(context, keyboard);
+ }
+
if (!TextUtils.isEmpty(key.mLabel)) {
final String label = key.mLabel.toString().trim();
+ // First, attempt to map the label to a pre-defined description.
if (mKeyLabelMap.containsKey(label)) {
return context.getString(mKeyLabelMap.get(label));
- } else if (label.length() == 1
- || (keyboard.isManualTemporaryUpperCase() && !TextUtils
- .isEmpty(key.mHintLabel))) {
- return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
- } else {
- return label;
}
- } else if (key.mCode != Keyboard.CODE_DUMMY) {
+ }
+
+ // Just attempt to speak the description.
+ if (key.mCode != Keyboard.CODE_UNSPECIFIED) {
return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
}
@@ -162,36 +131,64 @@ public class KeyCodeDescriptionMapper {
* @return a character sequence describing the action performed by pressing
* the key
*/
- private CharSequence getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) {
- final KeyboardId id = keyboard.mId;
-
- if (id.isAlphabetKeyboard()) {
- return context.getString(R.string.spoken_description_to_symbol);
- } else if (id.isSymbolsKeyboard()) {
- return context.getString(R.string.spoken_description_to_alpha);
- } else if (id.isPhoneShiftKeyboard()) {
- return context.getString(R.string.spoken_description_to_numeric);
- } else if (id.isPhoneKeyboard()) {
- return context.getString(R.string.spoken_description_to_symbol);
- } else {
+ private String getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) {
+ final KeyboardId keyboardId = keyboard.mId;
+ final int elementId = keyboardId.mElementId;
+ final int resId;
+
+ switch (elementId) {
+ case KeyboardId.ELEMENT_ALPHABET:
+ case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
+ resId = R.string.spoken_description_to_symbol;
+ break;
+ case KeyboardId.ELEMENT_SYMBOLS:
+ case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
+ resId = R.string.spoken_description_to_alpha;
+ break;
+ case KeyboardId.ELEMENT_PHONE:
+ resId = R.string.spoken_description_to_symbol;
+ break;
+ case KeyboardId.ELEMENT_PHONE_SYMBOLS:
+ resId = R.string.spoken_description_to_numeric;
+ break;
+ default:
+ Log.e(TAG, "Missing description for keyboard element ID:" + elementId);
return null;
}
+
+ return context.getString(resId);
}
/**
- * Returns the keycode for the specified key given the current keyboard
- * state.
+ * Returns a context-sensitive description of the "Shift" key.
*
+ * @param context The package's context.
* @param keyboard The keyboard on which the key resides.
- * @param key The key from which to obtain a key code.
- * @return the key code for the specified key
+ * @return A context-sensitive description of the "Shift" key.
*/
- private int getCorrectKeyCode(Keyboard keyboard, Key key) {
- if (keyboard.isManualTemporaryUpperCase() && !TextUtils.isEmpty(key.mHintLabel)) {
- return key.mHintLabel.charAt(0);
- } else {
- return key.mCode;
+ private String getDescriptionForShiftKey(Context context, Keyboard keyboard) {
+ final KeyboardId keyboardId = keyboard.mId;
+ final int elementId = keyboardId.mElementId;
+ final int resId;
+
+ switch (elementId) {
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
+ resId = R.string.spoken_description_caps_lock;
+ break;
+ case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+ case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
+ resId = R.string.spoken_description_shift_shifted;
+ break;
+ default:
+ resId = R.string.spoken_description_shift;
}
+
+ return context.getString(resId);
}
/**
@@ -215,15 +212,9 @@ public class KeyCodeDescriptionMapper {
* @return a character sequence describing the action performed by pressing
* the key
*/
- private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key,
+ private String getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key,
boolean shouldObscure) {
- final int code = getCorrectKeyCode(keyboard, key);
-
- if (keyboard.isShiftLocked() && mShiftLockedKeyCodeMap.containsKey(code)) {
- return context.getString(mShiftLockedKeyCodeMap.get(code));
- } else if (keyboard.isShiftedOrShiftLocked() && mShiftedKeyCodeMap.containsKey(code)) {
- return context.getString(mShiftedKeyCodeMap.get(code));
- }
+ final int code = key.mCode;
// If the key description should be obscured, now is the time to do it.
final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code);