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; 20f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.os.Bundle; 219a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.ViewCompat; 22f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.support.v4.view.accessibility.AccessibilityEventCompat; 239a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 249a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; 259a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityRecordCompat; 269a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.util.Log; 279a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.View; 289a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.accessibility.AccessibilityEvent; 299a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.inputmethod.EditorInfo; 309a81ce92c381007affe6bb2310bf94c9856eaae1alanv 319a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.Key; 329a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.Keyboard; 339a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.KeyboardView; 345b91b551e5ffaf2c2e691dfbd434f21c82293986Jean Chalardimport com.android.inputmethod.latin.common.CoordinateUtils; 3567319f92f31ca5b40e1f80f7b9ae63b9d8886f0eAlan Viveretteimport com.android.inputmethod.latin.settings.Settings; 3667319f92f31ca5b40e1f80f7b9ae63b9d8886f0eAlan Viveretteimport com.android.inputmethod.latin.settings.SettingsValues; 379a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3892892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaokaimport java.util.List; 3992892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka 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 */ 501e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaokafinal class KeyboardAccessibilityNodeProvider<KV extends KeyboardView> 511e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka extends AccessibilityNodeProviderCompat { 527b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka private static final String TAG = KeyboardAccessibilityNodeProvider.class.getSimpleName(); 539934740a5fd85ad0ea6126328ba9015069d2fdc0Tadashi G. Takaoka 549934740a5fd85ad0ea6126328ba9015069d2fdc0Tadashi G. Takaoka // From {@link android.view.accessibility.AccessibilityNodeInfo#UNDEFINED_ITEM_ID}. 559934740a5fd85ad0ea6126328ba9015069d2fdc0Tadashi G. Takaoka private static final int UNDEFINED = Integer.MAX_VALUE; 569a81ce92c381007affe6bb2310bf94c9856eaae1alanv 579a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper; 589a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final AccessibilityUtils mAccessibilityUtils; 599a81ce92c381007affe6bb2310bf94c9856eaae1alanv 609a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** Temporary rect used to calculate in-screen bounds. */ 619a81ce92c381007affe6bb2310bf94c9856eaae1alanv private final Rect mTempBoundsInScreen = new Rect(); 629a81ce92c381007affe6bb2310bf94c9856eaae1alanv 639a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** The parent view's cached on-screen location. */ 64359c35e0f6e534d096efc1d9f2ff585139e3b3acTadashi G. Takaoka private final int[] mParentLocation = CoordinateUtils.newInstance(); 659a81ce92c381007affe6bb2310bf94c9856eaae1alanv 66f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** The virtual view identifier for the focused node. */ 67f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv private int mAccessibilityFocusedView = UNDEFINED; 68f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 69d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka /** The virtual view identifier for the hovering node. */ 70d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka private int mHoveringNodeId = UNDEFINED; 71d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka 723d8848e5cb709fb47b450e7ede5a2926d99c957dTadashi G. Takaoka /** The keyboard view to provide an accessibility node info. */ 731e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka private final KV mKeyboardView; 741e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka /** The accessibility delegate. */ 751e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka private final KeyboardAccessibilityDelegate<KV> mDelegate; 7648ccd5528163383a46b597e9d5ea919ddc799f25alanv 7792892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka /** The current keyboard. */ 7892892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka private Keyboard mKeyboard; 7992892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka 801e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka public KeyboardAccessibilityNodeProvider(final KV keyboardView, 811e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka final KeyboardAccessibilityDelegate<KV> delegate) { 827b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka super(); 839a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance(); 849a81ce92c381007affe6bb2310bf94c9856eaae1alanv mAccessibilityUtils = AccessibilityUtils.getInstance(); 8548ccd5528163383a46b597e9d5ea919ddc799f25alanv mKeyboardView = keyboardView; 861e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka mDelegate = delegate; 87f147794fd41491a3383e6aca6d49007f58124068alanv 88f147794fd41491a3383e6aca6d49007f58124068alanv // Since this class is constructed lazily, we might not get a subsequent 89f147794fd41491a3383e6aca6d49007f58124068alanv // call to setKeyboard() and therefore need to call it now. 9092892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka setKeyboard(keyboardView.getKeyboard()); 91f147794fd41491a3383e6aca6d49007f58124068alanv } 9248ccd5528163383a46b597e9d5ea919ddc799f25alanv 93f147794fd41491a3383e6aca6d49007f58124068alanv /** 94f147794fd41491a3383e6aca6d49007f58124068alanv * Sets the keyboard represented by this node provider. 9592892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka * 9692892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka * @param keyboard The keyboard that is being set to the keyboard view. 97f147794fd41491a3383e6aca6d49007f58124068alanv */ 9892892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka public void setKeyboard(final Keyboard keyboard) { 9992892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka mKeyboard = keyboard; 10092892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka } 10192892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka 10292892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka private Key getKeyOf(final int virtualViewId) { 10392892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka if (mKeyboard == null) { 10492892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka return null; 10592892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka } 10692892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka final List<Key> sortedKeys = mKeyboard.getSortedKeys(); 10792892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka // Use a virtual view id as an index of the sorted keys list. 10892892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka if (virtualViewId >= 0 && virtualViewId < sortedKeys.size()) { 10992892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka return sortedKeys.get(virtualViewId); 11092892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka } 11192892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka return null; 11292892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka } 11392892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka 11492892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka private int getVirtualViewIdOf(final Key key) { 11592892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka if (mKeyboard == null) { 11692892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka return View.NO_ID; 11792892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka } 11892892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka final List<Key> sortedKeys = mKeyboard.getSortedKeys(); 11992892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka final int size = sortedKeys.size(); 12092892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka for (int index = 0; index < size; index++) { 12192892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka if (sortedKeys.get(index) == key) { 12292892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka // Use an index of the sorted keys list as a virtual view id. 12392892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka return index; 12492892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka } 12592892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka } 12692892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka return View.NO_ID; 1279a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1289a81ce92c381007affe6bb2310bf94c9856eaae1alanv 1299a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 1309a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Creates and populates an {@link AccessibilityEvent} for the specified key 1319a81ce92c381007affe6bb2310bf94c9856eaae1alanv * and event type. 1329a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 1339a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key A key on the host keyboard view. 1349a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param eventType The event type to create. 1359a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return A populated {@link AccessibilityEvent} for the key. 1369a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see AccessibilityEvent 1379a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 138b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa public AccessibilityEvent createAccessibilityEvent(final Key key, final int eventType) { 13992892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka final int virtualViewId = getVirtualViewIdOf(key); 1409a81ce92c381007affe6bb2310bf94c9856eaae1alanv final String keyDescription = getKeyDescription(key); 1419a81ce92c381007affe6bb2310bf94c9856eaae1alanv final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); 1429a81ce92c381007affe6bb2310bf94c9856eaae1alanv event.setPackageName(mKeyboardView.getContext().getPackageName()); 1439a81ce92c381007affe6bb2310bf94c9856eaae1alanv event.setClassName(key.getClass().getName()); 1446662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv event.setContentDescription(keyDescription); 14526c80a1b9a103cdccbaeafac75a3db2543a9ee7ealanv event.setEnabled(true); 146a80d55d4d374e6ca4459f975e885c2d4649c5f96Tadashi G. Takaoka final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); 1479a81ce92c381007affe6bb2310bf94c9856eaae1alanv record.setSource(mKeyboardView, virtualViewId); 1489a81ce92c381007affe6bb2310bf94c9856eaae1alanv return event; 1499a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 1509a81ce92c381007affe6bb2310bf94c9856eaae1alanv 151d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka public void onHoverEnterTo(final Key key) { 152d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka final int id = getVirtualViewIdOf(key); 153d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka if (id == View.NO_ID) { 154d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka return; 155d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka } 156d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka // Start hovering on the key. Because our accessibility model is lift-to-type, we should 157d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka // report the node info without click and long click actions to avoid unnecessary 158d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka // announcements. 159d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka mHoveringNodeId = id; 160d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka // Invalidate the node info of the key. 161d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED); 162d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); 163d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka } 164d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka 165d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka public void onHoverExitFrom(final Key key) { 166d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka mHoveringNodeId = UNDEFINED; 167d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka // Invalidate the node info of the key to be able to revert the change we have done 168d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka // in {@link #onHoverEnterTo(Key)}. 169d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED); 170d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); 171d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka } 172d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka 1739a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 1749a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual 1759a81ce92c381007affe6bb2310bf94c9856eaae1alanv * view, i.e. a descendant of the host View, with the given <code>virtualViewId</code> or 1769a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}. 1779a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p> 1789a81ce92c381007affe6bb2310bf94c9856eaae1alanv * A virtual descendant is an imaginary View that is reported as a part of 1799a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the view hierarchy for accessibility purposes. This enables custom views 1809a81ce92c381007affe6bb2310bf94c9856eaae1alanv * that draw complex content to report them selves as a tree of virtual 1819a81ce92c381007affe6bb2310bf94c9856eaae1alanv * views, thus conveying their logical structure. 1829a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p> 1839a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p> 1849a81ce92c381007affe6bb2310bf94c9856eaae1alanv * The implementer is responsible for obtaining an accessibility node info 1859a81ce92c381007affe6bb2310bf94c9856eaae1alanv * from the pool of reusable instances and setting the desired properties of 1869a81ce92c381007affe6bb2310bf94c9856eaae1alanv * the node info before returning it. 1879a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p> 1889a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 1899a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param virtualViewId A client defined virtual view id. 190b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual descendant or the host 191b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa * View. 1929a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @see AccessibilityNodeInfoCompat 1939a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 1949a81ce92c381007affe6bb2310bf94c9856eaae1alanv @Override 195b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(final int virtualViewId) { 196f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv if (virtualViewId == UNDEFINED) { 197f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return null; 198b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa } 199b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa if (virtualViewId == View.NO_ID) { 2009a81ce92c381007affe6bb2310bf94c9856eaae1alanv // We are requested to create an AccessibilityNodeInfo describing 201d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka // this View, i.e. the root of the virtual sub-tree. 202b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa final AccessibilityNodeInfoCompat rootInfo = 203b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa AccessibilityNodeInfoCompat.obtain(mKeyboardView); 204b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, rootInfo); 205d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka updateParentLocation(); 206d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka 207d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka // Add the virtual children of the root View. 208d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka final List<Key> sortedKeys = mKeyboard.getSortedKeys(); 209d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka final int size = sortedKeys.size(); 210d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka for (int index = 0; index < size; index++) { 211d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka final Key key = sortedKeys.get(index); 212d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka if (key.isSpacer()) { 213d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka continue; 214d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka } 215d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka // Use an index of the sorted keys list as a virtual view id. 216d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka rootInfo.addChild(mKeyboardView, index); 217d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka } 218b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa return rootInfo; 2199a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2209a81ce92c381007affe6bb2310bf94c9856eaae1alanv 22192892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka // Find the key that corresponds to the given virtual view id. 22292892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka final Key key = getKeyOf(virtualViewId); 223b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa if (key == null) { 224b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa Log.e(TAG, "Invalid virtual view ID: " + virtualViewId); 225b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa return null; 226b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa } 227b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa final String keyDescription = getKeyDescription(key); 2287dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka final Rect boundsInParent = key.getHitBox(); 229b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa 230b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa // Calculate the key's in-screen bounds. 231b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa mTempBoundsInScreen.set(boundsInParent); 232b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa mTempBoundsInScreen.offset( 233b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa CoordinateUtils.x(mParentLocation), CoordinateUtils.y(mParentLocation)); 234b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa final Rect boundsInScreen = mTempBoundsInScreen; 235b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa 236b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa // Obtain and initialize an AccessibilityNodeInfo with information about the virtual view. 237b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); 238b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.setPackageName(mKeyboardView.getContext().getPackageName()); 239b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.setClassName(key.getClass().getName()); 240b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.setContentDescription(keyDescription); 241b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.setBoundsInParent(boundsInParent); 242b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.setBoundsInScreen(boundsInScreen); 243b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.setParent(mKeyboardView); 244b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.setSource(mKeyboardView, virtualViewId); 245d801b6ee668f6ed4ef1926fd2cdb928fd36ace3aTadashi G. Takaoka info.setEnabled(key.isEnabled()); 246b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.setVisibleToUser(true); 247d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka // Don't add ACTION_CLICK and ACTION_LONG_CLOCK actions while hovering on the key. 248d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka // See {@link #onHoverEnterTo(Key)} and {@link #onHoverExitFrom(Key)}. 249d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka if (virtualViewId != mHoveringNodeId) { 250d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); 251d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka if (key.isLongPressEnabled()) { 252d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka info.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK); 253d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka } 254d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka } 255b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa 256b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa if (mAccessibilityFocusedView == virtualViewId) { 257b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS); 258b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa } else { 259b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); 260b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa } 2619a81ce92c381007affe6bb2310bf94c9856eaae1alanv return info; 2629a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 2639a81ce92c381007affe6bb2310bf94c9856eaae1alanv 264f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv @Override 265b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa public boolean performAction(final int virtualViewId, final int action, 266b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa final Bundle arguments) { 26792892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka final Key key = getKeyOf(virtualViewId); 268f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv if (key == null) { 269f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return false; 270f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 271a1b47eb44690ecfdc8411d5ada77233dbe8fb2e1Tadashi G. Takaoka return performActionForKey(key, action); 272f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 273f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 274f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** 275f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * Performs the specified accessibility action for the given key. 276f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * 277f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param key The on which to perform the action. 278f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param action The action to perform. 279b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa * @return The result of performing the action, or false if the action is not supported. 280f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv */ 281a1b47eb44690ecfdc8411d5ada77233dbe8fb2e1Tadashi G. Takaoka boolean performActionForKey(final Key key, final int action) { 282f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv switch (action) { 283f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: 284a1b47eb44690ecfdc8411d5ada77233dbe8fb2e1Tadashi G. Takaoka mAccessibilityFocusedView = getVirtualViewIdOf(key); 285f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv sendAccessibilityEventForKey( 286f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 287f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return true; 288f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS: 289f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv mAccessibilityFocusedView = UNDEFINED; 290f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv sendAccessibilityEventForKey( 291f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); 292f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return true; 293d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka case AccessibilityNodeInfoCompat.ACTION_CLICK: 294d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_CLICKED); 2951e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka mDelegate.performClickOn(key); 296d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka return true; 297d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka case AccessibilityNodeInfoCompat.ACTION_LONG_CLICK: 298d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 2991e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka mDelegate.performLongClickOn(key); 300d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka return true; 301b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa default: 302b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa return false; 303f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 304f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 305f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 306f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** 307f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * Sends an accessibility event for the given {@link Key}. 308f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * 309f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param key The key that's sending the event. 310f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv * @param eventType The type of event to send. 311f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv */ 312b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa void sendAccessibilityEventForKey(final Key key, final int eventType) { 313f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv final AccessibilityEvent event = createAccessibilityEvent(key, eventType); 314067689c57d353e4e37e0457989c6c2686977df9ealanv mAccessibilityUtils.requestSendAccessibilityEvent(event); 315f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 316f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 317f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** 3189a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Returns the context-specific description for a {@link Key}. 3199a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 3209a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param key The key to describe. 3219a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return The context-specific description of the key. 3229a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 323b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa private String getKeyDescription(final Key key) { 3247a78127a56bc427fbc690cb0561c415a81064e64Tadashi G. Takaoka final EditorInfo editorInfo = mKeyboard.mId.mEditorInfo; 3259a81ce92c381007affe6bb2310bf94c9856eaae1alanv final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo); 32667319f92f31ca5b40e1f80f7b9ae63b9d8886f0eAlan Viverette final SettingsValues currentSettings = Settings.getInstance().getCurrent(); 32767319f92f31ca5b40e1f80f7b9ae63b9d8886f0eAlan Viverette final String keyCodeDescription = mKeyCodeDescriptionMapper.getDescriptionForKey( 32892892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka mKeyboardView.getContext(), mKeyboard, key, shouldObscure); 32967319f92f31ca5b40e1f80f7b9ae63b9d8886f0eAlan Viverette if (currentSettings.isWordSeparator(key.getCode())) { 33067319f92f31ca5b40e1f80f7b9ae63b9d8886f0eAlan Viverette return mAccessibilityUtils.getAutoCorrectionDescription( 33167319f92f31ca5b40e1f80f7b9ae63b9d8886f0eAlan Viverette keyCodeDescription, shouldObscure); 33267319f92f31ca5b40e1f80f7b9ae63b9d8886f0eAlan Viverette } 3335f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka return keyCodeDescription; 3349a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3359a81ce92c381007affe6bb2310bf94c9856eaae1alanv 3369a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 3379a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Updates the parent's on-screen location. 3389a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 3399a81ce92c381007affe6bb2310bf94c9856eaae1alanv private void updateParentLocation() { 3409a81ce92c381007affe6bb2310bf94c9856eaae1alanv mKeyboardView.getLocationOnScreen(mParentLocation); 3419a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 3429a81ce92c381007affe6bb2310bf94c9856eaae1alanv} 343