KeyboardAccessibilityNodeProvider.java revision 067689c57d353e4e37e0457989c6c2686977df9e
19a81ce92c381007affe6bb2310bf94c9856eaae1alanv/* 29a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Copyright (C) 2012 The Android Open Source Project 39a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 49a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Licensed under the Apache License, Version 2.0 (the "License"); 59a81ce92c381007affe6bb2310bf94c9856eaae1alanv * you may not use this file except in compliance with the License. 69a81ce92c381007affe6bb2310bf94c9856eaae1alanv * You may obtain a copy of the License at 79a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 89a81ce92c381007affe6bb2310bf94c9856eaae1alanv * http://www.apache.org/licenses/LICENSE-2.0 99a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 109a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Unless required by applicable law or agreed to in writing, software 119a81ce92c381007affe6bb2310bf94c9856eaae1alanv * distributed under the License is distributed on an "AS IS" BASIS, 129a81ce92c381007affe6bb2310bf94c9856eaae1alanv * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139a81ce92c381007affe6bb2310bf94c9856eaae1alanv * See the License for the specific language governing permissions and 149a81ce92c381007affe6bb2310bf94c9856eaae1alanv * limitations under the License. 159a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 169a81ce92c381007affe6bb2310bf94c9856eaae1alanv 179a81ce92c381007affe6bb2310bf94c9856eaae1alanvpackage com.android.inputmethod.accessibility; 189a81ce92c381007affe6bb2310bf94c9856eaae1alanv 199a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.graphics.Rect; 209a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.inputmethodservice.InputMethodService; 21f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.os.Bundle; 22f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.os.SystemClock; 239a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.ViewCompat; 24f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.support.v4.view.accessibility.AccessibilityEventCompat; 259a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 269a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; 279a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityRecordCompat; 289a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.util.Log; 299a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.util.SparseArray; 30f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.view.MotionEvent; 319a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.View; 329a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.ViewTreeObserver.OnGlobalLayoutListener; 339a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.accessibility.AccessibilityEvent; 349a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.inputmethod.EditorInfo; 359a81ce92c381007affe6bb2310bf94c9856eaae1alanv 369a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.Key; 379a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.Keyboard; 389a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.KeyboardView; 399a81ce92c381007affe6bb2310bf94c9856eaae1alanv 409a81ce92c381007affe6bb2310bf94c9856eaae1alanv/** 419a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Exposes a virtual view sub-tree for {@link KeyboardView} and generates 429a81ce92c381007affe6bb2310bf94c9856eaae1alanv * {@link AccessibilityEvent}s for individual {@link Key}s. 439a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p> 449a81ce92c381007affe6bb2310bf94c9856eaae1alanv * A virtual sub-tree is composed of imaginary {@link View}s that are reported 459a81ce92c381007affe6bb2310bf94c9856eaae1alanv * as a part of the view hierarchy for accessibility purposes. This enables 469a81ce92c381007affe6bb2310bf94c9856eaae1alanv * custom views that draw complex content to report them selves as a tree of 479a81ce92c381007affe6bb2310bf94c9856eaae1alanv * virtual views, thus conveying their logical structure. 489a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p> 499a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 509a81ce92c381007affe6bb2310bf94c9856eaae1alanvpublic class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat { 519a81ce92c381007affe6bb2310bf94c9856eaae1alanv private static final String TAG = AccessibilityEntityProvider.class.getSimpleName(); 52f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv private static final int UNDEFINED = Integer.MIN_VALUE; 539a81ce92c381007affe6bb2310bf94c9856eaae1alanv 549a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final KeyboardView mKeyboardView; 559a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final InputMethodService mInputMethodService; 569a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper; 579a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final AccessibilityUtils mAccessibilityUtils; 589a81ce92c381007affe6bb2310bf94c9856eaae1alanv 599a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** A map of integer IDs to {@link Key}s. */ 609a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final SparseArray<Key> mVirtualViewIdToKey = new SparseArray<Key>(); 619a81ce92c381007affe6bb2310bf94c9856eaae1alanv 629a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** Temporary rect used to calculate in-screen bounds. */ 639a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final Rect mTempBoundsInScreen = new Rect(); 649a81ce92c381007affe6bb2310bf94c9856eaae1alanv 659a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** The parent view's cached on-screen location. */ 669a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final int[] mParentLocation = new int[2]; 679a81ce92c381007affe6bb2310bf94c9856eaae1alanv 68f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** The virtual view identifier for the focused node. */ 69f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv private int mAccessibilityFocusedView = UNDEFINED; 70f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 719a81ce92c381007affe6bb2310bf94c9856eaae1alanv public AccessibilityEntityProvider(KeyboardView keyboardView, InputMethodService inputMethod) { 729a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView = keyboardView; 739a81ce92c381007affe6bb2310bf94c9856eaae1alanv mInputMethodService = inputMethod; 749a81ce92c381007affe6bb2310bf94c9856eaae1alanv 759a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance(); 769a81ce92c381007affe6bb2310bf94c9856eaae1alanv mAccessibilityUtils = AccessibilityUtils.getInstance(); 779a81ce92c381007affe6bb2310bf94c9856eaae1alanv 789a81ce92c381007affe6bb2310bf94c9856eaae1alanv assignVirtualViewIds(); 799a81ce92c381007affe6bb2310bf94c9856eaae1alanv updateParentLocation(); 809a81ce92c381007affe6bb2310bf94c9856eaae1alanv 819a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Ensure that the on-screen bounds are cleared when the layout changes. 829a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener); 839a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 849a81ce92c381007affe6bb2310bf94c9856eaae1alanv 859a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 869a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Creates and populates an {@link AccessibilityEvent} for the specified key 879a81ce92c381007affe6bb2310bf94c9856eaae1alanv * and event type. 889a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 899a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key A key on the host keyboard view. 909a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param eventType The event type to create. 919a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return A populated {@link AccessibilityEvent} for the key. 929a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see AccessibilityEvent 939a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 949a81ce92c381007affe6bb2310bf94c9856eaae1alanv public AccessibilityEvent createAccessibilityEvent(Key key, int eventType) { 959a81ce92c381007affe6bb2310bf94c9856eaae1alanv final int virtualViewId = generateVirtualViewIdForKey(key); 969a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String keyDescription = getKeyDescription(key); 979a81ce92c381007affe6bb2310bf94c9856eaae1alanv 989a81ce92c381007affe6bb2310bf94c9856eaae1alanv final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); 999a81ce92c381007affe6bb2310bf94c9856eaae1alanv event.setPackageName(mKeyboardView.getContext().getPackageName()); 1009a81ce92c381007affe6bb2310bf94c9856eaae1alanv event.setClassName(key.getClass().getName()); 1016662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv event.setContentDescription(keyDescription); 10226c80a1b9a103cdccbaeafac75a3db2543a9ee7ealanv event.setEnabled(true); 1039a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1049a81ce92c381007affe6bb2310bf94c9856eaae1alanv final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event); 1059a81ce92c381007affe6bb2310bf94c9856eaae1alanv record.setSource(mKeyboardView, virtualViewId); 1069a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1079a81ce92c381007affe6bb2310bf94c9856eaae1alanv return event; 1089a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1099a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1109a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 1119a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual 1129a81ce92c381007affe6bb2310bf94c9856eaae1alanv * view, i.e. a descendant of the host View, with the given <code>virtualViewId</code> or 1139a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}. 1149a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p> 1159a81ce92c381007affe6bb2310bf94c9856eaae1alanv * A virtual descendant is an imaginary View that is reported as a part of 1169a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the view hierarchy for accessibility purposes. This enables custom views 1179a81ce92c381007affe6bb2310bf94c9856eaae1alanv * that draw complex content to report them selves as a tree of virtual 1189a81ce92c381007affe6bb2310bf94c9856eaae1alanv * views, thus conveying their logical structure. 1199a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p> 1209a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p> 1219a81ce92c381007affe6bb2310bf94c9856eaae1alanv * The implementer is responsible for obtaining an accessibility node info 1229a81ce92c381007affe6bb2310bf94c9856eaae1alanv * from the pool of reusable instances and setting the desired properties of 1239a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the node info before returning it. 1249a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p> 1259a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 1269a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param virtualViewId A client defined virtual view id. 1279a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual 1289a81ce92c381007affe6bb2310bf94c9856eaae1alanv * descendant or the host View. 1299a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see AccessibilityNodeInfoCompat 1309a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 1319a81ce92c381007affe6bb2310bf94c9856eaae1alanv @Override 1329a81ce92c381007affe6bb2310bf94c9856eaae1alanv public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) { 1339a81ce92c381007affe6bb2310bf94c9856eaae1alanv AccessibilityNodeInfoCompat info = null; 1349a81ce92c381007affe6bb2310bf94c9856eaae1alanv 135f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv if (virtualViewId == UNDEFINED) { 136f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return null; 137f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } else if (virtualViewId == View.NO_ID) { 1389a81ce92c381007affe6bb2310bf94c9856eaae1alanv // We are requested to create an AccessibilityNodeInfo describing 1399a81ce92c381007affe6bb2310bf94c9856eaae1alanv // this View, i.e. the root of the virtual sub-tree. 1409a81ce92c381007affe6bb2310bf94c9856eaae1alanv info = AccessibilityNodeInfoCompat.obtain(mKeyboardView); 1419a81ce92c381007affe6bb2310bf94c9856eaae1alanv ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, info); 1429a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1439a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Add the virtual children of the root View. 1449a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Keyboard keyboard = mKeyboardView.getKeyboard(); 145b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka final Key[] keys = keyboard.mKeys; 1469a81ce92c381007affe6bb2310bf94c9856eaae1alanv for (Key key : keys) { 1479a81ce92c381007affe6bb2310bf94c9856eaae1alanv final int childVirtualViewId = generateVirtualViewIdForKey(key); 1489a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.addChild(mKeyboardView, childVirtualViewId); 1499a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1509a81ce92c381007affe6bb2310bf94c9856eaae1alanv } else { 1519a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Find the view that corresponds to the given id. 1529a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Key key = mVirtualViewIdToKey.get(virtualViewId); 1539a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (key == null) { 1549a81ce92c381007affe6bb2310bf94c9856eaae1alanv Log.e(TAG, "Invalid virtual view ID: " + virtualViewId); 1559a81ce92c381007affe6bb2310bf94c9856eaae1alanv return null; 1569a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1579a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1589a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String keyDescription = getKeyDescription(key); 1599a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Rect boundsInParent = key.mHitBox; 1609a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1619a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Calculate the key's in-screen bounds. 1629a81ce92c381007affe6bb2310bf94c9856eaae1alanv mTempBoundsInScreen.set(boundsInParent); 1639a81ce92c381007affe6bb2310bf94c9856eaae1alanv mTempBoundsInScreen.offset(mParentLocation[0], mParentLocation[1]); 1649a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1659a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Rect boundsInScreen = mTempBoundsInScreen; 1669a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1679a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Obtain and initialize an AccessibilityNodeInfo with 1689a81ce92c381007affe6bb2310bf94c9856eaae1alanv // information about the virtual view. 1699a81ce92c381007affe6bb2310bf94c9856eaae1alanv info = AccessibilityNodeInfoCompat.obtain(); 1709a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setPackageName(mKeyboardView.getContext().getPackageName()); 1719a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setClassName(key.getClass().getName()); 1726662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv info.setContentDescription(keyDescription); 1739a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setBoundsInParent(boundsInParent); 1749a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setBoundsInScreen(boundsInScreen); 1759a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setParent(mKeyboardView); 1769a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setSource(mKeyboardView, virtualViewId); 1779a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setBoundsInScreen(boundsInScreen); 17826c80a1b9a103cdccbaeafac75a3db2543a9ee7ealanv info.setEnabled(true); 179f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv info.setClickable(true); 180f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); 181f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 182f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv if (mAccessibilityFocusedView == virtualViewId) { 183f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS); 184f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } else { 185f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); 186f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 1879a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1889a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1899a81ce92c381007affe6bb2310bf94c9856eaae1alanv return info; 1909a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1919a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1929a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 193f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * Simulates a key press by injecting touch events into the keyboard view. 194f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * This avoids the complexity of trackers and listeners within the keyboard. 195f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * 196f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param key The key to press. 197f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv */ 198f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv void simulateKeyPress(Key key) { 199f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final int x = key.mX + (key.mWidth / 2); 200f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final int y = key.mY + (key.mHeight / 2); 201f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final long downTime = SystemClock.uptimeMillis(); 202f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final MotionEvent downEvent = MotionEvent.obtain( 203f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0); 204f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final MotionEvent upEvent = MotionEvent.obtain( 205f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0); 206f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 207f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv mKeyboardView.onTouchEvent(downEvent); 208f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv mKeyboardView.onTouchEvent(upEvent); 209f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 210f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 211f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv @Override 212f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv public boolean performAction(int virtualViewId, int action, Bundle arguments) { 213f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final Key key = mVirtualViewIdToKey.get(virtualViewId); 214f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 215f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv if (key == null) { 216f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return false; 217f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 218f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 219f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return performActionForKey(key, action, arguments); 220f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 221f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 222f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** 223f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * Performs the specified accessibility action for the given key. 224f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * 225f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param key The on which to perform the action. 226f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param action The action to perform. 227f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param arguments The action's arguments. 228f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @return The result of performing the action, or false if the action is 229f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * not supported. 230f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv */ 231f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv boolean performActionForKey(Key key, int action, Bundle arguments) { 232f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final int virtualViewId = generateVirtualViewIdForKey(key); 233f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 234f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv switch (action) { 235f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv case AccessibilityNodeInfoCompat.ACTION_CLICK: 236f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv simulateKeyPress(key); 237f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return true; 238f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: 239f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv if (mAccessibilityFocusedView == virtualViewId) { 240f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return false; 241f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 242f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv mAccessibilityFocusedView = virtualViewId; 243f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv sendAccessibilityEventForKey( 244f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 245f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return true; 246f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS: 247f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv if (mAccessibilityFocusedView != virtualViewId) { 248f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return false; 249f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 250f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv mAccessibilityFocusedView = UNDEFINED; 251f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv sendAccessibilityEventForKey( 252f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); 253f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return true; 254f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 255f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 256f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return false; 257f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 258f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 259f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv @Override 260f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv public AccessibilityNodeInfoCompat findAccessibilityFocus(int virtualViewId) { 261f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return createAccessibilityNodeInfo(mAccessibilityFocusedView); 262f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 263f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 264f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv @Override 265f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv public AccessibilityNodeInfoCompat accessibilityFocusSearch(int direction, int virtualViewId) { 266f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv // Focus search is not currently supported for IMEs. 267f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return null; 268f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 269f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 270f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** 271f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * Sends an accessibility event for the given {@link Key}. 272f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * 273f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param key The key that's sending the event. 274f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param eventType The type of event to send. 275f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv */ 276f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv void sendAccessibilityEventForKey(Key key, int eventType) { 277f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final AccessibilityEvent event = createAccessibilityEvent(key, eventType); 278067689c57d353e4e37e0457989c6c2686977df9ealanv mAccessibilityUtils.requestSendAccessibilityEvent(event); 279f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 280f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 281f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** 2829a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Returns the context-specific description for a {@link Key}. 2839a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 2849a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key The key to describe. 2859a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return The context-specific description of the key. 2869a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 2879a81ce92c381007affe6bb2310bf94c9856eaae1alanv private String getKeyDescription(Key key) { 2889a81ce92c381007affe6bb2310bf94c9856eaae1alanv final EditorInfo editorInfo = mInputMethodService.getCurrentInputEditorInfo(); 2899a81ce92c381007affe6bb2310bf94c9856eaae1alanv final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo); 2909a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String keyDescription = mKeyCodeDescriptionMapper.getDescriptionForKey( 2919a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.getContext(), mKeyboardView.getKeyboard(), key, shouldObscure); 2929a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2939a81ce92c381007affe6bb2310bf94c9856eaae1alanv return keyDescription; 2949a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2959a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2969a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 2979a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Assigns virtual view IDs to keyboard keys and populates the related maps. 2989a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 2999a81ce92c381007affe6bb2310bf94c9856eaae1alanv private void assignVirtualViewIds() { 3009a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Keyboard keyboard = mKeyboardView.getKeyboard(); 3019a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (keyboard == null) { 3029a81ce92c381007affe6bb2310bf94c9856eaae1alanv return; 3039a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3049a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3059a81ce92c381007affe6bb2310bf94c9856eaae1alanv mVirtualViewIdToKey.clear(); 3069a81ce92c381007affe6bb2310bf94c9856eaae1alanv 307b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka final Key[] keys = keyboard.mKeys; 308b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka for (Key key : keys) { 3099a81ce92c381007affe6bb2310bf94c9856eaae1alanv final int virtualViewId = generateVirtualViewIdForKey(key); 3109a81ce92c381007affe6bb2310bf94c9856eaae1alanv mVirtualViewIdToKey.put(virtualViewId, key); 3119a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3129a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3139a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3149a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 3159a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Updates the parent's on-screen location. 3169a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 3179a81ce92c381007affe6bb2310bf94c9856eaae1alanv private void updateParentLocation() { 3189a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.getLocationOnScreen(mParentLocation); 3199a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3209a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3219a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 322b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv * Generates a virtual view identifier for the given key. Returned 323b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv * identifiers are valid until the next global layout state change. 3249a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 3259a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key The key to identify. 3269a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return A virtual view identifier. 3279a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 3289a81ce92c381007affe6bb2310bf94c9856eaae1alanv private static int generateVirtualViewIdForKey(Key key) { 329b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv // The key x- and y-coordinates are stable between layout changes. 330b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv // Generate an identifier by bit-shifting the x-coordinate to the 331b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv // left-half of the integer and OR'ing with the y-coordinate. 332b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv return ((0xFFFF & key.mX) << (Integer.SIZE / 2)) | (0xFFFF & key.mY); 3339a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3349a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3359a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() { 3369a81ce92c381007affe6bb2310bf94c9856eaae1alanv @Override 3379a81ce92c381007affe6bb2310bf94c9856eaae1alanv public void onGlobalLayout() { 3389a81ce92c381007affe6bb2310bf94c9856eaae1alanv assignVirtualViewIds(); 3399a81ce92c381007affe6bb2310bf94c9856eaae1alanv updateParentLocation(); 3409a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3419a81ce92c381007affe6bb2310bf94c9856eaae1alanv }; 3429a81ce92c381007affe6bb2310bf94c9856eaae1alanv} 343