diff options
Diffstat (limited to 'java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java')
-rw-r--r-- | java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java | 251 |
1 files changed, 180 insertions, 71 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index cef82267f..c85a5514e 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -17,35 +17,36 @@ 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.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.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 +54,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 +69,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 +124,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 MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_HOVER_EXIT: + return onHoverKey(key, event); + case MotionEvent.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(MotionEvent.ACTION_HOVER_EXIT); + onHoverKey(previousKey, event); - if (keyIndex == KeyDetector.NOT_A_KEY) - return; + event.setAction(MotionEvent.ACTION_HOVER_ENTER); + onHoverKey(currentKey, event); - final Key key = tracker.getKey(keyIndex); + event.setAction(MotionEvent.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 MotionEvent.ACTION_HOVER_ENTER: + sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); + break; + case MotionEvent.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. + mView.getParent().requestSendAccessibilityEvent(mView, event); } private class KeyboardFlickGestureDetector extends FlickGestureDetector { @@ -185,4 +227,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); + } } |