PointerTracker.java revision 1a6fba570260ca9f837e5a6874274f39a3c0a734
16a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka/* 26a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Copyright (C) 2010 Google Inc. 36a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * 46a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); you may not 56a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * use this file except in compliance with the License. You may obtain a copy of 66a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * the License at 76a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * 86a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 96a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * 106a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 136a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * License for the specific language governing permissions and limitations under 146a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * the License. 156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka */ 166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard; 186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 195a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView.UIHandler; 205a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.latin.R; 216a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 225e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaokaimport android.content.res.Resources; 2340a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaokaimport android.util.Log; 24c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaokaimport android.view.MotionEvent; 256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 26dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaokaimport java.util.Arrays; 27dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 286a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaokapublic class PointerTracker { 29dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final String TAG = PointerTracker.class.getSimpleName(); 301d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka private static final boolean ENABLE_ASSERTION = false; 31dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final boolean DEBUG_EVENT = false; 32dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final boolean DEBUG_MOVE_EVENT = false; 33dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final boolean DEBUG_LISTENER = false; 3440a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka 356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public interface UIProxy { 366a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public void invalidateKey(Key key); 376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public void showPreview(int keyIndex, PointerTracker tracker); 385e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka public boolean hasDistinctMultitouch(); 396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 406a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 41c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka public final int mPointerId; 42c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka 436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // Timing constants 445e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka private final int mDelayBeforeKeyRepeatStart; 455e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka private final int mLongPressKeyTimeout; 464189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka private final int mLongPressShiftKeyTimeout; 476a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // Miscellaneous constants 495a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka private static final int NOT_A_KEY = KeyDetector.NOT_A_KEY; 506a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private final UIProxy mProxy; 526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private final UIHandler mHandler; 532085d43daf44752deae1b6b00a14cb0f517d69cbTadashi G. Takaoka private final KeyDetector mKeyDetector; 54dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private KeyboardActionListener mListener = EMPTY_LISTENER; 559e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka private final KeyboardSwitcher mKeyboardSwitcher; 56c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka private final boolean mHasDistinctMultitouch; 5767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka private final boolean mConfigSlidingKeyInputEnabled; 586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 59baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka private final int mTouchNoiseThresholdMillis; 60baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka private final int mTouchNoiseThresholdDistanceSquared; 61baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 625a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka private Keyboard mKeyboard; 636a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private Key[] mKeys; 64eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka private int mKeyHysteresisDistanceSquared = -1; 656a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 66dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private final PointerTrackerKeyState mKeyState; 676a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 681a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // true if keyboard layout has been changed. 691a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka private boolean mKeyboardLayoutHasBeenChanged; 701a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka 71c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka // true if event is already translated to a key action (long press or mini-keyboard) 72c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka private boolean mKeyAlreadyProcessed; 73c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka 746252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka // true if this pointer is repeatable key 756252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka private boolean mIsRepeatableKey; 766252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka 77cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka // true if this pointer is in sliding key input 78cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka private boolean mIsInSlidingKeyInput; 79cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka 8067a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka // true if sliding key is allowed. 8167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka private boolean mIsAllowedSlidingKeyInput; 8267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka 836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // pressed key 84a521b31d96ff51672a3bc399a27a8f02dca840faTadashi G. Takaoka private int mPreviousKey = NOT_A_KEY; 856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 86dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // Empty {@link KeyboardActionListener} 87dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() { 88dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 89dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka public void onPress(int primaryCode) {} 90dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 91dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka public void onRelease(int primaryCode) {} 92dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 938aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {} 94dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 958aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka public void onTextInput(CharSequence text) {} 96dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 978aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka public void onCancelInput() {} 98dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 998aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka public void onSwipeDown() {} 100dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka }; 1016e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 102c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy, 1035e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka Resources res) { 1046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (proxy == null || handler == null || keyDetector == null) 1056a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new NullPointerException(); 106c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka mPointerId = id; 1076a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy = proxy; 1086a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler = handler; 1096a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeyDetector = keyDetector; 1109e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka mKeyboardSwitcher = KeyboardSwitcher.getInstance(); 111dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka mKeyState = new PointerTrackerKeyState(keyDetector); 1125e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); 11367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled); 1145e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); 1155e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout); 1164189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout); 117baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka mTouchNoiseThresholdMillis = res.getInteger(R.integer.config_touch_noise_threshold_millis); 118baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final float touchNoiseThresholdDistance = res.getDimension( 119baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka R.dimen.config_touch_noise_threshold_distance); 120baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka mTouchNoiseThresholdDistanceSquared = (int)( 121baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka touchNoiseThresholdDistance * touchNoiseThresholdDistance); 1226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 1245a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka public void setOnKeyboardActionListener(KeyboardActionListener listener) { 1256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mListener = listener; 1266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1276a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 1281a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // Returns true if keyboard has been changed by this callback. 1291a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka private boolean callListenerOnPressAndCheckKeyboardLayoutChange(int primaryCode) { 130dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 131dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka Log.d(TAG, "onPress : " + keyCodePrintable(primaryCode)); 132dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka mListener.onPress(primaryCode); 1331a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; 1341a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka mKeyboardLayoutHasBeenChanged = false; 1351a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka return keyboardLayoutHasBeenChanged; 136dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 137dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 1388aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka private void callListenerOnCodeInput(int primaryCode, int[] keyCodes, int x, int y) { 139dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 1408aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka Log.d(TAG, "onCodeInput: " + keyCodePrintable(primaryCode) 141dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka + " codes="+ Arrays.toString(keyCodes) + " x=" + x + " y=" + y); 1428aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka mListener.onCodeInput(primaryCode, keyCodes, x, y); 143dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 144dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 1458aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka private void callListenerOnTextInput(CharSequence text) { 146dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 1478aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka Log.d(TAG, "onTextInput: text=" + text); 1488aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka mListener.onTextInput(text); 149dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 150dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 151dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private void callListenerOnRelease(int primaryCode) { 152dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 153dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka Log.d(TAG, "onRelease : " + keyCodePrintable(primaryCode)); 154dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka mListener.onRelease(primaryCode); 155dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 156dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 1578aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka private void callListenerOnCancelInput() { 158dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 1598aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka Log.d(TAG, "onCancelInput"); 1608aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka mListener.onCancelInput(); 161dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 162dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 1635a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka public void setKeyboard(Keyboard keyboard, Key[] keys, float keyHysteresisDistance) { 16466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka if (keyboard == null || keys == null || keyHysteresisDistance < 0) 1656a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new IllegalArgumentException(); 16666e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka mKeyboard = keyboard; 1676a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeys = keys; 168eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); 1691a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // Mark that keyboard layout has been changed. 1701a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka mKeyboardLayoutHasBeenChanged = true; 1716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 173cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka public boolean isInSlidingKeyInput() { 174cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka return mIsInSlidingKeyInput; 175cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka } 176cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka 177c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka private boolean isValidKeyIndex(int keyIndex) { 178c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka return keyIndex >= 0 && keyIndex < mKeys.length; 179c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 180c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka 1816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public Key getKey(int keyIndex) { 182c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka return isValidKeyIndex(keyIndex) ? mKeys[keyIndex] : null; 1836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 185dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static boolean isModifierCode(int primaryCode) { 186571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka return primaryCode == Keyboard.CODE_SHIFT 187e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka || primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL; 18840a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka } 18940a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka 190dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private boolean isModifierInternal(int keyIndex) { 191dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final Key key = getKey(keyIndex); 192c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka return key == null ? false : isModifierCode(key.mCode); 193dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 194dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 1952aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka public boolean isModifier() { 1966e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return isModifierInternal(mKeyState.getKeyIndex()); 1972aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka } 1982aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka 1991d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka private boolean isOnModifierKey(int x, int y) { 2002aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka return isModifierInternal(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null)); 2012aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka } 2022aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka 203418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka public boolean isOnShiftKey(int x, int y) { 204418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka final Key key = getKey(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null)); 205c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka return key != null && key.mCode == Keyboard.CODE_SHIFT; 206418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka } 207418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka 2083a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka public boolean isSpaceKey(int keyIndex) { 2093a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka Key key = getKey(keyIndex); 210c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka return key != null && key.mCode == Keyboard.CODE_SPACE; 2113a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka } 2123a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka 2134189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka public void releaseKey() { 2144189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka updateKeyGraphics(NOT_A_KEY); 2154189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } 2164189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka 2174189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka private void updateKeyGraphics(int keyIndex) { 2186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka int oldKeyIndex = mPreviousKey; 2196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mPreviousKey = keyIndex; 2206a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (keyIndex != oldKeyIndex) { 221c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(oldKeyIndex)) { 2226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // if new key index is not a key, old key was just released inside of the key. 2236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka final boolean inside = (keyIndex == NOT_A_KEY); 2246a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeys[oldKeyIndex].onReleased(inside); 2256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy.invalidateKey(mKeys[oldKeyIndex]); 2266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 227c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 2286a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeys[keyIndex].onPressed(); 2296a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy.invalidateKey(mKeys[keyIndex]); 2306a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2316a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2326a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 234c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka public void setAlreadyProcessed() { 235c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka mKeyAlreadyProcessed = true; 236c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka } 237c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka 2381d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka private void checkAssertion(PointerTrackerQueue queue) { 2391d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (mHasDistinctMultitouch && queue == null) 2401d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka throw new RuntimeException( 2411d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka "PointerTrackerQueue must be passed on distinct multi touch device"); 2421d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (!mHasDistinctMultitouch && queue != null) 2431d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka throw new RuntimeException( 2441d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka "PointerTrackerQueue must be null on non-distinct multi touch device"); 2451d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 2461d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 2471d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onTouchEvent(int action, int x, int y, long eventTime, PointerTrackerQueue queue) { 248c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka switch (action) { 249c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_MOVE: 2501d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onMoveEvent(x, y, eventTime, queue); 251c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 252c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_DOWN: 253c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_POINTER_DOWN: 2541d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onDownEvent(x, y, eventTime, queue); 255c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 256c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_UP: 257c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_POINTER_UP: 2581d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onUpEvent(x, y, eventTime, queue); 259c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 260c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_CANCEL: 2611d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onCancelEvent(x, y, eventTime, queue); 262c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 263c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka } 264c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka } 265c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka 2661d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onDownEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { 2671d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (ENABLE_ASSERTION) checkAssertion(queue); 268dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_EVENT) 269dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka printTouchEvent("onDownEvent:", x, y, eventTime); 2701d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 271baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // TODO: up-to-down filter, if (down-up) is less than threshold, removeMessage(UP, this) in 272baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // Handler, and just ignore this down event. 273baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // TODO: down-to-up filter, just record down time. do not enqueue pointer now. 274baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 275baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // Naive up-to-down noise filter. 276baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final long deltaT = eventTime - mKeyState.getUpTime(); 277baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka if (deltaT < mTouchNoiseThresholdMillis) { 278baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final int dx = x - mKeyState.getLastX(); 279baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final int dy = y - mKeyState.getLastY(); 280baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final int distanceSquared = (dx * dx + dy * dy); 281baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka if (distanceSquared < mTouchNoiseThresholdDistanceSquared) { 282baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT 283baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka + " distance=" + distanceSquared); 284baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka setAlreadyProcessed(); 285baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka return; 286baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka } 287baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka } 288baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 2891d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (queue != null) { 2901d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (isOnModifierKey(x, y)) { 2911d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka // Before processing a down event of modifier key, all pointers already being 2921d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka // tracked should be released. 2931d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.releaseAllPointers(eventTime); 2941d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 2951d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.add(this); 2961d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 2971d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onDownEventInternal(x, y, eventTime); 2981d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 2991d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 300baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka private void onDownEventInternal(int x, int y, long eventTime) { 3016e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int keyIndex = mKeyState.onDownKey(x, y, eventTime); 30267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding 303dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // from modifier key, or 3) this pointer is on mini-keyboard. 30467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka mIsAllowedSlidingKeyInput = mConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex) 30567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka || mKeyDetector instanceof MiniKeyboardKeyDetector; 3061a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka mKeyboardLayoutHasBeenChanged = false; 307c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka mKeyAlreadyProcessed = false; 3086252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka mIsRepeatableKey = false; 309cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka mIsInSlidingKeyInput = false; 310dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 3111a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // This onPress call may have changed keyboard layout. Those cases are detected at 3121a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // {@link #setKeyboard}. In those cases, we should update keyIndex according to the new 3131a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // keyboard layout. 3141a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka if (callListenerOnPressAndCheckKeyboardLayoutChange(mKeys[keyIndex].mCode)) 3151a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka keyIndex = mKeyState.onDownKey(x, y, eventTime); 3166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 317c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 318571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka if (mKeys[keyIndex].mRepeatable) { 319c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka repeatKey(keyIndex); 3205e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); 3216252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka mIsRepeatableKey = true; 322c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 323adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka startLongPressTimer(keyIndex); 3246a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3254189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(keyIndex); 3266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3276a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 3281d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onMoveEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { 3291d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (ENABLE_ASSERTION) checkAssertion(queue); 330dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_MOVE_EVENT) 331dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka printTouchEvent("onMoveEvent:", x, y, eventTime); 332e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka if (mKeyAlreadyProcessed) 333e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka return; 334dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final PointerTrackerKeyState keyState = mKeyState; 335baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 336baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // TODO: down-to-up filter, if (eventTime-downTime) is less than threshold, just ignore 337baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // this move event. Otherwise fire {@link onDownEventInternal} and continue. 338baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 3391a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka int keyIndex = keyState.onMoveKey(x, y); 340c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka final Key oldKey = getKey(keyState.getKeyIndex()); 341c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 342c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (oldKey == null) { 3439e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // The pointer has been slid in to the new key, but the finger was not on any keys. 3449e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // In this case, we must call onPress() to notify that the new key is being pressed. 3451a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // This onPress call may have changed keyboard layout. Those cases are detected at 3461a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // {@link #setKeyboard}. In those cases, we should update keyIndex according to the 3471a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // new keyboard layout. 3481a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex).mCode)) 3491a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka keyIndex = keyState.onMoveKey(x, y); 3506e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x, y); 351adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka startLongPressTimer(keyIndex); 3526e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } else if (!isMinorMoveBounce(x, y, keyIndex)) { 3539e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // The pointer has been slid in to the new key from the previous key, we must call 3549e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // onRelease() first to notify that the previous key has been released, then call 3559e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // onPress() to notify that the new key is being pressed. 356cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka mIsInSlidingKeyInput = true; 357c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka callListenerOnRelease(oldKey.mCode); 3585ef421b58afa7bc58be40ed9331ce04998efbf56Tadashi G. Takaoka mHandler.cancelLongPressTimers(); 35967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka if (mIsAllowedSlidingKeyInput) { 3601a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // This onPress call may have changed keyboard layout. Those cases are detected 3611a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // at {@link #setKeyboard}. In those cases, we should update keyIndex according 3621a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // to the new keyboard layout. 3631a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex).mCode)) 3641a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka keyIndex = keyState.onMoveKey(x, y); 36567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x, y); 36667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka startLongPressTimer(keyIndex); 36767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka } else { 36867a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka setAlreadyProcessed(); 36967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); 37067a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka return; 37167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka } 3726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 374ef71c4aa4c11c50ff3d369d7abfe245aceedde97Tadashi G. Takaoka if (oldKey != null && !isMinorMoveBounce(x, y, keyIndex)) { 3759e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // The pointer has been slid out from the previous key, we must call onRelease() to 3769e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // notify that the previous key has been released. 377cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka mIsInSlidingKeyInput = true; 378c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka callListenerOnRelease(oldKey.mCode); 3795ef421b58afa7bc58be40ed9331ce04998efbf56Tadashi G. Takaoka mHandler.cancelLongPressTimers(); 38067a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka if (mIsAllowedSlidingKeyInput) { 38167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x ,y); 38267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka } else { 38367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka setAlreadyProcessed(); 38467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); 38567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka return; 38667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka } 38707221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka } 3886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3894189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(mKeyState.getKeyIndex()); 3906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 392baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // TODO: up-to-down filter, if delayed UP message is fired, invoke {@link onUpEventInternal}. 393baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 3941d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onUpEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { 3951d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (ENABLE_ASSERTION) checkAssertion(queue); 396dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_EVENT) 397dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka printTouchEvent("onUpEvent :", x, y, eventTime); 3981d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 399baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // TODO: up-to-down filter, just sendDelayedMessage(UP, this) to Handler. 400baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // TODO: down-to-up filter, if (eventTime-downTime) is less than threshold, just ignore 401baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // this up event. Otherwise fire {@link onDownEventInternal} and {@link onUpEventInternal}. 402baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 4031d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (queue != null) { 4041d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (isModifier()) { 4051d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka // Before processing an up event of modifier key, all pointers already being 4061d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka // tracked should be released. 4071d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.releaseAllPointersExcept(this, eventTime); 4081d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } else { 4091d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.releaseAllPointersOlderThan(this, eventTime); 4101d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 4111d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.remove(this); 4121d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 4131d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onUpEventInternal(x, y, eventTime); 4141d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 4151d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 4161d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onUpEventForRelease(int x, int y, long eventTime) { 4171d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onUpEventInternal(x, y, eventTime); 4181d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 4191d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 4201d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka private void onUpEventInternal(int pointX, int pointY, long eventTime) { 4211d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka int x = pointX; 4221d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka int y = pointY; 4236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelKeyTimers(); 4246a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelPopupPreview(); 425cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); 426cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka mIsInSlidingKeyInput = false; 4275ef421b58afa7bc58be40ed9331ce04998efbf56Tadashi G. Takaoka if (mKeyAlreadyProcessed) 4285ef421b58afa7bc58be40ed9331ce04998efbf56Tadashi G. Takaoka return; 429dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final PointerTrackerKeyState keyState = mKeyState; 430baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka int keyIndex = keyState.onUpKey(x, y, eventTime); 4316e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka if (isMinorMoveBounce(x, y, keyIndex)) { 4326e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka // Use previous fixed key index and coordinates. 433dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka keyIndex = keyState.getKeyIndex(); 434dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka x = keyState.getKeyX(); 435dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka y = keyState.getKeyY(); 4366a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4376252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka if (!mIsRepeatableKey) { 438c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka detectAndSendKey(keyIndex, x, y); 4396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4406e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 441c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) 4426a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy.invalidateKey(mKeys[keyIndex]); 4436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 4451d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { 4461d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (ENABLE_ASSERTION) checkAssertion(queue); 447dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_EVENT) 448dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka printTouchEvent("onCancelEvt:", x, y, eventTime); 4491d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 4501d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (queue != null) 4511d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.remove(this); 452baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka onCancelEventInternal(); 4531d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 4541d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 455baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka private void onCancelEventInternal() { 4566a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelKeyTimers(); 4576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelPopupPreview(); 4584189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); 459cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka mIsInSlidingKeyInput = false; 4606e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int keyIndex = mKeyState.getKeyIndex(); 461c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) 4626a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy.invalidateKey(mKeys[keyIndex]); 4636a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4646a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 4656a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public void repeatKey(int keyIndex) { 466c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka Key key = getKey(keyIndex); 467c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (key != null) { 468c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka detectAndSendKey(keyIndex, key.mX, key.mY); 469c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 4706a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 472400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public int getLastX() { 4736e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getLastX(); 474400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka } 475400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka 476400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public int getLastY() { 4776e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getLastY(); 478400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka } 479400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka 48007221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka public long getDownTime() { 4816e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getDownTime(); 48207221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka } 48307221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka 4846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // These package scope methods are only for debugging purpose. 4856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka /* package */ int getStartX() { 4866e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getStartX(); 4876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 4896a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka /* package */ int getStartY() { 4906e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getStartY(); 4916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 4936e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private boolean isMinorMoveBounce(int x, int y, int newKey) { 494eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka if (mKeys == null || mKeyHysteresisDistanceSquared < 0) 4956a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new IllegalStateException("keyboard and/or hysteresis not set"); 4966e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int curKey = mKeyState.getKeyIndex(); 4976a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (newKey == curKey) { 4986a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return true; 499c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } else if (isValidKeyIndex(curKey)) { 50059b7bd07301196ac333dabafb5dd80750fcd2987Tadashi G. Takaoka return mKeys[curKey].squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared; 5016a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 5026a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return false; 5036a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5056a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 5064189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka private void showKeyPreviewAndUpdateKeyGraphics(int keyIndex) { 5074189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka updateKeyGraphics(keyIndex); 508c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka // The modifier key, such as shift key, should not be shown as preview when multi-touch is 509c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka // supported. On the other hand, if multi-touch is not supported, the modifier key should 510c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka // be shown as preview. 511efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka if (mHasDistinctMultitouch && isModifier()) { 512efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka mProxy.showPreview(NOT_A_KEY, this); 513efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka } else { 51440a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka mProxy.showPreview(keyIndex, this); 515efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka } 5166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 51866e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka private void startLongPressTimer(int keyIndex) { 51966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka Key key = getKey(keyIndex); 520c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka if (key.mCode == Keyboard.CODE_SHIFT) { 5214189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this); 5222b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka } else if (key.mManualTemporaryUpperCaseCode != Keyboard.CODE_DUMMY 5232b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka && mKeyboard.isManualTemporaryUpperCase()) { 5242b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka // We need not start long press timer on the key which has manual temporary upper case 5252b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka // code defined and the keyboard is in manual temporary upper case mode. 5262b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka return; 5279e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka } else if (mKeyboardSwitcher.isInMomentaryAutoModeSwitchState()) { 5289e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // We use longer timeout for sliding finger input started from the symbols mode key. 5295797cefca6a50cba36d873ca78b861486b6726d5Tadashi G. Takaoka mHandler.startLongPressTimer(mLongPressKeyTimeout * 3, keyIndex, this); 5304189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } else { 5314189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); 5324189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } 53366e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka } 53466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 535c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka private void detectAndSendKey(int index, int x, int y) { 53683e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka final Key key = getKey(index); 53783e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka if (key == null) { 5388aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka callListenerOnCancelInput(); 539dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka return; 540dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 541dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (key.mOutputText != null) { 5428aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka callListenerOnTextInput(key.mOutputText); 543c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka callListenerOnRelease(key.mCode); 54483e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka } else { 545c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka int code = key.mCode; 546dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final int[] codes = mKeyDetector.newCodeArray(); 547dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); 54866e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 549dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // If keyboard is in manual temporary upper case state and key has manual temporary 550dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // shift code, alternate character code should be sent. 5512b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka if (mKeyboard.isManualTemporaryUpperCase() 5522b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka && key.mManualTemporaryUpperCaseCode != Keyboard.CODE_DUMMY) { 553dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka code = key.mManualTemporaryUpperCaseCode; 554dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka codes[0] = code; 555dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 55666e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 557dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // Swap the first and second values in the codes array if the primary code is not the 558dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // first value but the second value in the array. This happens when key debouncing is 559dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // in effect. 560dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (codes.length >= 2 && codes[0] != code && codes[1] == code) { 561dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka codes[1] = codes[0]; 562dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka codes[0] = code; 5636a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5648aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka callListenerOnCodeInput(code, codes, x, y); 565dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka callListenerOnRelease(code); 5666a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5676a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5686a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 5696a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public CharSequence getPreviewText(Key key) { 570c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka return key.mLabel; 5716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5723d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka 573dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private long mPreviousEventTime; 574dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 575dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private void printTouchEvent(String title, int x, int y, long eventTime) { 576dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); 577dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final Key key = getKey(keyIndex); 578c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka final String code = (key == null) ? "----" : keyCodePrintable(key.mCode); 579dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final long delta = eventTime - mPreviousEventTime; 580dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %3d(%s)", title, 581dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, keyIndex, code)); 582dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka mPreviousEventTime = eventTime; 583dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 584dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 585dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static String keyCodePrintable(int primaryCode) { 586dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final String modifier = isModifierCode(primaryCode) ? " modifier" : ""; 587dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka return String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode) + modifier; 5883d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka } 5896e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka} 590