diff options
Diffstat (limited to 'java/src/com/android/inputmethod/accessibility')
-rw-r--r-- | java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java | 123 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java | 123 |
2 files changed, 142 insertions, 104 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java index 063f2113d..720cf6b2a 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java @@ -19,15 +19,12 @@ package com.android.inputmethod.accessibility; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.os.Bundle; -import android.os.SystemClock; import android.support.v4.view.ViewCompat; import android.support.v4.view.accessibility.AccessibilityEventCompat; 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.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.EditorInfo; @@ -37,9 +34,10 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; +import java.util.List; + /** * Exposes a virtual view sub-tree for {@link KeyboardView} and generates * {@link AccessibilityEvent}s for individual {@link Key}s. @@ -58,9 +56,6 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper; private final AccessibilityUtils mAccessibilityUtils; - /** A map of integer IDs to {@link Key}s. */ - private final SparseArray<Key> mVirtualViewIdToKey = CollectionUtils.newSparseArray(); - /** Temporary rect used to calculate in-screen bounds. */ private final Rect mTempBoundsInScreen = new Rect(); @@ -73,6 +68,9 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider /** The current keyboard view. */ private KeyboardView mKeyboardView; + /** The current keyboard. */ + private Keyboard mKeyboard; + public AccessibilityEntityProvider(final KeyboardView keyboardView, final InputMethodService inputMethod) { mInputMethodService = inputMethod; @@ -92,14 +90,43 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider // Since this class is constructed lazily, we might not get a subsequent // call to setKeyboard() and therefore need to call it now. - setKeyboard(); + setKeyboard(keyboardView.getKeyboard()); } /** * Sets the keyboard represented by this node provider. + * + * @param keyboard The keyboard that is being set to the keyboard view. */ - public void setKeyboard() { - assignVirtualViewIds(); + public void setKeyboard(final Keyboard keyboard) { + mKeyboard = keyboard; + } + + private Key getKeyOf(final int virtualViewId) { + if (mKeyboard == null) { + return null; + } + final List<Key> sortedKeys = mKeyboard.getSortedKeys(); + // Use a virtual view id as an index of the sorted keys list. + if (virtualViewId >= 0 && virtualViewId < sortedKeys.size()) { + return sortedKeys.get(virtualViewId); + } + return null; + } + + private int getVirtualViewIdOf(final Key key) { + if (mKeyboard == null) { + return View.NO_ID; + } + final List<Key> sortedKeys = mKeyboard.getSortedKeys(); + final int size = sortedKeys.size(); + for (int index = 0; index < size; index++) { + if (sortedKeys.get(index) == key) { + // Use an index of the sorted keys list as a virtual view id. + return index; + } + } + return View.NO_ID; } /** @@ -112,7 +139,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider * @see AccessibilityEvent */ public AccessibilityEvent createAccessibilityEvent(final Key key, final int eventType) { - final int virtualViewId = generateVirtualViewIdForKey(key); + final int virtualViewId = getVirtualViewIdOf(key); final String keyDescription = getKeyDescription(key); final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); event.setPackageName(mKeyboardView.getContext().getPackageName()); @@ -158,16 +185,21 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, rootInfo); // Add the virtual children of the root View. - final Keyboard keyboard = mKeyboardView.getKeyboard(); - for (final Key key : keyboard.getSortedKeys()) { - final int childVirtualViewId = generateVirtualViewIdForKey(key); - rootInfo.addChild(mKeyboardView, childVirtualViewId); + final List<Key> sortedKeys = mKeyboard.getSortedKeys(); + final int size = sortedKeys.size(); + for (int index = 0; index < size; index++) { + final Key key = sortedKeys.get(index); + if (key.isSpacer()) { + continue; + } + // Use an index of the sorted keys list as a virtual view id. + rootInfo.addChild(mKeyboardView, index); } return rootInfo; } - // Find the view that corresponds to the given id. - final Key key = mVirtualViewIdToKey.get(virtualViewId); + // Find the key that corresponds to the given virtual view id. + final Key key = getKeyOf(virtualViewId); if (key == null) { Log.e(TAG, "Invalid virtual view ID: " + virtualViewId); return null; @@ -202,31 +234,10 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider return info; } - /** - * Simulates a key press by injecting touch events into the keyboard view. - * This avoids the complexity of trackers and listeners within the keyboard. - * - * @param key The key to press. - */ - void simulateKeyPress(final Key key) { - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - final long downTime = SystemClock.uptimeMillis(); - final MotionEvent downEvent = MotionEvent.obtain( - downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0); - final MotionEvent upEvent = MotionEvent.obtain( - downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0); - - mKeyboardView.onTouchEvent(downEvent); - mKeyboardView.onTouchEvent(upEvent); - downEvent.recycle(); - upEvent.recycle(); - } - @Override public boolean performAction(final int virtualViewId, final int action, final Bundle arguments) { - final Key key = mVirtualViewIdToKey.get(virtualViewId); + final Key key = getKeyOf(virtualViewId); if (key == null) { return false; } @@ -242,7 +253,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider * @return The result of performing the action, or false if the action is not supported. */ boolean performActionForKey(final Key key, final int action, final Bundle arguments) { - final int virtualViewId = generateVirtualViewIdForKey(key); + final int virtualViewId = getVirtualViewIdOf(key); switch (action) { case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: @@ -288,7 +299,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo); final SettingsValues currentSettings = Settings.getInstance().getCurrent(); final String keyCodeDescription = mKeyCodeDescriptionMapper.getDescriptionForKey( - mKeyboardView.getContext(), mKeyboardView.getKeyboard(), key, shouldObscure); + mKeyboardView.getContext(), mKeyboard, key, shouldObscure); if (currentSettings.isWordSeparator(key.getCode())) { return mAccessibilityUtils.getAutoCorrectionDescription( keyCodeDescription, shouldObscure); @@ -298,39 +309,9 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider } /** - * 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(); - - for (final Key key : keyboard.getSortedKeys()) { - 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 given key. Returned - * identifiers are valid until the next global layout state change. - * - * @param key The key to identify. - * @return A virtual view identifier. - */ - private static int generateVirtualViewIdForKey(final Key key) { - // The key x- and y-coordinates are stable between layout changes. - // Generate an identifier by bit-shifting the x-coordinate to the - // left-half of the integer and OR'ing with the y-coordinate. - return ((0xFFFF & key.getX()) << (Integer.SIZE / 2)) | (0xFFFF & key.getY()); - } } diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 0043b7844..d2031d155 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -18,6 +18,7 @@ package com.android.inputmethod.accessibility; import android.content.Context; import android.inputmethodservice.InputMethodService; +import android.os.SystemClock; import android.support.v4.view.AccessibilityDelegateCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.accessibility.AccessibilityEventCompat; @@ -34,6 +35,7 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy(); @@ -55,6 +57,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp private InputMethodService mInputMethod; private MainKeyboardView mView; + private Keyboard mKeyboard; private AccessibilityEntityProvider mAccessibilityNodeProvider; private Key mLastHoverKey = null; @@ -65,7 +68,8 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp private int mEdgeSlop; /** The most recently set keyboard mode. */ - private int mLastKeyboardMode; + private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN; + private static final int KEYBOARD_IS_HIDDEN = -1; public static void init(final InputMethodService inputMethod) { sInstance.initInternal(inputMethod); @@ -104,6 +108,10 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp return; } mAccessibilityNodeProvider.setView(view); + + // Since this class is constructed lazily, we might not get a subsequent + // call to setKeyboard() and therefore need to call it now. + setKeyboard(view.getKeyboard()); } /** @@ -111,24 +119,33 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * <p> * <b>Note:</b> This method will be called even if accessibility is not * enabled. + * @param keyboard The keyboard that is being set to the wrapping view. */ - public void setKeyboard() { - if (mView == null) { + public void setKeyboard(final Keyboard keyboard) { + if (keyboard == null) { return; } if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.setKeyboard(); + mAccessibilityNodeProvider.setKeyboard(keyboard); } - final int keyboardMode = mView.getKeyboard().mId.mMode; + final Keyboard lastKeyboard = mKeyboard; + final int lastKeyboardMode = mLastKeyboardMode; + mKeyboard = keyboard; + mLastKeyboardMode = keyboard.mId.mMode; // Since this method is called even when accessibility is off, make sure - // to check the state before announcing anything. Also, don't announce - // changes within the same mode. - if (AccessibilityUtils.getInstance().isAccessibilityEnabled() - && (mLastKeyboardMode != keyboardMode)) { - announceKeyboardMode(keyboardMode); + // to check the state before announcing anything. + if (!AccessibilityUtils.getInstance().isAccessibilityEnabled()) { + return; + } + // Announce the language name only when the language is changed. + if (lastKeyboard == null || !lastKeyboard.mId.mSubtype.equals(keyboard.mId.mSubtype)) { + announceKeyboardLanguage(keyboard); + } + // Announce the mode only when the mode is changed. + if (lastKeyboardMode != keyboard.mId.mMode) { + announceKeyboardMode(keyboard); } - mLastKeyboardMode = keyboardMode; } /** @@ -139,23 +156,35 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp return; } announceKeyboardHidden(); - mLastKeyboardMode = -1; + mLastKeyboardMode = KEYBOARD_IS_HIDDEN; } /** - * Announces which type of keyboard is being displayed. If the keyboard type - * is unknown, no announcement is made. + * Announces which language of keyboard is being displayed. * - * @param mode The new keyboard mode. + * @param keyboard The new keyboard. */ - private void announceKeyboardMode(int mode) { - final int resId = KEYBOARD_MODE_RES_IDS.get(mode); - if (resId == 0) { + private void announceKeyboardLanguage(final Keyboard keyboard) { + final String languageText = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale( + keyboard.mId.mSubtype); + sendWindowStateChanged(languageText); + } + + /** + * Announces which type of keyboard is being displayed. + * If the keyboard type is unknown, no announcement is made. + * + * @param keyboard The new keyboard. + */ + private void announceKeyboardMode(final Keyboard keyboard) { + final int mode = keyboard.mId.mMode; + final Context context = mView.getContext(); + final int modeTextResId = KEYBOARD_MODE_RES_IDS.get(mode); + if (modeTextResId == 0) { return; } - final Context context = mView.getContext(); - final String keyboardMode = context.getString(resId); - final String text = context.getString(R.string.announce_keyboard_mode, keyboardMode); + final String modeText = context.getString(modeTextResId); + final String text = context.getString(R.string.announce_keyboard_mode, modeText); sendWindowStateChanged(text); } @@ -233,7 +262,8 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp // Make sure we're not getting an EXIT event because the user slid // off the keyboard area, then force a key press. if (key != null) { - getAccessibilityNodeProvider().simulateKeyPress(key); + final long downTime = simulateKeyPress(key); + simulateKeyRelease(key, downTime); } //$FALL-THROUGH$ case MotionEvent.ACTION_HOVER_ENTER: @@ -274,6 +304,38 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp } /** + * Simulates a key press by injecting touch event into the keyboard view. + * This avoids the complexity of trackers and listeners within the keyboard. + * + * @param key The key to press. + */ + private long simulateKeyPress(final Key key) { + final int x = key.getHitBox().centerX(); + final int y = key.getHitBox().centerY(); + final long downTime = SystemClock.uptimeMillis(); + final MotionEvent downEvent = MotionEvent.obtain( + downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0); + mView.onTouchEvent(downEvent); + downEvent.recycle(); + return downTime; + } + + /** + * Simulates a key release by injecting touch event into the keyboard view. + * This avoids the complexity of trackers and listeners within the keyboard. + * + * @param key The key to release. + */ + private void simulateKeyRelease(final Key key, final long downTime) { + final int x = key.getHitBox().centerX(); + final int y = key.getHitBox().centerY(); + final MotionEvent upEvent = MotionEvent.obtain( + downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0); + mView.onTouchEvent(upEvent); + upEvent.recycle(); + } + + /** * 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. * @@ -329,12 +391,11 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * Notifies the user of changes in the keyboard shift state. */ public void notifyShiftState() { - if (mView == null) { + if (mView == null || mKeyboard == null) { return; } - final Keyboard keyboard = mView.getKeyboard(); - final KeyboardId keyboardId = keyboard.mId; + final KeyboardId keyboardId = mKeyboard.mId; final int elementId = keyboardId.mElementId; final Context context = mView.getContext(); final CharSequence text; @@ -359,14 +420,13 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * Notifies the user of changes in the keyboard symbols state. */ public void notifySymbolsState() { - if (mView == null) { + if (mView == null || mKeyboard == null) { return; } - final Keyboard keyboard = mView.getKeyboard(); - final Context context = mView.getContext(); - final KeyboardId keyboardId = keyboard.mId; + final KeyboardId keyboardId = mKeyboard.mId; final int elementId = keyboardId.mElementId; + final Context context = mView.getContext(); final int resId; switch (elementId) { @@ -388,12 +448,9 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp resId = R.string.spoken_description_mode_phone_shift; break; default: - resId = -1; - } - - if (resId < 0) { return; } + final String text = context.getString(resId); AccessibilityUtils.getInstance().announceForAccessibility(mView, text); } |