diff options
Diffstat (limited to 'java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java')
-rw-r--r-- | java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java | 275 |
1 files changed, 188 insertions, 87 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java index eed40f4a9..d67d9dc4b 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,15 +31,31 @@ 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; +/** + * This class represents a delegate that can be registered in a class that extends + * {@link KeyboardView} to enhance accessibility support via composition rather via inheritance. + * + * To implement accessibility mode, the target keyboard view has to:<p> + * - Call {@link #setKeyboard(Keyboard)} when a new keyboard is set to the keyboard view. + * - Dispatch a hover event by calling {@link #onHoverEnter(MotionEvent)}. + * + * @param <KV> The keyboard view class type. + */ public class KeyboardAccessibilityDelegate<KV extends KeyboardView> extends AccessibilityDelegateCompat { + private static final String TAG = KeyboardAccessibilityDelegate.class.getSimpleName(); + protected static final boolean DEBUG_HOVER = false; + protected final KV mKeyboardView; protected final KeyDetector mKeyDetector; private Keyboard mKeyboard; private KeyboardAccessibilityNodeProvider mAccessibilityNodeProvider; private Key mLastHoverKey; + public static final int HOVER_EVENT_POINTER_ID = 0; + public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) { super(); mKeyboardView = keyboardView; @@ -65,10 +82,31 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView> mKeyboard = keyboard; } - protected Keyboard getKeyboard() { + protected final Keyboard getKeyboard() { return mKeyboard; } + protected final void setLastHoverKey(final Key key) { + mLastHoverKey = key; + } + + protected final Key getLastHoverKey() { + return mLastHoverKey; + } + + /** + * 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 +152,182 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView> } /** - * Receives hover events when touch exploration is turned on in SDK versions ICS and higher. + * Get a key that a hover event is on. * * @param event The hover event. - * @return {@code true} if the event is handled + * @return key The key that the <code>event</code> is on. */ - 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; + protected final Key getHoverKeyOf(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); + } - 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$ + /** + * 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. + */ + 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 = getHoverKeyOf(event); + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverEnter: key=" + key); + } + if (key != null) { + onHoverEnterTo(key); + } + setLastHoverKey(key); + } + + /** + * Process {@link MotionEvent#ACTION_HOVER_MOVE} event. + * + * @param event A hover move event. + */ + protected void onHoverMove(final MotionEvent event) { + final Key lastKey = getLastHoverKey(); + final Key key = getHoverKeyOf(event); + if (key != lastKey) { + if (lastKey != null) { + onHoverExitFrom(lastKey); } - return onHoverKey(key, event); + if (key != null) { + onHoverEnterTo(key); + } + } + if (key != null) { + onHoverMoveWithin(key); } - return false; + setLastHoverKey(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 lastKey = getLastHoverKey(); + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey); + } + if (lastKey != null) { + onHoverExitFrom(lastKey); + } + final Key key = getHoverKeyOf(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) { + onRegisterHoverKey(key, event); + onHoverExitFrom(key); + } + setLastHoverKey(null); + } + + /** + * Register a key that is selected by a hover event + * + * @param key A key to be registered. + * @param event A hover exit event that triggers key registering. + */ + protected void onRegisterHoverKey(final Key key, final MotionEvent event) { + if (DEBUG_HOVER) { + Log.d(TAG, "onRegisterHoverKey: key=" + key); + } + simulateTouchEvent(MotionEvent.ACTION_DOWN, event); + simulateTouchEvent(MotionEvent.ACTION_UP, event); } /** - * 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. - */ - private boolean onTransitionKey(final Key currentKey, final Key previousKey, - 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; - } - - /** - * Handles a hover event on a key. If {@link Key} extended View, this would be analogous to - * calling View.onHoverEvent(MotionEvent). + * @param touchAction The action of the synthesizing touch event. + * @param hoverEvent 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>. + */ + protected static MotionEvent synthesizeTouchEvent(final int touchAction, + final MotionEvent hoverEvent) { + final MotionEvent touchEvent = MotionEvent.obtain(hoverEvent); + touchEvent.setAction(touchAction); + return touchEvent; + } + + /** + * 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 onHoverEnterTo(final Key key) { + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverEnterTo: 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; + /** + * Handles a hover move event on a key. + * + * @param key The currently hovered key. + */ + protected void onHoverMoveWithin(final Key key) { } + + /** + * Handles a hover exit event on a key. + * + * @param key The currently hovered key. + */ + protected void onHoverExitFrom(final Key key) { + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverExitFrom: key=" + key); } - return true; + key.onReleased(); + mKeyboardView.invalidateKey(key); + final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider(); + provider.sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); } } |