Loading java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java +115 −1 Original line number Diff line number Diff line Loading @@ -18,13 +18,18 @@ 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.ViewParent; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.EditorInfo; Loading @@ -45,6 +50,7 @@ import com.android.inputmethod.keyboard.KeyboardView; */ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat { private static final String TAG = AccessibilityEntityProvider.class.getSimpleName(); private static final int UNDEFINED = Integer.MIN_VALUE; private final KeyboardView mKeyboardView; private final InputMethodService mInputMethodService; Loading @@ -60,6 +66,9 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat /** The parent view's cached on-screen location. */ private final int[] mParentLocation = new int[2]; /** The virtual view identifier for the focused node. */ private int mAccessibilityFocusedView = UNDEFINED; public AccessibilityEntityProvider(KeyboardView keyboardView, InputMethodService inputMethod) { mKeyboardView = keyboardView; mInputMethodService = inputMethod; Loading Loading @@ -124,7 +133,9 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) { AccessibilityNodeInfoCompat info = null; if (virtualViewId == View.NO_ID) { if (virtualViewId == UNDEFINED) { return null; } else 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); Loading Loading @@ -166,11 +177,114 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat info.setSource(mKeyboardView, virtualViewId); info.setBoundsInScreen(boundsInScreen); info.setEnabled(true); info.setClickable(true); info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); if (mAccessibilityFocusedView == virtualViewId) { info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS); } else { info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); } } 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(Key key) { final int x = key.mX + (key.mWidth / 2); final int y = key.mY + (key.mHeight / 2); 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); } @Override public boolean performAction(int virtualViewId, int action, Bundle arguments) { final Key key = mVirtualViewIdToKey.get(virtualViewId); if (key == null) { return false; } return performActionForKey(key, action, arguments); } /** * Performs the specified accessibility action for the given key. * * @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(Key key, int action, Bundle arguments) { final int virtualViewId = generateVirtualViewIdForKey(key); switch (action) { case AccessibilityNodeInfoCompat.ACTION_CLICK: simulateKeyPress(key); return true; case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: if (mAccessibilityFocusedView == virtualViewId) { return false; } mAccessibilityFocusedView = virtualViewId; 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); return true; } return false; } @Override public AccessibilityNodeInfoCompat findAccessibilityFocus(int virtualViewId) { return createAccessibilityNodeInfo(mAccessibilityFocusedView); } @Override public AccessibilityNodeInfoCompat accessibilityFocusSearch(int direction, int virtualViewId) { // Focus search is not currently supported for IMEs. return null; } /** * Sends an accessibility event for the given {@link Key}. * * @param key The key that's sending the event. * @param eventType The type of event to send. */ void sendAccessibilityEventForKey(Key key, int eventType) { final AccessibilityEvent event = createAccessibilityEvent(key, eventType); final ViewParent parent = mKeyboardView.getParent(); if (parent == null) { return; } parent.requestSendAccessibilityEvent(mKeyboardView, event); } /** * Returns the context-specific description for a {@link Key}. * Loading java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +24 −25 Original line number Diff line number Diff line Loading @@ -21,10 +21,10 @@ import android.inputmethodservice.InputMethodService; 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.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; Loading Loading @@ -91,13 +91,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { */ @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 mAccessibilityNodeProvider; return getAccessibilityNodeProvider(); } /** Loading @@ -120,7 +114,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { // Make sure we're not getting an EXIT event because the user slid // off the keyboard area, then force a key press. if (pointInView(x, y)) { tracker.onRegisterKey(key); getAccessibilityNodeProvider().simulateKeyPress(key); } //$FALL-THROUGH$ case MotionEvent.ACTION_HOVER_ENTER: Loading @@ -136,6 +130,19 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { return false; } /** * @return A lazily-instantiated node provider for this view proxy. */ private AccessibilityEntityProvider getAccessibilityNodeProvider() { // 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 mAccessibilityNodeProvider; } /** * Utility method to determine whether the given point, in local * coordinates, is inside the view, where the area of the view is contracted Loading Loading @@ -191,32 +198,24 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { return false; } final AccessibilityEntityProvider provider = getAccessibilityNodeProvider(); switch (event.getAction()) { case MotionEvent.ACTION_HOVER_ENTER: sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); provider.sendAccessibilityEventForKey( key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); provider.performActionForKey( key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null); break; case MotionEvent.ACTION_HOVER_EXIT: sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); provider.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); } /** * Notifies the user of changes in the keyboard shift state. */ Loading java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +3 −0 Original line number Diff line number Diff line Loading @@ -111,6 +111,9 @@ public class KeyCodeDescriptionMapper { if (mKeyLabelMap.containsKey(label)) { return context.getString(mKeyLabelMap.get(label)); } // Otherwise, return the label. return key.mLabel; } // Just attempt to speak the description. Loading Loading
java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java +115 −1 Original line number Diff line number Diff line Loading @@ -18,13 +18,18 @@ 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.ViewParent; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.EditorInfo; Loading @@ -45,6 +50,7 @@ import com.android.inputmethod.keyboard.KeyboardView; */ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat { private static final String TAG = AccessibilityEntityProvider.class.getSimpleName(); private static final int UNDEFINED = Integer.MIN_VALUE; private final KeyboardView mKeyboardView; private final InputMethodService mInputMethodService; Loading @@ -60,6 +66,9 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat /** The parent view's cached on-screen location. */ private final int[] mParentLocation = new int[2]; /** The virtual view identifier for the focused node. */ private int mAccessibilityFocusedView = UNDEFINED; public AccessibilityEntityProvider(KeyboardView keyboardView, InputMethodService inputMethod) { mKeyboardView = keyboardView; mInputMethodService = inputMethod; Loading Loading @@ -124,7 +133,9 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) { AccessibilityNodeInfoCompat info = null; if (virtualViewId == View.NO_ID) { if (virtualViewId == UNDEFINED) { return null; } else 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); Loading Loading @@ -166,11 +177,114 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat info.setSource(mKeyboardView, virtualViewId); info.setBoundsInScreen(boundsInScreen); info.setEnabled(true); info.setClickable(true); info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); if (mAccessibilityFocusedView == virtualViewId) { info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS); } else { info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); } } 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(Key key) { final int x = key.mX + (key.mWidth / 2); final int y = key.mY + (key.mHeight / 2); 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); } @Override public boolean performAction(int virtualViewId, int action, Bundle arguments) { final Key key = mVirtualViewIdToKey.get(virtualViewId); if (key == null) { return false; } return performActionForKey(key, action, arguments); } /** * Performs the specified accessibility action for the given key. * * @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(Key key, int action, Bundle arguments) { final int virtualViewId = generateVirtualViewIdForKey(key); switch (action) { case AccessibilityNodeInfoCompat.ACTION_CLICK: simulateKeyPress(key); return true; case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: if (mAccessibilityFocusedView == virtualViewId) { return false; } mAccessibilityFocusedView = virtualViewId; 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); return true; } return false; } @Override public AccessibilityNodeInfoCompat findAccessibilityFocus(int virtualViewId) { return createAccessibilityNodeInfo(mAccessibilityFocusedView); } @Override public AccessibilityNodeInfoCompat accessibilityFocusSearch(int direction, int virtualViewId) { // Focus search is not currently supported for IMEs. return null; } /** * Sends an accessibility event for the given {@link Key}. * * @param key The key that's sending the event. * @param eventType The type of event to send. */ void sendAccessibilityEventForKey(Key key, int eventType) { final AccessibilityEvent event = createAccessibilityEvent(key, eventType); final ViewParent parent = mKeyboardView.getParent(); if (parent == null) { return; } parent.requestSendAccessibilityEvent(mKeyboardView, event); } /** * Returns the context-specific description for a {@link Key}. * Loading
java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +24 −25 Original line number Diff line number Diff line Loading @@ -21,10 +21,10 @@ import android.inputmethodservice.InputMethodService; 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.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; Loading Loading @@ -91,13 +91,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { */ @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 mAccessibilityNodeProvider; return getAccessibilityNodeProvider(); } /** Loading @@ -120,7 +114,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { // Make sure we're not getting an EXIT event because the user slid // off the keyboard area, then force a key press. if (pointInView(x, y)) { tracker.onRegisterKey(key); getAccessibilityNodeProvider().simulateKeyPress(key); } //$FALL-THROUGH$ case MotionEvent.ACTION_HOVER_ENTER: Loading @@ -136,6 +130,19 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { return false; } /** * @return A lazily-instantiated node provider for this view proxy. */ private AccessibilityEntityProvider getAccessibilityNodeProvider() { // 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 mAccessibilityNodeProvider; } /** * Utility method to determine whether the given point, in local * coordinates, is inside the view, where the area of the view is contracted Loading Loading @@ -191,32 +198,24 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { return false; } final AccessibilityEntityProvider provider = getAccessibilityNodeProvider(); switch (event.getAction()) { case MotionEvent.ACTION_HOVER_ENTER: sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); provider.sendAccessibilityEventForKey( key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); provider.performActionForKey( key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null); break; case MotionEvent.ACTION_HOVER_EXIT: sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); provider.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); } /** * Notifies the user of changes in the keyboard shift state. */ Loading
java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +3 −0 Original line number Diff line number Diff line Loading @@ -111,6 +111,9 @@ public class KeyCodeDescriptionMapper { if (mKeyLabelMap.containsKey(label)) { return context.getString(mKeyLabelMap.get(label)); } // Otherwise, return the label. return key.mLabel; } // Just attempt to speak the description. Loading