14d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka/* 24d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Copyright (C) 2014 The Android Open Source Project 34d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * 44d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 54d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * you may not use this file except in compliance with the License. 64d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * You may obtain a copy of the License at 74d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * 84d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 94d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * 104d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 114d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 124d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * See the License for the specific language governing permissions and 144d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * limitations under the License. 154d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka */ 164d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 174d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokapackage com.android.inputmethod.accessibility; 184d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 194d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport android.content.Context; 20176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaokaimport android.graphics.Rect; 2162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaokaimport android.os.SystemClock; 2262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaokaimport android.util.Log; 234d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport android.util.SparseIntArray; 2462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaokaimport android.view.MotionEvent; 254d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 2662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaokaimport com.android.inputmethod.keyboard.Key; 274d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyDetector; 284d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard; 294d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardId; 304d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport com.android.inputmethod.keyboard.MainKeyboardView; 3162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker; 324d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport com.android.inputmethod.latin.R; 334d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport com.android.inputmethod.latin.utils.SubtypeLocaleUtils; 344d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 3562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka/** 3662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * This class represents a delegate that can be registered in {@link MainKeyboardView} to enhance 3762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * accessibility support via composition rather via inheritance. 3862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka */ 394d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokapublic final class MainKeyboardAccessibilityDelegate 4062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka extends KeyboardAccessibilityDelegate<MainKeyboardView> 4162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka implements AccessibilityLongPressTimer.LongPressTimerCallback { 4262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka private static final String TAG = MainKeyboardAccessibilityDelegate.class.getSimpleName(); 4362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka 444d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka /** Map of keyboard modes to resource IDs. */ 454d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka private static final SparseIntArray KEYBOARD_MODE_RES_IDS = new SparseIntArray(); 464d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 474d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka static { 484d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATE, R.string.keyboard_mode_date); 494d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATETIME, R.string.keyboard_mode_date_time); 504d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_EMAIL, R.string.keyboard_mode_email); 514d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_IM, R.string.keyboard_mode_im); 524d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_NUMBER, R.string.keyboard_mode_number); 534d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_PHONE, R.string.keyboard_mode_phone); 544d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TEXT, R.string.keyboard_mode_text); 554d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TIME, R.string.keyboard_mode_time); 564d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_URL, R.string.keyboard_mode_url); 574d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 584d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 594d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka /** The most recently set keyboard mode. */ 604d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN; 614d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka private static final int KEYBOARD_IS_HIDDEN = -1; 62176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // The rectangle region to ignore hover events. 63176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka private final Rect mBoundsToIgnoreHoverEvent = new Rect(); 6462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka 6562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka private final AccessibilityLongPressTimer mAccessibilityLongPressTimer; 664d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 674d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka public MainKeyboardAccessibilityDelegate(final MainKeyboardView mainKeyboardView, 684d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final KeyDetector keyDetector) { 694d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka super(mainKeyboardView, keyDetector); 7062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka mAccessibilityLongPressTimer = new AccessibilityLongPressTimer( 7162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka this /* callback */, mainKeyboardView.getContext()); 724d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 734d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 744d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka /** 754d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * {@inheritDoc} 764d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka */ 774d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka @Override 784d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka public void setKeyboard(final Keyboard keyboard) { 794d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka if (keyboard == null) { 804d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return; 814d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 824d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final Keyboard lastKeyboard = getKeyboard(); 834d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka super.setKeyboard(keyboard); 844d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final int lastKeyboardMode = mLastKeyboardMode; 854d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka mLastKeyboardMode = keyboard.mId.mMode; 864d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 874d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka // Since this method is called even when accessibility is off, make sure 884d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka // to check the state before announcing anything. 894d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka if (!AccessibilityUtils.getInstance().isAccessibilityEnabled()) { 904d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return; 914d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 924d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka // Announce the language name only when the language is changed. 934d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka if (lastKeyboard == null || !keyboard.mId.mSubtype.equals(lastKeyboard.mId.mSubtype)) { 944d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka announceKeyboardLanguage(keyboard); 954d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return; 964d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 974d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka // Announce the mode only when the mode is changed. 984d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka if (keyboard.mId.mMode != lastKeyboardMode) { 994d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka announceKeyboardMode(keyboard); 1004d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return; 1014d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 1024d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka // Announce the keyboard type only when the type is changed. 1034d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka if (keyboard.mId.mElementId != lastKeyboard.mId.mElementId) { 1044d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka announceKeyboardType(keyboard, lastKeyboard); 1054d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return; 1064d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 1074d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 1084d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 1094d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka /** 1104d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Called when the keyboard is hidden and accessibility is enabled. 1114d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka */ 1124d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka public void onHideWindow() { 11338b15afb47feab79c8a8bc90e3daf2afcba935acYohei Yukawa if (mLastKeyboardMode != KEYBOARD_IS_HIDDEN) { 11438b15afb47feab79c8a8bc90e3daf2afcba935acYohei Yukawa announceKeyboardHidden(); 11538b15afb47feab79c8a8bc90e3daf2afcba935acYohei Yukawa } 1164d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka mLastKeyboardMode = KEYBOARD_IS_HIDDEN; 1174d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 1184d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 1194d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka /** 1204d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Announces which language of keyboard is being displayed. 1214d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * 1224d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * @param keyboard The new keyboard. 1234d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka */ 1244d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka private void announceKeyboardLanguage(final Keyboard keyboard) { 1254d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final String languageText = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale( 12685ddfe1317a4475269e53f62c2338c335e02e839Jean Chalard keyboard.mId.mSubtype.getRawSubtype()); 1274d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka sendWindowStateChanged(languageText); 1284d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 1294d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 1304d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka /** 1314d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Announces which type of keyboard is being displayed. 1324d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * If the keyboard type is unknown, no announcement is made. 1334d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * 1344d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * @param keyboard The new keyboard. 1354d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka */ 1364d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka private void announceKeyboardMode(final Keyboard keyboard) { 1374d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final Context context = mKeyboardView.getContext(); 1384d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final int modeTextResId = KEYBOARD_MODE_RES_IDS.get(keyboard.mId.mMode); 1394d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka if (modeTextResId == 0) { 1404d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return; 1414d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 1424d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final String modeText = context.getString(modeTextResId); 1434d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final String text = context.getString(R.string.announce_keyboard_mode, modeText); 1444d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka sendWindowStateChanged(text); 1454d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 1464d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 1474d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka /** 1484d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Announces which type of keyboard is being displayed. 1494d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * 1504d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * @param keyboard The new keyboard. 1514d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * @param lastKeyboard The last keyboard. 1524d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka */ 1534d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka private void announceKeyboardType(final Keyboard keyboard, final Keyboard lastKeyboard) { 1544d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final int lastElementId = lastKeyboard.mId.mElementId; 1554d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka final int resId; 1564d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka switch (keyboard.mId.mElementId) { 1574d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: 1584d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_ALPHABET: 1594d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka if (lastElementId == KeyboardId.ELEMENT_ALPHABET 1604d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka || lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { 16148b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka // Transition between alphabet mode and automatic shifted mode should be silently 16248b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka // ignored because it can be determined by each key's talk back announce. 1634d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return; 1644d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 1654d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka resId = R.string.spoken_description_mode_alpha; 1664d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka break; 1674d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: 16848b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka if (lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { 16948b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka // Resetting automatic shifted mode by pressing the shift key causes the transition 17048b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka // from automatic shifted to manual shifted that should be silently ignored. 17148b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka return; 17248b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka } 1734d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka resId = R.string.spoken_description_shiftmode_on; 1744d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka break; 1754d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: 17648b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka if (lastElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) { 17748b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka // Resetting caps locked mode by pressing the shift key causes the transition 17848b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka // from shift locked to shift lock shifted that should be silently ignored. 17948b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka return; 18048b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka } 18148b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka resId = R.string.spoken_description_shiftmode_locked; 18248b96a6a52dab6f3a44adf80b40832d629fe5871Tadashi G. Takaoka break; 1834d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: 1844d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka resId = R.string.spoken_description_shiftmode_locked; 1854d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka break; 1864d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_SYMBOLS: 1874d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka resId = R.string.spoken_description_mode_symbol; 1884d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka break; 1894d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: 1904d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka resId = R.string.spoken_description_mode_symbol_shift; 1914d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka break; 1924d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_PHONE: 1934d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka resId = R.string.spoken_description_mode_phone; 1944d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka break; 1954d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka case KeyboardId.ELEMENT_PHONE_SYMBOLS: 1964d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka resId = R.string.spoken_description_mode_phone_shift; 1974d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka break; 1984d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka default: 1994d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return; 2004d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 20187d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka sendWindowStateChanged(resId); 2024d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 2034d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka 2044d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka /** 2054d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Announces that the keyboard has been hidden. 2064d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka */ 2074d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka private void announceKeyboardHidden() { 20887d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka sendWindowStateChanged(R.string.announce_keyboard_hidden); 2094d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka } 21062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka 21162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka @Override 2121e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka public void performClickOn(final Key key) { 213176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka final int x = key.getHitBox().centerX(); 214176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka final int y = key.getHitBox().centerY(); 21562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 2161e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka Log.d(TAG, "performClickOn: key=" + key 217176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); 21862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 219176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka if (mBoundsToIgnoreHoverEvent.contains(x, y)) { 220176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // This hover exit event points to the key that should be ignored. 221176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // Clear the ignoring region to handle further hover events. 222176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka mBoundsToIgnoreHoverEvent.setEmpty(); 223176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka return; 22462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 2251e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka super.performClickOn(key); 22662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 22762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka 22862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka @Override 22962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka protected void onHoverEnterTo(final Key key) { 230176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka final int x = key.getHitBox().centerX(); 231176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka final int y = key.getHitBox().centerY(); 23262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 233176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka Log.d(TAG, "onHoverEnterTo: key=" + key 234176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); 23562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 23662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka mAccessibilityLongPressTimer.cancelLongPress(); 237176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka if (mBoundsToIgnoreHoverEvent.contains(x, y)) { 238176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka return; 239176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka } 240176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // This hover enter event points to the key that isn't in the ignoring region. 241176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // Further hover events should be handled. 242176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka mBoundsToIgnoreHoverEvent.setEmpty(); 24362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka super.onHoverEnterTo(key); 24462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (key.isLongPressEnabled()) { 24562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka mAccessibilityLongPressTimer.startLongPress(key); 24662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 24762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 24862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka 249176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka @Override 25062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka protected void onHoverExitFrom(final Key key) { 251176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka final int x = key.getHitBox().centerX(); 252176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka final int y = key.getHitBox().centerY(); 25362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 254176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka Log.d(TAG, "onHoverExitFrom: key=" + key 255176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); 25662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 25762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka mAccessibilityLongPressTimer.cancelLongPress(); 25862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka super.onHoverExitFrom(key); 25962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 26062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka 26162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka @Override 2621e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka public void performLongClickOn(final Key key) { 26362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 2641e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka Log.d(TAG, "performLongClickOn: key=" + key); 26562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 26662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(HOVER_EVENT_POINTER_ID); 26762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka final long eventTime = SystemClock.uptimeMillis(); 26862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka final int x = key.getHitBox().centerX(); 26962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka final int y = key.getHitBox().centerY(); 27062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka final MotionEvent downEvent = MotionEvent.obtain( 27162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka eventTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0 /* metaState */); 27262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka // Inject a fake down event to {@link PointerTracker} to handle a long press correctly. 27362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka tracker.processMotionEvent(downEvent, mKeyDetector); 27462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka downEvent.recycle(); 27553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka // Invoke {@link PointerTracker#onLongPressed()} as if a long press timeout has passed. 27653b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka tracker.onLongPressed(); 27762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka // If {@link Key#hasNoPanelAutoMoreKeys()} is true (such as "0 +" key on the phone layout) 27862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka // or a key invokes IME switcher dialog, we should just ignore the next 27962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka // {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether 28062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka // {@link PointerTracker} is in operation or not. 281176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka if (tracker.isInOperation()) { 282176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // This long press shows a more keys keyboard and further hover events should be 283176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // handled. 284176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka mBoundsToIgnoreHoverEvent.setEmpty(); 285176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka return; 286176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka } 287176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // This long press has handled at {@link MainKeyboardView#onLongPress(PointerTracker)}. 288176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // We should ignore further hover events on this key. 289176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka mBoundsToIgnoreHoverEvent.set(key.getHitBox()); 290176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka if (key.hasNoPanelAutoMoreKey()) { 291176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // This long press has registered a code point without showing a more keys keyboard. 292176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka // We should talk back the code point if possible. 293176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka final int codePointOfNoPanelAutoMoreKey = key.getMoreKeys()[0].mCode; 294176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka final String text = KeyCodeDescriptionMapper.getInstance().getDescriptionForCodePoint( 295176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka mKeyboardView.getContext(), codePointOfNoPanelAutoMoreKey); 296176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka if (text != null) { 297176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka sendWindowStateChanged(text); 298176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka } 299176f803176de964cbb3715cfe033797de62aa1feTadashi G. Takaoka } 30062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 3014d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka} 302