Use sorted keys index as virtual view id

Change-Id: Id3f81de1edaacc06362b65aa7b68e9317e6596bd
main
Tadashi G. Takaoka 2014-04-21 19:26:08 -07:00
parent b2de802de4
commit 9289260822
3 changed files with 73 additions and 66 deletions

View File

@ -26,7 +26,6 @@ import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat; import android.support.v4.view.accessibility.AccessibilityRecordCompat;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
@ -37,9 +36,10 @@ import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.CoordinateUtils;
import java.util.List;
/** /**
* Exposes a virtual view sub-tree for {@link KeyboardView} and generates * Exposes a virtual view sub-tree for {@link KeyboardView} and generates
* {@link AccessibilityEvent}s for individual {@link Key}s. * {@link AccessibilityEvent}s for individual {@link Key}s.
@ -58,9 +58,6 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper; private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper;
private final AccessibilityUtils mAccessibilityUtils; 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. */ /** Temporary rect used to calculate in-screen bounds. */
private final Rect mTempBoundsInScreen = new Rect(); private final Rect mTempBoundsInScreen = new Rect();
@ -73,6 +70,9 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
/** The current keyboard view. */ /** The current keyboard view. */
private KeyboardView mKeyboardView; private KeyboardView mKeyboardView;
/** The current keyboard. */
private Keyboard mKeyboard;
public AccessibilityEntityProvider(final KeyboardView keyboardView, public AccessibilityEntityProvider(final KeyboardView keyboardView,
final InputMethodService inputMethod) { final InputMethodService inputMethod) {
mInputMethodService = inputMethod; mInputMethodService = inputMethod;
@ -92,14 +92,43 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
// Since this class is constructed lazily, we might not get a subsequent // Since this class is constructed lazily, we might not get a subsequent
// call to setKeyboard() and therefore need to call it now. // call to setKeyboard() and therefore need to call it now.
setKeyboard(); setKeyboard(keyboardView.getKeyboard());
} }
/** /**
* Sets the keyboard represented by this node provider. * Sets the keyboard represented by this node provider.
*
* @param keyboard The keyboard that is being set to the keyboard view.
*/ */
public void setKeyboard() { public void setKeyboard(final Keyboard keyboard) {
assignVirtualViewIds(); 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 +141,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
* @see AccessibilityEvent * @see AccessibilityEvent
*/ */
public AccessibilityEvent createAccessibilityEvent(final Key key, final int eventType) { 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 String keyDescription = getKeyDescription(key);
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setPackageName(mKeyboardView.getContext().getPackageName()); event.setPackageName(mKeyboardView.getContext().getPackageName());
@ -158,16 +187,21 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, rootInfo); ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, rootInfo);
// Add the virtual children of the root View. // Add the virtual children of the root View.
final Keyboard keyboard = mKeyboardView.getKeyboard(); final List<Key> sortedKeys = mKeyboard.getSortedKeys();
for (final Key key : keyboard.getSortedKeys()) { final int size = sortedKeys.size();
final int childVirtualViewId = generateVirtualViewIdForKey(key); for (int index = 0; index < size; index++) {
rootInfo.addChild(mKeyboardView, childVirtualViewId); 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; return rootInfo;
} }
// Find the view that corresponds to the given id. // Find the key that corresponds to the given virtual view id.
final Key key = mVirtualViewIdToKey.get(virtualViewId); final Key key = getKeyOf(virtualViewId);
if (key == null) { if (key == null) {
Log.e(TAG, "Invalid virtual view ID: " + virtualViewId); Log.e(TAG, "Invalid virtual view ID: " + virtualViewId);
return null; return null;
@ -226,7 +260,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
@Override @Override
public boolean performAction(final int virtualViewId, final int action, public boolean performAction(final int virtualViewId, final int action,
final Bundle arguments) { final Bundle arguments) {
final Key key = mVirtualViewIdToKey.get(virtualViewId); final Key key = getKeyOf(virtualViewId);
if (key == null) { if (key == null) {
return false; return false;
} }
@ -242,7 +276,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
* @return The result of performing the action, or false if the action is not supported. * @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) { boolean performActionForKey(final Key key, final int action, final Bundle arguments) {
final int virtualViewId = generateVirtualViewIdForKey(key); final int virtualViewId = getVirtualViewIdOf(key);
switch (action) { switch (action) {
case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
@ -288,7 +322,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo); final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo);
final SettingsValues currentSettings = Settings.getInstance().getCurrent(); final SettingsValues currentSettings = Settings.getInstance().getCurrent();
final String keyCodeDescription = mKeyCodeDescriptionMapper.getDescriptionForKey( final String keyCodeDescription = mKeyCodeDescriptionMapper.getDescriptionForKey(
mKeyboardView.getContext(), mKeyboardView.getKeyboard(), key, shouldObscure); mKeyboardView.getContext(), mKeyboard, key, shouldObscure);
if (currentSettings.isWordSeparator(key.getCode())) { if (currentSettings.isWordSeparator(key.getCode())) {
return mAccessibilityUtils.getAutoCorrectionDescription( return mAccessibilityUtils.getAutoCorrectionDescription(
keyCodeDescription, shouldObscure); keyCodeDescription, shouldObscure);
@ -297,40 +331,10 @@ 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. * Updates the parent's on-screen location.
*/ */
private void updateParentLocation() { private void updateParentLocation() {
mKeyboardView.getLocationOnScreen(mParentLocation); 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());
}
} }

View File

@ -55,6 +55,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
private InputMethodService mInputMethod; private InputMethodService mInputMethod;
private MainKeyboardView mView; private MainKeyboardView mView;
private Keyboard mKeyboard;
private AccessibilityEntityProvider mAccessibilityNodeProvider; private AccessibilityEntityProvider mAccessibilityNodeProvider;
private Key mLastHoverKey = null; private Key mLastHoverKey = null;
@ -66,6 +67,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
/** The most recently set keyboard mode. */ /** The most recently set keyboard mode. */
private int mLastKeyboardMode; private int mLastKeyboardMode;
private static final int NOT_A_KEYBOARD_MODE = -1;
public static void init(final InputMethodService inputMethod) { public static void init(final InputMethodService inputMethod) {
sInstance.initInternal(inputMethod); sInstance.initInternal(inputMethod);
@ -104,6 +106,10 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
return; return;
} }
mAccessibilityNodeProvider.setView(view); 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,15 +117,17 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
* <p> * <p>
* <b>Note:</b> This method will be called even if accessibility is not * <b>Note:</b> This method will be called even if accessibility is not
* enabled. * enabled.
* @param keyboard The keyboard that is being set to the wrapping view.
*/ */
public void setKeyboard() { public void setKeyboard(final Keyboard keyboard) {
if (mView == null) { if (keyboard == null) {
return; return;
} }
mKeyboard = keyboard;
if (mAccessibilityNodeProvider != null) { if (mAccessibilityNodeProvider != null) {
mAccessibilityNodeProvider.setKeyboard(); mAccessibilityNodeProvider.setKeyboard(keyboard);
} }
final int keyboardMode = mView.getKeyboard().mId.mMode; final int keyboardMode = keyboard.mId.mMode;
// Since this method is called even when accessibility is off, make sure // Since this method is called even when accessibility is off, make sure
// to check the state before announcing anything. Also, don't announce // to check the state before announcing anything. Also, don't announce
@ -139,7 +147,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
return; return;
} }
announceKeyboardHidden(); announceKeyboardHidden();
mLastKeyboardMode = -1; mLastKeyboardMode = NOT_A_KEYBOARD_MODE;
} }
/** /**
@ -148,7 +156,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
* *
* @param mode The new keyboard mode. * @param mode The new keyboard mode.
*/ */
private void announceKeyboardMode(int mode) { private void announceKeyboardMode(final int mode) {
final int resId = KEYBOARD_MODE_RES_IDS.get(mode); final int resId = KEYBOARD_MODE_RES_IDS.get(mode);
if (resId == 0) { if (resId == 0) {
return; return;
@ -329,12 +337,11 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
* Notifies the user of changes in the keyboard shift state. * Notifies the user of changes in the keyboard shift state.
*/ */
public void notifyShiftState() { public void notifyShiftState() {
if (mView == null) { if (mView == null || mKeyboard == null) {
return; return;
} }
final Keyboard keyboard = mView.getKeyboard(); final KeyboardId keyboardId = mKeyboard.mId;
final KeyboardId keyboardId = keyboard.mId;
final int elementId = keyboardId.mElementId; final int elementId = keyboardId.mElementId;
final Context context = mView.getContext(); final Context context = mView.getContext();
final CharSequence text; final CharSequence text;
@ -359,14 +366,13 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
* Notifies the user of changes in the keyboard symbols state. * Notifies the user of changes in the keyboard symbols state.
*/ */
public void notifySymbolsState() { public void notifySymbolsState() {
if (mView == null) { if (mView == null || mKeyboard == null) {
return; return;
} }
final Keyboard keyboard = mView.getKeyboard(); final KeyboardId keyboardId = mKeyboard.mId;
final Context context = mView.getContext();
final KeyboardId keyboardId = keyboard.mId;
final int elementId = keyboardId.mElementId; final int elementId = keyboardId.mElementId;
final Context context = mView.getContext();
final int resId; final int resId;
switch (elementId) { switch (elementId) {
@ -388,12 +394,9 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
resId = R.string.spoken_description_mode_phone_shift; resId = R.string.spoken_description_mode_phone_shift;
break; break;
default: default:
resId = -1;
}
if (resId < 0) {
return; return;
} }
final String text = context.getString(resId); final String text = context.getString(resId);
AccessibilityUtils.getInstance().announceForAccessibility(mView, text); AccessibilityUtils.getInstance().announceForAccessibility(mView, text);
} }

View File

@ -406,7 +406,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
// This always needs to be set since the accessibility state can // This always needs to be set since the accessibility state can
// potentially change without the keyboard being set again. // potentially change without the keyboard being set again.
AccessibleKeyboardViewProxy.getInstance().setKeyboard(); AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard);
} }
/** /**