KeyboardAccessibilityNodeProvider.java revision 9a81ce92c381007affe6bb2310bf94c9856eaae1
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; 219a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.ViewCompat; 229a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 239a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; 249a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityRecordCompat; 259a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.util.Log; 269a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.util.SparseArray; 279a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.View; 289a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.ViewTreeObserver.OnGlobalLayoutListener; 299a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.accessibility.AccessibilityEvent; 309a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.inputmethod.EditorInfo; 319a81ce92c381007affe6bb2310bf94c9856eaae1alanv 329a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.Key; 339a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.Keyboard; 349a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.KeyboardView; 359a81ce92c381007affe6bb2310bf94c9856eaae1alanv 369a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport java.util.Collections; 379a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport java.util.LinkedList; 389a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport java.util.List; 399a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport java.util.Set; 409a81ce92c381007affe6bb2310bf94c9856eaae1alanv 419a81ce92c381007affe6bb2310bf94c9856eaae1alanv/** 429a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Exposes a virtual view sub-tree for {@link KeyboardView} and generates 439a81ce92c381007affe6bb2310bf94c9856eaae1alanv * {@link AccessibilityEvent}s for individual {@link Key}s. 449a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p> 459a81ce92c381007affe6bb2310bf94c9856eaae1alanv * A virtual sub-tree is composed of imaginary {@link View}s that are reported 469a81ce92c381007affe6bb2310bf94c9856eaae1alanv * as a part of the view hierarchy for accessibility purposes. This enables 479a81ce92c381007affe6bb2310bf94c9856eaae1alanv * custom views that draw complex content to report them selves as a tree of 489a81ce92c381007affe6bb2310bf94c9856eaae1alanv * virtual views, thus conveying their logical structure. 499a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p> 509a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 519a81ce92c381007affe6bb2310bf94c9856eaae1alanvpublic class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat { 529a81ce92c381007affe6bb2310bf94c9856eaae1alanv private static final String TAG = AccessibilityEntityProvider.class.getSimpleName(); 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 689a81ce92c381007affe6bb2310bf94c9856eaae1alanv public AccessibilityEntityProvider(KeyboardView keyboardView, InputMethodService inputMethod) { 699a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView = keyboardView; 709a81ce92c381007affe6bb2310bf94c9856eaae1alanv mInputMethodService = inputMethod; 719a81ce92c381007affe6bb2310bf94c9856eaae1alanv 729a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance(); 739a81ce92c381007affe6bb2310bf94c9856eaae1alanv mAccessibilityUtils = AccessibilityUtils.getInstance(); 749a81ce92c381007affe6bb2310bf94c9856eaae1alanv 759a81ce92c381007affe6bb2310bf94c9856eaae1alanv assignVirtualViewIds(); 769a81ce92c381007affe6bb2310bf94c9856eaae1alanv updateParentLocation(); 779a81ce92c381007affe6bb2310bf94c9856eaae1alanv 789a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Ensure that the on-screen bounds are cleared when the layout changes. 799a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener); 809a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 819a81ce92c381007affe6bb2310bf94c9856eaae1alanv 829a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 839a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Creates and populates an {@link AccessibilityEvent} for the specified key 849a81ce92c381007affe6bb2310bf94c9856eaae1alanv * and event type. 859a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 869a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key A key on the host keyboard view. 879a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param eventType The event type to create. 889a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return A populated {@link AccessibilityEvent} for the key. 899a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see AccessibilityEvent 909a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 919a81ce92c381007affe6bb2310bf94c9856eaae1alanv public AccessibilityEvent createAccessibilityEvent(Key key, int eventType) { 929a81ce92c381007affe6bb2310bf94c9856eaae1alanv final int virtualViewId = generateVirtualViewIdForKey(key); 939a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String keyDescription = getKeyDescription(key); 949a81ce92c381007affe6bb2310bf94c9856eaae1alanv 959a81ce92c381007affe6bb2310bf94c9856eaae1alanv final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); 969a81ce92c381007affe6bb2310bf94c9856eaae1alanv event.setPackageName(mKeyboardView.getContext().getPackageName()); 979a81ce92c381007affe6bb2310bf94c9856eaae1alanv event.setClassName(key.getClass().getName()); 989a81ce92c381007affe6bb2310bf94c9856eaae1alanv event.getText().add(keyDescription); 999a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1009a81ce92c381007affe6bb2310bf94c9856eaae1alanv final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event); 1019a81ce92c381007affe6bb2310bf94c9856eaae1alanv record.setSource(mKeyboardView, virtualViewId); 1029a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1039a81ce92c381007affe6bb2310bf94c9856eaae1alanv return event; 1049a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1059a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1069a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 1079a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual 1089a81ce92c381007affe6bb2310bf94c9856eaae1alanv * view, i.e. a descendant of the host View, with the given <code>virtualViewId</code> or 1099a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}. 1109a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p> 1119a81ce92c381007affe6bb2310bf94c9856eaae1alanv * A virtual descendant is an imaginary View that is reported as a part of 1129a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the view hierarchy for accessibility purposes. This enables custom views 1139a81ce92c381007affe6bb2310bf94c9856eaae1alanv * that draw complex content to report them selves as a tree of virtual 1149a81ce92c381007affe6bb2310bf94c9856eaae1alanv * views, thus conveying their logical structure. 1159a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p> 1169a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p> 1179a81ce92c381007affe6bb2310bf94c9856eaae1alanv * The implementer is responsible for obtaining an accessibility node info 1189a81ce92c381007affe6bb2310bf94c9856eaae1alanv * from the pool of reusable instances and setting the desired properties of 1199a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the node info before returning it. 1209a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p> 1219a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 1229a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param virtualViewId A client defined virtual view id. 1239a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual 1249a81ce92c381007affe6bb2310bf94c9856eaae1alanv * descendant or the host View. 1259a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see AccessibilityNodeInfoCompat 1269a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 1279a81ce92c381007affe6bb2310bf94c9856eaae1alanv @Override 1289a81ce92c381007affe6bb2310bf94c9856eaae1alanv public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) { 1299a81ce92c381007affe6bb2310bf94c9856eaae1alanv AccessibilityNodeInfoCompat info = null; 1309a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1319a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (virtualViewId == View.NO_ID) { 1329a81ce92c381007affe6bb2310bf94c9856eaae1alanv // We are requested to create an AccessibilityNodeInfo describing 1339a81ce92c381007affe6bb2310bf94c9856eaae1alanv // this View, i.e. the root of the virtual sub-tree. 1349a81ce92c381007affe6bb2310bf94c9856eaae1alanv info = AccessibilityNodeInfoCompat.obtain(mKeyboardView); 1359a81ce92c381007affe6bb2310bf94c9856eaae1alanv ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, info); 1369a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1379a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Add the virtual children of the root View. 1389a81ce92c381007affe6bb2310bf94c9856eaae1alanv // TODO(alanv): Need to assign a unique ID to each key. 1399a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Keyboard keyboard = mKeyboardView.getKeyboard(); 1409a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Set<Key> keys = keyboard.mKeys; 1419a81ce92c381007affe6bb2310bf94c9856eaae1alanv for (Key key : keys) { 1429a81ce92c381007affe6bb2310bf94c9856eaae1alanv final int childVirtualViewId = generateVirtualViewIdForKey(key); 1439a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.addChild(mKeyboardView, childVirtualViewId); 1449a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1459a81ce92c381007affe6bb2310bf94c9856eaae1alanv } else { 1469a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Find the view that corresponds to the given id. 1479a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Key key = mVirtualViewIdToKey.get(virtualViewId); 1489a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (key == null) { 1499a81ce92c381007affe6bb2310bf94c9856eaae1alanv Log.e(TAG, "Invalid virtual view ID: " + virtualViewId); 1509a81ce92c381007affe6bb2310bf94c9856eaae1alanv return null; 1519a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1529a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1539a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String keyDescription = getKeyDescription(key); 1549a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Rect boundsInParent = key.mHitBox; 1559a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1569a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Calculate the key's in-screen bounds. 1579a81ce92c381007affe6bb2310bf94c9856eaae1alanv mTempBoundsInScreen.set(boundsInParent); 1589a81ce92c381007affe6bb2310bf94c9856eaae1alanv mTempBoundsInScreen.offset(mParentLocation[0], mParentLocation[1]); 1599a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1609a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Rect boundsInScreen = mTempBoundsInScreen; 1619a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1629a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Obtain and initialize an AccessibilityNodeInfo with 1639a81ce92c381007affe6bb2310bf94c9856eaae1alanv // information about the virtual view. 1649a81ce92c381007affe6bb2310bf94c9856eaae1alanv info = AccessibilityNodeInfoCompat.obtain(); 1659a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.addAction(AccessibilityNodeInfoCompat.ACTION_SELECT); 1669a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION); 1679a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setPackageName(mKeyboardView.getContext().getPackageName()); 1689a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setClassName(key.getClass().getName()); 1699a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setBoundsInParent(boundsInParent); 1709a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setBoundsInScreen(boundsInScreen); 1719a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setParent(mKeyboardView); 1729a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setSource(mKeyboardView, virtualViewId); 1739a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setBoundsInScreen(boundsInScreen); 1749a81ce92c381007affe6bb2310bf94c9856eaae1alanv info.setText(keyDescription); 1759a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1769a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1779a81ce92c381007affe6bb2310bf94c9856eaae1alanv return info; 1789a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1799a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1809a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 1819a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Performs an accessibility action on a virtual view, i.e. a descendant of 1829a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the host View, with the given <code>virtualViewId</code> or the host View itself if 1839a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <code>virtualViewId</code> equals to {@link View#NO_ID}. 1849a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 1859a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param action The action to perform. 1869a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param virtualViewId A client defined virtual view id. 1879a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return True if the action was performed. 1889a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see #createAccessibilityNodeInfo(int) 1899a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see AccessibilityNodeInfoCompat 1909a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 1919a81ce92c381007affe6bb2310bf94c9856eaae1alanv @Override 1929a81ce92c381007affe6bb2310bf94c9856eaae1alanv public boolean performAccessibilityAction(int action, int virtualViewId) { 1939a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (virtualViewId == View.NO_ID) { 1949a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Perform the action on the host View. 1959a81ce92c381007affe6bb2310bf94c9856eaae1alanv switch (action) { 1969a81ce92c381007affe6bb2310bf94c9856eaae1alanv case AccessibilityNodeInfoCompat.ACTION_SELECT: 1979a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (!mKeyboardView.isSelected()) { 1989a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.setSelected(true); 1999a81ce92c381007affe6bb2310bf94c9856eaae1alanv return mKeyboardView.isSelected(); 2009a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2019a81ce92c381007affe6bb2310bf94c9856eaae1alanv break; 2029a81ce92c381007affe6bb2310bf94c9856eaae1alanv case AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION: 2039a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (mKeyboardView.isSelected()) { 2049a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.setSelected(false); 2059a81ce92c381007affe6bb2310bf94c9856eaae1alanv return !mKeyboardView.isSelected(); 2069a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2079a81ce92c381007affe6bb2310bf94c9856eaae1alanv break; 2089a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2099a81ce92c381007affe6bb2310bf94c9856eaae1alanv } else { 2109a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Find the view that corresponds to the given id. 2119a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Key child = mVirtualViewIdToKey.get(virtualViewId); 2129a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (child == null) 2139a81ce92c381007affe6bb2310bf94c9856eaae1alanv return false; 2149a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2159a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Perform the action on a virtual view. 2169a81ce92c381007affe6bb2310bf94c9856eaae1alanv switch (action) { 2179a81ce92c381007affe6bb2310bf94c9856eaae1alanv case AccessibilityNodeInfoCompat.ACTION_SELECT: 2189a81ce92c381007affe6bb2310bf94c9856eaae1alanv // TODO: Provide some focus indicator. 2199a81ce92c381007affe6bb2310bf94c9856eaae1alanv return true; 2209a81ce92c381007affe6bb2310bf94c9856eaae1alanv case AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION: 2219a81ce92c381007affe6bb2310bf94c9856eaae1alanv // TODO: Provide some clear focus indicator. 2229a81ce92c381007affe6bb2310bf94c9856eaae1alanv return true; 2239a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2249a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2259a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2269a81ce92c381007affe6bb2310bf94c9856eaae1alanv return false; 2279a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2289a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2299a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 2309a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Finds {@link AccessibilityNodeInfoCompat}s by text. The match is case 2319a81ce92c381007affe6bb2310bf94c9856eaae1alanv * insensitive containment. The search is relative to the virtual view, i.e. 2329a81ce92c381007affe6bb2310bf94c9856eaae1alanv * a descendant of the host View, with the given <code>virtualViewId</code> or the host 2339a81ce92c381007affe6bb2310bf94c9856eaae1alanv * View itself <code>virtualViewId</code> equals to {@link View#NO_ID}. 2349a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 2359a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param virtualViewId A client defined virtual view id which defined the 2369a81ce92c381007affe6bb2310bf94c9856eaae1alanv * root of the tree in which to perform the search. 2379a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param text The searched text. 2389a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return A list of node info. 2399a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see #createAccessibilityNodeInfo(int) 2409a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see AccessibilityNodeInfoCompat 2419a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 2429a81ce92c381007affe6bb2310bf94c9856eaae1alanv @Override 2439a81ce92c381007affe6bb2310bf94c9856eaae1alanv public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText( 2449a81ce92c381007affe6bb2310bf94c9856eaae1alanv String text, int virtualViewId) { 2459a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String searchedLowerCase = text.toLowerCase(); 2469a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Keyboard keyboard = mKeyboardView.getKeyboard(); 2479a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2489a81ce92c381007affe6bb2310bf94c9856eaae1alanv List<AccessibilityNodeInfoCompat> results = null; 2499a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2509a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (virtualViewId == View.NO_ID) { 2519a81ce92c381007affe6bb2310bf94c9856eaae1alanv for (Key key : keyboard.mKeys) { 2529a81ce92c381007affe6bb2310bf94c9856eaae1alanv results = findByTextAndPopulate(searchedLowerCase, key, results); 2539a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2549a81ce92c381007affe6bb2310bf94c9856eaae1alanv } else { 2559a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Key key = mVirtualViewIdToKey.get(virtualViewId); 2569a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2579a81ce92c381007affe6bb2310bf94c9856eaae1alanv results = findByTextAndPopulate(searchedLowerCase, key, results); 2589a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2599a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2609a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (results == null) { 2619a81ce92c381007affe6bb2310bf94c9856eaae1alanv return Collections.emptyList(); 2629a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2639a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2649a81ce92c381007affe6bb2310bf94c9856eaae1alanv return results; 2659a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2669a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2679a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 2689a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Helper method for {@link #findAccessibilityNodeInfosByText(String, int)}. 2699a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Takes a current set of results and matches a specified key against a 2709a81ce92c381007affe6bb2310bf94c9856eaae1alanv * lower-case search string. Returns an updated list of results. 2719a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 2729a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param searchedLowerCase The lower-case search string. 2739a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key The key to compare against. 2749a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param results The current list of results, or {@code null} if no results 2759a81ce92c381007affe6bb2310bf94c9856eaae1alanv * found. 2769a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return An updated list of results, or {@code null} if no results found. 2779a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 2789a81ce92c381007affe6bb2310bf94c9856eaae1alanv private List<AccessibilityNodeInfoCompat> findByTextAndPopulate(String searchedLowerCase, 2799a81ce92c381007affe6bb2310bf94c9856eaae1alanv Key key, List<AccessibilityNodeInfoCompat> results) { 2809a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (!keyContainsText(key, searchedLowerCase)) { 2819a81ce92c381007affe6bb2310bf94c9856eaae1alanv return results; 2829a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2839a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2849a81ce92c381007affe6bb2310bf94c9856eaae1alanv final int childVirtualViewId = generateVirtualViewIdForKey(key); 2859a81ce92c381007affe6bb2310bf94c9856eaae1alanv final AccessibilityNodeInfoCompat nodeInfo = createAccessibilityNodeInfo( 2869a81ce92c381007affe6bb2310bf94c9856eaae1alanv childVirtualViewId); 2879a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2889a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (results == null) { 2899a81ce92c381007affe6bb2310bf94c9856eaae1alanv results = new LinkedList<AccessibilityNodeInfoCompat>(); 2909a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2919a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2929a81ce92c381007affe6bb2310bf94c9856eaae1alanv results.add(nodeInfo); 2939a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2949a81ce92c381007affe6bb2310bf94c9856eaae1alanv return results; 2959a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2969a81ce92c381007affe6bb2310bf94c9856eaae1alanv 2979a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 2989a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Returns whether a key's current description contains the lower-case 2999a81ce92c381007affe6bb2310bf94c9856eaae1alanv * search text. 3009a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 3019a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key The key to compare against. 3029a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param textLowerCase The lower-case search string. 3039a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return {@code true} if the key contains the search text. 3049a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 3059a81ce92c381007affe6bb2310bf94c9856eaae1alanv private boolean keyContainsText(Key key, String textLowerCase) { 3069a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (key == null) { 3079a81ce92c381007affe6bb2310bf94c9856eaae1alanv return false; 3089a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3099a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3109a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String description = getKeyDescription(key); 3119a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3129a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (description == null) { 3139a81ce92c381007affe6bb2310bf94c9856eaae1alanv return false; 3149a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3159a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3169a81ce92c381007affe6bb2310bf94c9856eaae1alanv return description.toLowerCase().contains(textLowerCase); 3179a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3189a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3199a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 3209a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Returns the context-specific description for a {@link Key}. 3219a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 3229a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key The key to describe. 3239a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return The context-specific description of the key. 3249a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 3259a81ce92c381007affe6bb2310bf94c9856eaae1alanv private String getKeyDescription(Key key) { 3269a81ce92c381007affe6bb2310bf94c9856eaae1alanv final EditorInfo editorInfo = mInputMethodService.getCurrentInputEditorInfo(); 3279a81ce92c381007affe6bb2310bf94c9856eaae1alanv final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo); 3289a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String keyDescription = mKeyCodeDescriptionMapper.getDescriptionForKey( 3299a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.getContext(), mKeyboardView.getKeyboard(), key, shouldObscure); 3309a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3319a81ce92c381007affe6bb2310bf94c9856eaae1alanv return keyDescription; 3329a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3339a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3349a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 3359a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Assigns virtual view IDs to keyboard keys and populates the related maps. 3369a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 3379a81ce92c381007affe6bb2310bf94c9856eaae1alanv private void assignVirtualViewIds() { 3389a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Keyboard keyboard = mKeyboardView.getKeyboard(); 3399a81ce92c381007affe6bb2310bf94c9856eaae1alanv if (keyboard == null) { 3409a81ce92c381007affe6bb2310bf94c9856eaae1alanv return; 3419a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3429a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3439a81ce92c381007affe6bb2310bf94c9856eaae1alanv mVirtualViewIdToKey.clear(); 3449a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3459a81ce92c381007affe6bb2310bf94c9856eaae1alanv final Set<Key> keySet = keyboard.mKeys; 3469a81ce92c381007affe6bb2310bf94c9856eaae1alanv for (Key key : keySet) { 3479a81ce92c381007affe6bb2310bf94c9856eaae1alanv final int virtualViewId = generateVirtualViewIdForKey(key); 3489a81ce92c381007affe6bb2310bf94c9856eaae1alanv mVirtualViewIdToKey.put(virtualViewId, key); 3499a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3509a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3519a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3529a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 3539a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Updates the parent's on-screen location. 3549a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 3559a81ce92c381007affe6bb2310bf94c9856eaae1alanv private void updateParentLocation() { 3569a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.getLocationOnScreen(mParentLocation); 3579a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3589a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3599a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 3609a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Generates a virtual view identifier for the specified key. 3619a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 3629a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key The key to identify. 3639a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return A virtual view identifier. 3649a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 3659a81ce92c381007affe6bb2310bf94c9856eaae1alanv private static int generateVirtualViewIdForKey(Key key) { 3669a81ce92c381007affe6bb2310bf94c9856eaae1alanv // The key code is unique within an instance of a Keyboard. 3679a81ce92c381007affe6bb2310bf94c9856eaae1alanv return key.mCode; 3689a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3699a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3709a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() { 3719a81ce92c381007affe6bb2310bf94c9856eaae1alanv @Override 3729a81ce92c381007affe6bb2310bf94c9856eaae1alanv public void onGlobalLayout() { 3739a81ce92c381007affe6bb2310bf94c9856eaae1alanv assignVirtualViewIds(); 3749a81ce92c381007affe6bb2310bf94c9856eaae1alanv updateParentLocation(); 3759a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3769a81ce92c381007affe6bb2310bf94c9856eaae1alanv }; 3779a81ce92c381007affe6bb2310bf94c9856eaae1alanv} 378