15ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette/* 25ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Copyright (C) 2011 The Android Open Source Project 35ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License. 68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at 75ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 95ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Unless required by applicable law or agreed to in writing, software 118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and 148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License. 155ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 165ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 175ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverettepackage com.android.inputmethod.accessibility; 185ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1987d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaokaimport android.content.Context; 201e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaokaimport android.os.SystemClock; 219a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.AccessibilityDelegateCompat; 229a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.ViewCompat; 23f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaokaimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 24639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaokaimport android.util.Log; 255ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.view.MotionEvent; 269a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.View; 27c2ee72a214fef46bc02ce486220365bbefd78714Alan Viveretteimport android.view.ViewParent; 28c2ee72a214fef46bc02ce486220365bbefd78714Alan Viveretteimport android.view.accessibility.AccessibilityEvent; 295ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 30f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaokaimport com.android.inputmethod.keyboard.Key; 316a23b9e3d54031524445b1190cc1eba77916c5b3Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyDetector; 328d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanvimport com.android.inputmethod.keyboard.Keyboard; 337b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView; 345ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 3562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka/** 3662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * This class represents a delegate that can be registered in a class that extends 3762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * {@link KeyboardView} to enhance accessibility support via composition rather via inheritance. 3862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * 3962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * To implement accessibility mode, the target keyboard view has to:<p> 4062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * - Call {@link #setKeyboard(Keyboard)} when a new keyboard is set to the keyboard view. 4162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * - Dispatch a hover event by calling {@link #onHoverEnter(MotionEvent)}. 4262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * 4362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka * @paramThe keyboard view class type. 4462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka */ 45f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaokapublic class KeyboardAccessibilityDelegate<KV extends KeyboardView> 464d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka extends AccessibilityDelegateCompat { 4762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka private static final String TAG = KeyboardAccessibilityDelegate.class.getSimpleName(); 4862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka protected static final boolean DEBUG_HOVER = false; 4962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka 504d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka protected final KV mKeyboardView; 514d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka protected final KeyDetector mKeyDetector; 5292892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka private Keyboard mKeyboard; 531e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka private KeyboardAccessibilityNodeProvider<KV> mAccessibilityNodeProvider; 5482674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka private Key mLastHoverKey; 555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 5662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka public static final int HOVER_EVENT_POINTER_ID = 0; 5762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka 584d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) { 597b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka super(); 607b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka mKeyboardView = keyboardView; 617b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka mKeyDetector = keyDetector; 625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 639a81ce92c381007affe6bb2310bf94c9856eaae1alanv // Ensure that the view has an accessibility delegate. 647b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka ViewCompat.setAccessibilityDelegate(keyboardView, this); 659a81ce92c381007affe6bb2310bf94c9856eaae1alanv } 665ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 67c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette /** 68c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette * Called when the keyboard layout changes. 69c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette * <p> 70c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette * <b>Note:</b> This method will be called even if accessibility is not 71c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette * enabled. 7292892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka * @param keyboard The keyboard that is being set to the wrapping view. 73c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette */ 7492892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka public void setKeyboard(final Keyboard keyboard) { 7592892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka if (keyboard == null) { 76559616fb0c39e2f0bacdf294b84ba16ad1e8f371Alan Viverette return; 77559616fb0c39e2f0bacdf294b84ba16ad1e8f371Alan Viverette } 78c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette if (mAccessibilityNodeProvider != null) { 7992892608228f680aa7e7c24c79c6285adbf4f4c1Tadashi G. Takaoka mAccessibilityNodeProvider.setKeyboard(keyboard); 80c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette } 813cf759074ad490790ec110836242f60333efcad4Tadashi G. Takaoka mKeyboard = keyboard; 82c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette } 83c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette 84639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka protected final Keyboard getKeyboard() { 854d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka return mKeyboard; 86c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette } 87c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette 8882674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka protected final void setLastHoverKey(final Key key) { 8982674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka mLastHoverKey = key; 90639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 91639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka 9282674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka protected final Key getLastHoverKey() { 9382674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka return mLastHoverKey; 94639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 95639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka 96c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette /** 9787d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka * Sends a window state change event with the specified string resource id. 9887d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka * 9987d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka * @param resId The string resource id of the text to send with the event. 10087d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka */ 10187d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka protected void sendWindowStateChanged(final int resId) { 10287d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka if (resId == 0) { 10387d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka return; 10487d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka } 10587d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka final Context context = mKeyboardView.getContext(); 10687d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka sendWindowStateChanged(context.getString(resId)); 10787d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka } 10887d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka 10987d2f3ea0edc8ebb724f5d2c6a07c125cfa59d23Tadashi G. Takaoka /** 110c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette * Sends a window state change event with the specified text. 111c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette * 112a7b4398c35eaf87fd00086f660af7710c071c369Alan Viverette * @param text The text to send with the event. 113c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette */ 1144d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka protected void sendWindowStateChanged(final String text) { 115c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette final AccessibilityEvent stateChange = AccessibilityEvent.obtain( 116c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 1177b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka mKeyboardView.onInitializeAccessibilityEvent(stateChange); 118c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette stateChange.getText().add(text); 119c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette stateChange.setContentDescription(null); 120c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette 1217b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka final ViewParent parent = mKeyboardView.getParent(); 122c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette if (parent != null) { 1237b90d2c432fd7ffbf0022fac9db921cf39197ac6Tadashi G. Takaoka parent.requestSendAccessibilityEvent(mKeyboardView, stateChange); 124c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette } 125f147794fd41491a3383e6aca6d49007f58124068alanv } 126f147794fd41491a3383e6aca6d49007f58124068alanv 1279a81ce92c381007affe6bb2310bf94c9856eaae1alanv /** 128bca7e4e9a2ed07d5d87f4dce9f793e40edb09691Tadashi G. Takaoka * Delegate method for View.getAccessibilityNodeProvider(). This method is called in SDK 129b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa * version 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) and higher to obtain the virtual 130b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa * node hierarchy provider. 1319a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 132559616fb0c39e2f0bacdf294b84ba16ad1e8f371Alan Viverette * @param host The host view for the provider. 1339a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @return The accessibility node provider for the current keyboard. 1349a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 1359a81ce92c381007affe6bb2310bf94c9856eaae1alanv @Override 1361e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka public KeyboardAccessibilityNodeProvider<KV> getAccessibilityNodeProvider(final View host) { 137f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return getAccessibilityNodeProvider(); 1385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /** 141bca7e4e9a2ed07d5d87f4dce9f793e40edb09691Tadashi G. Takaoka * @return A lazily-instantiated node provider for this view delegate. 142f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv */ 1431e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka protected KeyboardAccessibilityNodeProvider<KV> getAccessibilityNodeProvider() { 144f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv // Instantiate the provide only when requested. Since the system 145f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv // will call this method multiple times it is a good practice to 146f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv // cache the provider instance. 147f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv if (mAccessibilityNodeProvider == null) { 1481e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka mAccessibilityNodeProvider = 1491e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka new KeyboardAccessibilityNodeProvider<>(mKeyboardView, this); 150f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 151f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv return mAccessibilityNodeProvider; 152f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv } 153f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv 154f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv /** 155639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * Get a key that a hover event is on. 156639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * 157639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * @param event The hover event. 158639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * @return key The key that the <code>event</code> is on. 159639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka */ 16082674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka protected final Key getHoverKeyOf(final MotionEvent event) { 161639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka final int actionIndex = event.getActionIndex(); 162639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka final int x = (int)event.getX(actionIndex); 163639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka final int y = (int)event.getY(actionIndex); 164639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka return mKeyDetector.detectHitKey(x, y); 165639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 166639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka 167639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka /** 1684d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaoka * Receives hover events when touch exploration is turned on in SDK versions ICS and higher. 1699a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 1709a81ce92c381007affe6bb2310bf94c9856eaae1alanv * @param event The hover event. 1719647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka * @return {@code true} if the event is handled. 1729a81ce92c381007affe6bb2310bf94c9856eaae1alanv */ 1739647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka public boolean onHoverEvent(final MotionEvent event) { 174639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka switch (event.getActionMasked()) { 175f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka case MotionEvent.ACTION_HOVER_ENTER: 176639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka onHoverEnter(event); 1779647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka break; 178f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka case MotionEvent.ACTION_HOVER_MOVE: 179639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka onHoverMove(event); 180639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka break; 181639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka case MotionEvent.ACTION_HOVER_EXIT: 182639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka onHoverExit(event); 183639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka break; 184639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka default: 185639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka Log.w(getClass().getSimpleName(), "Unknown hover event: " + event); 186639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka break; 187639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 188639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka return true; 189639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 190639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka 191639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka /** 192639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * Process {@link MotionEvent#ACTION_HOVER_ENTER} event. 193639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * 194639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * @param event A hover enter event. 195639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka */ 196639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka protected void onHoverEnter(final MotionEvent event) { 19782674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka final Key key = getHoverKeyOf(event); 19862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 19962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka Log.d(TAG, "onHoverEnter: key=" + key); 20062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 201639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka if (key != null) { 20262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka onHoverEnterTo(key); 203639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 20482674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka setLastHoverKey(key); 205639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 206639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka 207639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka /** 208639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * Process {@link MotionEvent#ACTION_HOVER_MOVE} event. 209639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * 210639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * @param event A hover move event. 211639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka */ 212639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka protected void onHoverMove(final MotionEvent event) { 213a021395e84d4a934c042c56ef03f352cf43463f3Tadashi G. Takaoka final Key lastKey = getLastHoverKey(); 21482674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka final Key key = getHoverKeyOf(event); 215a021395e84d4a934c042c56ef03f352cf43463f3Tadashi G. Takaoka if (key != lastKey) { 216a021395e84d4a934c042c56ef03f352cf43463f3Tadashi G. Takaoka if (lastKey != null) { 21762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka onHoverExitFrom(lastKey); 218f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka } 2199647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka if (key != null) { 22062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka onHoverEnterTo(key); 2219647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka } 222f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka } 223639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka if (key != null) { 22462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka onHoverMoveWithin(key); 225639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 22682674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka setLastHoverKey(key); 227639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 228639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka 229639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka /** 230639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * Process {@link MotionEvent#ACTION_HOVER_EXIT} event. 231639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * 232639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * @param event A hover exit event. 233639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka */ 234639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka protected void onHoverExit(final MotionEvent event) { 23582674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka final Key lastKey = getLastHoverKey(); 23662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 23762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey); 23862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 23982674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka if (lastKey != null) { 24062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka onHoverExitFrom(lastKey); 24182674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka } 24282674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka final Key key = getHoverKeyOf(event); 243639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka // Make sure we're not getting an EXIT event because the user slid 244639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka // off the keyboard area, then force a key press. 245639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka if (key != null) { 2461e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka performClickOn(key); 24762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka onHoverExitFrom(key); 248639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka } 24982674ca81c40acbba4fb9b7113a9a8fe13afccc6Tadashi G. Takaoka setLastHoverKey(null); 250f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka } 251f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka 252f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka /** 2531e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka * Perform click on a key. 2541a0cd0869dbe6c860edcf19ddb5af6beaba661fcTadashi G. Takaoka * 2551a0cd0869dbe6c860edcf19ddb5af6beaba661fcTadashi G. Takaoka * @param key A key to be registered. 2561a0cd0869dbe6c860edcf19ddb5af6beaba661fcTadashi G. Takaoka */ 2571e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka public void performClickOn(final Key key) { 25862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 2591e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka Log.d(TAG, "performClickOn: key=" + key); 26062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 2611e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka simulateTouchEvent(MotionEvent.ACTION_DOWN, key); 2621e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka simulateTouchEvent(MotionEvent.ACTION_UP, key); 2631a0cd0869dbe6c860edcf19ddb5af6beaba661fcTadashi G. Takaoka } 2641a0cd0869dbe6c860edcf19ddb5af6beaba661fcTadashi G. Takaoka 2651a0cd0869dbe6c860edcf19ddb5af6beaba661fcTadashi G. Takaoka /** 2663d8848e5cb709fb47b450e7ede5a2926d99c957dTadashi G. Takaoka * Simulating a touch event by injecting a synthesized touch event into {@link KeyboardView}. 267f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka * 268639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka * @param touchAction The action of the synthesizing touch event. 2691e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka * @param key The key that a synthesized touch event is on. 270f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka */ 2711e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka private void simulateTouchEvent(final int touchAction, final Key key) { 2721e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka final int x = key.getHitBox().centerX(); 2731e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka final int y = key.getHitBox().centerY(); 2741e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka final long eventTime = SystemClock.uptimeMillis(); 2751e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka final MotionEvent touchEvent = MotionEvent.obtain( 2761e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka eventTime, eventTime, touchAction, x, y, 0 /* metaState */); 2773d8848e5cb709fb47b450e7ede5a2926d99c957dTadashi G. Takaoka mKeyboardView.onTouchEvent(touchEvent); 2783d8848e5cb709fb47b450e7ede5a2926d99c957dTadashi G. Takaoka touchEvent.recycle(); 279f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka } 280f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka 281f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka /** 2829647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka * Handles a hover enter event on a key. 283f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka * 2849647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka * @param key The currently hovered key. 285f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka */ 28662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka protected void onHoverEnterTo(final Key key) { 28762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 28862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka Log.d(TAG, "onHoverEnterTo: key=" + key); 28962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 290639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka key.onPressed(); 291639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka mKeyboardView.invalidateKey(key); 2921e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka final KeyboardAccessibilityNodeProvider<KV> provider = getAccessibilityNodeProvider(); 293d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka provider.onHoverEnterTo(key); 2949647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka provider.performActionForKey(key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); 295f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka } 296f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka 297f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka /** 2989647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka * Handles a hover move event on a key. 299f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka * 300f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka * @param key The currently hovered key. 301f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka */ 30262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka protected void onHoverMoveWithin(final Key key) { } 303f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka 3049647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka /** 3059647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka * Handles a hover exit event on a key. 3069647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka * 3079647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka * @param key The currently hovered key. 3089647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka */ 30962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka protected void onHoverExitFrom(final Key key) { 31062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka if (DEBUG_HOVER) { 31162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka Log.d(TAG, "onHoverExitFrom: key=" + key); 31262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka } 313639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka key.onReleased(); 314639e431fa24b96a6118c85407d1f4a0af73a2813Tadashi G. Takaoka mKeyboardView.invalidateKey(key); 3151e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka final KeyboardAccessibilityNodeProvider<KV> provider = getAccessibilityNodeProvider(); 316d4b6af14d8fc90ae64f55d87d88cdfd5885cb63dTadashi G. Takaoka provider.onHoverExitFrom(key); 317f22285006af2d132a568aafbff85efb83698eaebTadashi G. Takaoka } 3181e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka 3191e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka /** 3201e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka * Perform long click on a key. 3211e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka * 3221e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka * @param key A key to be long pressed on. 3231e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka */ 3241e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka public void performLongClickOn(final Key key) { 3251e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka // A extended class should override this method to implement long press. 3261e3167229519843b83ba8bea7d78a82ffba236bcTadashi G. Takaoka } 3275ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette} 328