diff options
Diffstat (limited to 'java/src/com/android/inputmethod/accessibility')
5 files changed, 170 insertions, 118 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java index d50dd3ee6..27896fd11 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java @@ -114,7 +114,7 @@ public final class AccessibilityUtils { * @param event The event to check. * @return {@true} is the event is a touch exploration event */ - public boolean isTouchExplorationEvent(final MotionEvent event) { + public static boolean isTouchExplorationEvent(final MotionEvent event) { final int action = event.getAction(); return action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_EXIT diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 2c87fc1e9..58672ace7 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -285,15 +285,14 @@ public final class KeyCodeDescriptionMapper { if (index >= 0) { return context.getString(mKeyCodeMap.valueAt(index)); } - final String accentedLetter = getSpokenAccentedLetterDescriptionId(context, code); + final String accentedLetter = getSpokenAccentedLetterDescription(context, code); if (accentedLetter != null) { return accentedLetter; } - // Here, <code>code</code> may be a base letter. - final int spokenEmojiId = getSpokenDescriptionId( - context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT); - if (spokenEmojiId != 0) { - return context.getString(spokenEmojiId); + // Here, <code>code</code> may be a base (non-accented) letter. + final String emojiDescription = getSpokenEmojiDescription(context, code); + if (emojiDescription != null) { + return emojiDescription; } if (isDefinedNonCtrl) { return Character.toString((char) code); @@ -304,7 +303,7 @@ public final class KeyCodeDescriptionMapper { return context.getString(R.string.spoken_description_unknown, code); } - private String getSpokenAccentedLetterDescriptionId(final Context context, final int code) { + private String getSpokenAccentedLetterDescription(final Context context, final int code) { final boolean isUpperCase = Character.isUpperCase(code); final int baseCode = isUpperCase ? Character.toLowerCase(code) : code; final int baseIndex = mKeyCodeMap.indexOfKey(baseCode); @@ -314,7 +313,17 @@ public final class KeyCodeDescriptionMapper { return null; } final String spokenText = context.getString(resId); - return isUpperCase ? context.getString(R.string.spoke_description_upper_case, spokenText) + return isUpperCase ? context.getString(R.string.spoken_description_upper_case, spokenText) + : spokenText; + } + + private String getSpokenEmojiDescription(final Context context, final int code) { + final int resId = getSpokenDescriptionId(context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT); + if (resId == 0) { + return null; + } + final String spokenText = context.getString(resId); + return TextUtils.isEmpty(spokenText) ? context.getString(R.string.spoken_emoji_unknown) : spokenText; } diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java index eed40f4a9..7cae9861c 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java +++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java @@ -16,11 +16,12 @@ package com.android.inputmethod.accessibility; -import android.os.SystemClock; +import android.content.Context; import android.support.v4.view.AccessibilityDelegateCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.accessibility.AccessibilityEventCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewParent; @@ -30,6 +31,7 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.PointerTracker; public class KeyboardAccessibilityDelegate<KV extends KeyboardView> extends AccessibilityDelegateCompat { @@ -37,7 +39,7 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView> protected final KeyDetector mKeyDetector; private Keyboard mKeyboard; private KeyboardAccessibilityNodeProvider mAccessibilityNodeProvider; - private Key mLastHoverKey; + private Key mCurrentHoverKey; public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) { super(); @@ -65,10 +67,31 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView> mKeyboard = keyboard; } - protected Keyboard getKeyboard() { + protected final Keyboard getKeyboard() { return mKeyboard; } + protected final void setCurrentHoverKey(final Key key) { + mCurrentHoverKey = key; + } + + protected final Key getCurrentHoverKey() { + return mCurrentHoverKey; + } + + /** + * Sends a window state change event with the specified string resource id. + * + * @param resId The string resource id of the text to send with the event. + */ + protected void sendWindowStateChanged(final int resId) { + if (resId == 0) { + return; + } + final Context context = mKeyboardView.getContext(); + sendWindowStateChanged(context.getString(resId)); + } + /** * Sends a window state change event with the specified text. * @@ -114,119 +137,157 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView> } /** + * Get a key that a hover event is on. + * + * @param event The hover event. + * @return key The key that the <code>event</code> is on. + */ + protected final Key getHoverKey(final MotionEvent event) { + final int actionIndex = event.getActionIndex(); + final int x = (int)event.getX(actionIndex); + final int y = (int)event.getY(actionIndex); + return mKeyDetector.detectHitKey(x, y); + } + + /** * Receives hover events when touch exploration is turned on in SDK versions ICS and higher. * * @param event The hover event. - * @return {@code true} if the event is handled + * @return {@code true} if the event is handled. */ - public boolean dispatchHoverEvent(final MotionEvent event) { - final int x = (int) event.getX(); - final int y = (int) event.getY(); - final Key previousKey = mLastHoverKey; - final Key key = mKeyDetector.detectHitKey(x, y); - mLastHoverKey = key; - - switch (event.getAction()) { - case MotionEvent.ACTION_HOVER_EXIT: - // 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) { - final long downTime = simulateKeyPress(key); - simulateKeyRelease(key, downTime); - } - //$FALL-THROUGH$ + public boolean onHoverEvent(final MotionEvent event) { + switch (event.getActionMasked()) { case MotionEvent.ACTION_HOVER_ENTER: - return onHoverKey(key, event); + onHoverEnter(event); + break; case MotionEvent.ACTION_HOVER_MOVE: - if (key != previousKey) { - return onTransitionKey(key, previousKey, event); + onHoverMove(event); + break; + case MotionEvent.ACTION_HOVER_EXIT: + onHoverExit(event); + break; + default: + Log.w(getClass().getSimpleName(), "Unknown hover event: " + event); + break; + } + return true; + } + + /** + * Process {@link MotionEvent#ACTION_HOVER_ENTER} event. + * + * @param event A hover enter event. + */ + protected void onHoverEnter(final MotionEvent event) { + final Key key = getHoverKey(event); + if (key != null) { + onHoverEnterKey(key); + } + setCurrentHoverKey(key); + } + + /** + * Process {@link MotionEvent#ACTION_HOVER_MOVE} event. + * + * @param event A hover move event. + */ + protected void onHoverMove(final MotionEvent event) { + final Key previousKey = getCurrentHoverKey(); + final Key key = getHoverKey(event); + if (key != previousKey) { + if (previousKey != null) { + onHoverExitKey(previousKey); } - return onHoverKey(key, event); + if (key != null) { + onHoverEnterKey(key); + } + } + if (key != null) { + onHoverMoveKey(key); } - return false; + setCurrentHoverKey(key); } /** - * Simulates a key press by injecting touch an event into the keyboard view. - * This avoids the complexity of trackers and listeners within the keyboard. + * Process {@link MotionEvent#ACTION_HOVER_EXIT} event. * - * @param key The key to press. + * @param event A hover exit event. */ - 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); - mKeyboardView.onTouchEvent(downEvent); - downEvent.recycle(); - return downTime; + protected void onHoverExit(final MotionEvent event) { + final Key key = getHoverKey(event); + // 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) { + simulateTouchEvent(MotionEvent.ACTION_DOWN, event); + simulateTouchEvent(MotionEvent.ACTION_UP, event); + onHoverExitKey(key); + } + setCurrentHoverKey(null); } /** - * Simulates a key release by injecting touch an event into the keyboard view. - * This avoids the complexity of trackers and listeners within the keyboard. + * Simulating a touch event by injecting a synthesized touch event into {@link PointerTracker}. * - * @param key The key to release. + * @param touchAction The action of the synthesizing touch event. + * @param hoverEvent The base hover event from that the touch event is synthesized. */ - 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); - mKeyboardView.onTouchEvent(upEvent); - upEvent.recycle(); + protected void simulateTouchEvent(final int touchAction, final MotionEvent hoverEvent) { + final MotionEvent touchEvent = synthesizeTouchEvent(touchAction, hoverEvent); + final int actionIndex = touchEvent.getActionIndex(); + final int pointerId = touchEvent.getPointerId(actionIndex); + final PointerTracker tracker = PointerTracker.getPointerTracker(pointerId); + tracker.processMotionEvent(touchEvent, mKeyDetector); + touchEvent.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. + * Synthesize a touch event from a hover event. * - * @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. + * @param touchAction The action of the synthesizing touch event. + * @param event The base hover event from that the touch event is synthesized. + * @return The synthesized touch event of <code>touchAction</code> that has pointer information + * of <code>event</code>. */ - private boolean onTransitionKey(final Key currentKey, final Key previousKey, + protected static MotionEvent synthesizeTouchEvent(final int touchAction, final MotionEvent event) { - final int savedAction = event.getAction(); - event.setAction(MotionEvent.ACTION_HOVER_EXIT); - onHoverKey(previousKey, event); - event.setAction(MotionEvent.ACTION_HOVER_ENTER); - onHoverKey(currentKey, event); - event.setAction(MotionEvent.ACTION_HOVER_MOVE); - final boolean handled = onHoverKey(currentKey, event); - event.setAction(savedAction); - return handled; + final long downTime = event.getDownTime(); + final long eventTime = event.getEventTime(); + final int actionIndex = event.getActionIndex(); + final float x = event.getX(actionIndex); + final float y = event.getY(actionIndex); + final int pointerId = event.getPointerId(actionIndex); + return MotionEvent.obtain(downTime, eventTime, touchAction, x, y, pointerId); } /** - * Handles a hover event on a key. If {@link Key} extended View, this would be analogous to - * calling View.onHoverEvent(MotionEvent). + * Handles a hover enter event on a key. * * @param key The currently hovered key. - * @param event The hover event. - * @return {@code true} if the event was handled. */ - private boolean onHoverKey(final Key key, final MotionEvent event) { - // Null keys can't receive events. - if (key == null) { - return false; - } + protected void onHoverEnterKey(final Key key) { + key.onPressed(); + mKeyboardView.invalidateKey(key); final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider(); + provider.sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); + provider.performActionForKey(key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); + } - switch (event.getAction()) { - case MotionEvent.ACTION_HOVER_ENTER: - provider.sendAccessibilityEventForKey( - key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); - provider.performActionForKey( - key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null); - break; - case MotionEvent.ACTION_HOVER_EXIT: - provider.sendAccessibilityEventForKey( - key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); - break; - } - return true; + /** + * Handles a hover move event on a key. + * + * @param key The currently hovered key. + */ + protected void onHoverMoveKey(final Key key) { } + + /** + * Handles a hover exit event on a key. + * + * @param key The currently hovered key. + */ + protected void onHoverExitKey(final Key key) { + key.onReleased(); + mKeyboardView.invalidateKey(key); + final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider(); + provider.sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); } } diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java index cddd1c7ed..a6997e2f9 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java +++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java @@ -134,7 +134,7 @@ public final class KeyboardAccessibilityNodeProvider extends AccessibilityNodePr event.setClassName(key.getClass().getName()); event.setContentDescription(keyDescription); event.setEnabled(true); - final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event); + final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); record.setSource(mKeyboardView, virtualViewId); return event; } @@ -229,7 +229,7 @@ public final class KeyboardAccessibilityNodeProvider extends AccessibilityNodePr if (key == null) { return false; } - return performActionForKey(key, action, arguments); + return performActionForKey(key, action); } /** @@ -237,25 +237,16 @@ public final class KeyboardAccessibilityNodeProvider extends AccessibilityNodePr * * @param key The on which to perform the action. * @param action The action to perform. - * @param arguments The action's arguments. * @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 = getVirtualViewIdOf(key); - + boolean performActionForKey(final Key key, final int action) { switch (action) { case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: - if (mAccessibilityFocusedView == virtualViewId) { - return false; - } - mAccessibilityFocusedView = virtualViewId; + mAccessibilityFocusedView = getVirtualViewIdOf(key); sendAccessibilityEventForKey( key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED); return true; case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS: - if (mAccessibilityFocusedView != virtualViewId) { - return false; - } mAccessibilityFocusedView = UNDEFINED; sendAccessibilityEventForKey( key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); diff --git a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java index c114551c8..ec6bb0156 100644 --- a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java +++ b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java @@ -17,13 +17,8 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.os.SystemClock; -import android.support.v4.view.accessibility.AccessibilityEventCompat; -import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.util.SparseIntArray; -import android.view.MotionEvent; -import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; @@ -168,17 +163,13 @@ public final class MainKeyboardAccessibilityDelegate default: return; } - final String text = mKeyboardView.getContext().getString(resId); - sendWindowStateChanged(text); + sendWindowStateChanged(resId); } /** * Announces that the keyboard has been hidden. */ private void announceKeyboardHidden() { - final Context context = mKeyboardView.getContext(); - final String text = context.getString(R.string.announce_keyboard_hidden); - - sendWindowStateChanged(text); + sendWindowStateChanged(R.string.announce_keyboard_hidden); } } |