PointerTracker.java revision d9786ce2e389c8c02af7773b53b5c44fe4fa0b0c
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; 20faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger; 215a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.latin.R; 226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 235e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaokaimport android.content.res.Resources; 2440a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaokaimport android.util.Log; 25c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaokaimport android.view.MotionEvent; 266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 27dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaokaimport java.util.Arrays; 28dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaokaimport java.util.List; 29dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 306a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaokapublic class PointerTracker { 31dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final String TAG = PointerTracker.class.getSimpleName(); 321d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka private static final boolean ENABLE_ASSERTION = false; 33dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final boolean DEBUG_EVENT = false; 34dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final boolean DEBUG_MOVE_EVENT = false; 35dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final boolean DEBUG_LISTENER = false; 36faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka private static boolean DEBUG_MODE = LatinImeLogger.sDBG; 3740a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka 386a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public interface UIProxy { 396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public void invalidateKey(Key key); 40d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka public void showKeyPreview(int keyIndex, PointerTracker tracker); 41d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka public void dismissKeyPreview(PointerTracker tracker); 425e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka public boolean hasDistinctMultitouch(); 43dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette public boolean isAccessibilityEnabled(); 446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 456a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 46c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka public final int mPointerId; 47c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka 486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // Timing constants 495e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka private final int mDelayBeforeKeyRepeatStart; 505e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka private final int mLongPressKeyTimeout; 514189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka private final int mLongPressShiftKeyTimeout; 526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 53d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka private final KeyboardView mKeyboardView; 546a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private final UIProxy mProxy; 556a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private final UIHandler mHandler; 562085d43daf44752deae1b6b00a14cb0f517d69cbTadashi G. Takaoka private final KeyDetector mKeyDetector; 57dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private KeyboardActionListener mListener = EMPTY_LISTENER; 589e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka private final KeyboardSwitcher mKeyboardSwitcher; 59c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka private final boolean mHasDistinctMultitouch; 6067a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka private final boolean mConfigSlidingKeyInputEnabled; 616a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 62baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka private final int mTouchNoiseThresholdMillis; 63baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka private final int mTouchNoiseThresholdDistanceSquared; 64baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 655a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka private Keyboard mKeyboard; 66dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka private List<Key> mKeys; 67eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka private int mKeyHysteresisDistanceSquared = -1; 68faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka private int mKeyQuarterWidthSquared; 696a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 70dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private final PointerTrackerKeyState mKeyState; 716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 72dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette // true if accessibility is enabled in the parent keyboard 73dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette private boolean mIsAccessibilityEnabled; 74dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette 751a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // true if keyboard layout has been changed. 761a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka private boolean mKeyboardLayoutHasBeenChanged; 771a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka 78c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka // true if event is already translated to a key action (long press or mini-keyboard) 79c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka private boolean mKeyAlreadyProcessed; 80c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka 816252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka // true if this pointer is repeatable key 826252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka private boolean mIsRepeatableKey; 836252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka 84cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka // true if this pointer is in sliding key input 85cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka private boolean mIsInSlidingKeyInput; 86cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka 8767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka // true if sliding key is allowed. 8867a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka private boolean mIsAllowedSlidingKeyInput; 8967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka 90996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka // ignore modifier key if true 91996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka private boolean mIgnoreModifierKey; 92996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka 93dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // Empty {@link KeyboardActionListener} 94dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() { 95dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 96e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka public void onPress(int primaryCode, boolean withSliding) {} 97dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 98e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka public void onRelease(int primaryCode, boolean withSliding) {} 99dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 1008aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {} 101dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 1028aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka public void onTextInput(CharSequence text) {} 103dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 1048aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka public void onCancelInput() {} 105dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka @Override 1068aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka public void onSwipeDown() {} 107dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka }; 1086e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 109d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka public PointerTracker(int id, KeyboardView keyboardView, UIHandler handler, 110d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka KeyDetector keyDetector, UIProxy proxy) { 1116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (proxy == null || handler == null || keyDetector == null) 1126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new NullPointerException(); 113c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka mPointerId = id; 114d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka mKeyboardView = keyboardView; 1156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy = proxy; 1166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler = handler; 1176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeyDetector = keyDetector; 1189e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka mKeyboardSwitcher = KeyboardSwitcher.getInstance(); 119dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka mKeyState = new PointerTrackerKeyState(keyDetector); 120dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette mIsAccessibilityEnabled = proxy.isAccessibilityEnabled(); 1215e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); 122d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka final Resources res = mKeyboardView.getResources(); 12367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled); 1245e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); 1255e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout); 1264189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout); 127baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka mTouchNoiseThresholdMillis = res.getInteger(R.integer.config_touch_noise_threshold_millis); 128baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final float touchNoiseThresholdDistance = res.getDimension( 129baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka R.dimen.config_touch_noise_threshold_distance); 130baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka mTouchNoiseThresholdDistanceSquared = (int)( 131baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka touchNoiseThresholdDistance * touchNoiseThresholdDistance); 1326a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 1345a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka public void setOnKeyboardActionListener(KeyboardActionListener listener) { 1356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mListener = listener; 1366a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 138dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette public void setAccessibilityEnabled(boolean accessibilityEnabled) { 139dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette mIsAccessibilityEnabled = accessibilityEnabled; 140dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette } 141dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette 1421a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // Returns true if keyboard has been changed by this callback. 143e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key, boolean withSliding) { 144996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode); 145dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 146996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka Log.d(TAG, "onPress : " + keyCodePrintable(key.mCode) + " sliding=" + withSliding 147996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka + " ignoreModifier=" + ignoreModifierKey); 148996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka if (ignoreModifierKey) 149996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka return false; 150690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka if (key.mEnabled) { 151e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka mListener.onPress(key.mCode, withSliding); 152690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; 153690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka mKeyboardLayoutHasBeenChanged = false; 154690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka return keyboardLayoutHasBeenChanged; 155690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka } 156690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka return false; 157dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 158dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 159690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka // Note that we need primaryCode argument because the keyboard may in shifted state and the 160690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka // primaryCode is different from {@link Key#mCode}. 161690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka private void callListenerOnCodeInput(Key key, int primaryCode, int[] keyCodes, int x, int y) { 162996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode); 163dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 1648aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka Log.d(TAG, "onCodeInput: " + keyCodePrintable(primaryCode) 165996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka + " codes="+ Arrays.toString(keyCodes) + " x=" + x + " y=" + y 166996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka + " ignoreModifier=" + ignoreModifierKey); 167996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka if (ignoreModifierKey) 168996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka return; 169690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka if (key.mEnabled) 170690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka mListener.onCodeInput(primaryCode, keyCodes, x, y); 171dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 172dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 173690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka private void callListenerOnTextInput(Key key) { 174dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 175690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka Log.d(TAG, "onTextInput: text=" + key.mOutputText); 176690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka if (key.mEnabled) 177690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka mListener.onTextInput(key.mOutputText); 178dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 179dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 180690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka // Note that we need primaryCode argument because the keyboard may in shifted state and the 181690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka // primaryCode is different from {@link Key#mCode}. 182e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) { 183996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode); 184dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 185996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka Log.d(TAG, "onRelease : " + keyCodePrintable(primaryCode) + " sliding=" 186996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka + withSliding + " ignoreModifier=" + ignoreModifierKey); 187996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka if (ignoreModifierKey) 188996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka return; 189690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka if (key.mEnabled) 190e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka mListener.onRelease(primaryCode, withSliding); 191dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 192dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 1938aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka private void callListenerOnCancelInput() { 194dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_LISTENER) 1958aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka Log.d(TAG, "onCancelInput"); 1968aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka mListener.onCancelInput(); 197dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 198dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 199dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka public void setKeyboard(Keyboard keyboard, float keyHysteresisDistance) { 200dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka if (keyboard == null || keyHysteresisDistance < 0) 2016a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new IllegalArgumentException(); 20266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka mKeyboard = keyboard; 203dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka mKeys = keyboard.getKeys(); 204eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); 205faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka final int keyQuarterWidth = keyboard.getKeyWidth() / 4; 206faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth; 2071a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // Mark that keyboard layout has been changed. 2081a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka mKeyboardLayoutHasBeenChanged = true; 2096a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2106a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 211cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka public boolean isInSlidingKeyInput() { 212cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka return mIsInSlidingKeyInput; 213cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka } 214cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka 215c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka private boolean isValidKeyIndex(int keyIndex) { 216dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka return keyIndex >= 0 && keyIndex < mKeys.size(); 217c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 218c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka 2196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public Key getKey(int keyIndex) { 220dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka return isValidKeyIndex(keyIndex) ? mKeys.get(keyIndex) : null; 2216a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 223dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static boolean isModifierCode(int primaryCode) { 224571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka return primaryCode == Keyboard.CODE_SHIFT 225e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka || primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL; 22640a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka } 22740a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka 228dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private boolean isModifierInternal(int keyIndex) { 229dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final Key key = getKey(keyIndex); 230c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka return key == null ? false : isModifierCode(key.mCode); 231dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 232dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 2332aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka public boolean isModifier() { 2346e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return isModifierInternal(mKeyState.getKeyIndex()); 2352aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka } 2362aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka 2371d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka private boolean isOnModifierKey(int x, int y) { 2382aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka return isModifierInternal(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null)); 2392aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka } 2402aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka 241418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka public boolean isOnShiftKey(int x, int y) { 242418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka final Key key = getKey(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null)); 243c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka return key != null && key.mCode == Keyboard.CODE_SHIFT; 244418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka } 245418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka 2463a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka public boolean isSpaceKey(int keyIndex) { 2473a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka Key key = getKey(keyIndex); 248c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka return key != null && key.mCode == Keyboard.CODE_SPACE; 2493a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka } 2503a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka 251d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka public void setReleasedKeyGraphics() { 252d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka setReleasedKeyGraphics(mKeyState.getKeyIndex()); 2534189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } 2544189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka 255d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka private void setReleasedKeyGraphics(int keyIndex) { 256d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka final Key key = getKey(keyIndex); 257d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka if (key != null) { 258d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka key.onReleased(); 259d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka mProxy.invalidateKey(key); 2606a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2616a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2626a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 263d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka private void setPressedKeyGraphics(int keyIndex) { 264d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka final Key key = getKey(keyIndex); 265d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka if (key != null && key.mEnabled) { 266d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka key.onPressed(); 267d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka mProxy.invalidateKey(key); 268d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka } 269c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka } 270c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka 2711d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka private void checkAssertion(PointerTrackerQueue queue) { 2721d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (mHasDistinctMultitouch && queue == null) 2731d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka throw new RuntimeException( 2741d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka "PointerTrackerQueue must be passed on distinct multi touch device"); 2751d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (!mHasDistinctMultitouch && queue != null) 2761d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka throw new RuntimeException( 2771d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka "PointerTrackerQueue must be null on non-distinct multi touch device"); 2781d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 2791d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 2801d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onTouchEvent(int action, int x, int y, long eventTime, PointerTrackerQueue queue) { 281c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka switch (action) { 282c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_MOVE: 2831d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onMoveEvent(x, y, eventTime, queue); 284c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 285c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_DOWN: 286c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_POINTER_DOWN: 2871d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onDownEvent(x, y, eventTime, queue); 288c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 289c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_UP: 290c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_POINTER_UP: 2911d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onUpEvent(x, y, eventTime, queue); 292c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 293c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_CANCEL: 2941d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onCancelEvent(x, y, eventTime, queue); 295c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 296c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka } 297c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka } 298c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka 2991d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onDownEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { 3001d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (ENABLE_ASSERTION) checkAssertion(queue); 301dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_EVENT) 302dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka printTouchEvent("onDownEvent:", x, y, eventTime); 3031d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 304baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka // Naive up-to-down noise filter. 305baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final long deltaT = eventTime - mKeyState.getUpTime(); 306baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka if (deltaT < mTouchNoiseThresholdMillis) { 307baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final int dx = x - mKeyState.getLastX(); 308baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final int dy = y - mKeyState.getLastY(); 309baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka final int distanceSquared = (dx * dx + dy * dy); 310baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka if (distanceSquared < mTouchNoiseThresholdDistanceSquared) { 311faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka if (DEBUG_MODE) 312faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT 313faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka + " distance=" + distanceSquared); 314d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka mKeyAlreadyProcessed = true; 315baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka return; 316baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka } 317baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka } 318baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 3191d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (queue != null) { 3201d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (isOnModifierKey(x, y)) { 3211d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka // Before processing a down event of modifier key, all pointers already being 3221d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka // tracked should be released. 3231d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.releaseAllPointers(eventTime); 3241d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 3251d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.add(this); 3261d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 3271d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka onDownEventInternal(x, y, eventTime); 3281d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 3291d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 330baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka private void onDownEventInternal(int x, int y, long eventTime) { 3316e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int keyIndex = mKeyState.onDownKey(x, y, eventTime); 33267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding 333dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette // from modifier key, 3) this pointer is on mini-keyboard, or 4) accessibility is enabled. 33467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka mIsAllowedSlidingKeyInput = mConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex) 335dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette || mKeyDetector instanceof MiniKeyboardKeyDetector 336dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette || mIsAccessibilityEnabled; 3371a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka mKeyboardLayoutHasBeenChanged = false; 338c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka mKeyAlreadyProcessed = false; 3396252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka mIsRepeatableKey = false; 340cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka mIsInSlidingKeyInput = false; 341996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka mIgnoreModifierKey = false; 342d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 3431a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // This onPress call may have changed keyboard layout. Those cases are detected at 3441a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // {@link #setKeyboard}. In those cases, we should update keyIndex according to the new 3451a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // keyboard layout. 346d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), false)) 3471a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka keyIndex = mKeyState.onDownKey(x, y, eventTime); 348996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka 349dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette // Accessibility disables key repeat because users may need to pause on a key to hear 350dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette // its spoken description. 351d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka final Key key = getKey(keyIndex); 352d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka if (key != null && key.mRepeatable && !mIsAccessibilityEnabled) { 353c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka repeatKey(keyIndex); 3545e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); 3556252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka mIsRepeatableKey = true; 356c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 357adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka startLongPressTimer(keyIndex); 358d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka showKeyPreview(keyIndex); 359d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka setPressedKeyGraphics(keyIndex); 3606a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3616a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3626a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 363996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka private void startSlidingKeyInput(Key key) { 364996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka if (!mIsInSlidingKeyInput) 365996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka mIgnoreModifierKey = isModifierCode(key.mCode); 366996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka mIsInSlidingKeyInput = true; 367996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka } 368996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka 3691d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onMoveEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { 3701d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (ENABLE_ASSERTION) checkAssertion(queue); 371dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_MOVE_EVENT) 372dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka printTouchEvent("onMoveEvent:", x, y, eventTime); 373e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka if (mKeyAlreadyProcessed) 374e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka return; 375dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final PointerTrackerKeyState keyState = mKeyState; 376baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka 377faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka final int lastX = keyState.getLastX(); 378faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka final int lastY = keyState.getLastY(); 379d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka final int oldKeyIndex = keyState.getKeyIndex(); 380d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka final Key oldKey = getKey(oldKeyIndex); 3811a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka int keyIndex = keyState.onMoveKey(x, y); 382c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 383c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (oldKey == null) { 3849e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // The pointer has been slid in to the new key, but the finger was not on any keys. 3859e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // In this case, we must call onPress() to notify that the new key is being pressed. 3861a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // This onPress call may have changed keyboard layout. Those cases are detected at 3871a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // {@link #setKeyboard}. In those cases, we should update keyIndex according to the 3881a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // new keyboard layout. 389e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), true)) 3901a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka keyIndex = keyState.onMoveKey(x, y); 3916e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x, y); 392adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka startLongPressTimer(keyIndex); 393d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka showKeyPreview(keyIndex); 394d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka setPressedKeyGraphics(keyIndex); 3956e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } else if (!isMinorMoveBounce(x, y, keyIndex)) { 3969e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // The pointer has been slid in to the new key from the previous key, we must call 3979e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // onRelease() first to notify that the previous key has been released, then call 3989e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // onPress() to notify that the new key is being pressed. 399d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka setReleasedKeyGraphics(oldKeyIndex); 400e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka callListenerOnRelease(oldKey, oldKey.mCode, true); 401996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka startSlidingKeyInput(oldKey); 4025ef421b58afa7bc58be40ed9331ce04998efbf56Tadashi G. Takaoka mHandler.cancelLongPressTimers(); 40367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka if (mIsAllowedSlidingKeyInput) { 4041a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // This onPress call may have changed keyboard layout. Those cases are detected 4051a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // at {@link #setKeyboard}. In those cases, we should update keyIndex according 4061a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka // to the new keyboard layout. 407e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), true)) 4081a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka keyIndex = keyState.onMoveKey(x, y); 40967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x, y); 41067a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka startLongPressTimer(keyIndex); 411d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka setPressedKeyGraphics(keyIndex); 412d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka showKeyPreview(keyIndex); 41367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka } else { 414faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka // HACK: On some devices, quick successive touches may be translated to sudden 415faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka // move by touch panel firmware. This hack detects the case and translates the 416faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka // move event to successive up and down events. 417faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka final int dx = x - lastX; 418faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka final int dy = y - lastY; 419faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka final int lastMoveSquared = dx * dx + dy * dy; 420faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka if (lastMoveSquared >= mKeyQuarterWidthSquared) { 421faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka if (DEBUG_MODE) 422faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka Log.w(TAG, String.format("onMoveEvent: sudden move is translated to " 423faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y)); 424d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka onUpEventInternal(lastX, lastY, eventTime, true); 425faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka onDownEventInternal(x, y, eventTime); 426faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka } else { 427d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka mKeyAlreadyProcessed = true; 428d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka dismissKeyPreview(); 429d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka setReleasedKeyGraphics(oldKeyIndex); 430faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka } 43167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka return; 43267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka } 4336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 435ef71c4aa4c11c50ff3d369d7abfe245aceedde97Tadashi G. Takaoka if (oldKey != null && !isMinorMoveBounce(x, y, keyIndex)) { 4369e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // The pointer has been slid out from the previous key, we must call onRelease() to 4379e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // notify that the previous key has been released. 438d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka setReleasedKeyGraphics(oldKeyIndex); 439e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka callListenerOnRelease(oldKey, oldKey.mCode, true); 440996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka startSlidingKeyInput(oldKey); 4415ef421b58afa7bc58be40ed9331ce04998efbf56Tadashi G. Takaoka mHandler.cancelLongPressTimers(); 44267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka if (mIsAllowedSlidingKeyInput) { 443d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x, y); 44467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka } else { 445d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka mKeyAlreadyProcessed = true; 446d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka dismissKeyPreview(); 44767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka return; 44867a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka } 44907221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka } 4506a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 4531d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onUpEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { 4541d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (ENABLE_ASSERTION) checkAssertion(queue); 455dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_EVENT) 456dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka printTouchEvent("onUpEvent :", x, y, eventTime); 4571d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 4581d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (queue != null) { 4591d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (isModifier()) { 4601d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka // Before processing an up event of modifier key, all pointers already being 4611d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka // tracked should be released. 4621d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.releaseAllPointersExcept(this, eventTime); 4631d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } else { 4641d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.releaseAllPointersOlderThan(this, eventTime); 4651d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 4661d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.remove(this); 4671d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 468d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka onUpEventInternal(x, y, eventTime, true); 4691d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 4701d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 471d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event. 472d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a 473d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka // "virtual" up event. 474d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka public void onPhantomUpEvent(int x, int y, long eventTime) { 475d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka onUpEventInternal(x, y, eventTime, false); 476d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka mKeyAlreadyProcessed = true; 4771d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 4781d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 479d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka private void onUpEventInternal(int x, int y, long eventTime, 480d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka boolean updateReleasedKeyGraphics) { 4816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelKeyTimers(); 482d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka mHandler.cancelShowKeyPreview(this); 483cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka mIsInSlidingKeyInput = false; 484dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final PointerTrackerKeyState keyState = mKeyState; 485d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka final int keyX, keyY; 486d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka if (!isMinorMoveBounce(x, y, keyState.onMoveKey(x, y))) { 487d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka keyX = x; 488d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka keyY = y; 489d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka } else { 490d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka // Use previous fixed key coordinates. 491d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka keyX = keyState.getKeyX(); 492d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka keyY = keyState.getKeyY(); 4936a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 494d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka final int keyIndex = keyState.onUpKey(keyX, keyY, eventTime); 495d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka dismissKeyPreview(); 496d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka if (updateReleasedKeyGraphics) 497d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka setReleasedKeyGraphics(keyIndex); 498d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka if (mKeyAlreadyProcessed) 499d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka return; 5006252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka if (!mIsRepeatableKey) { 501d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka detectAndSendKey(keyIndex, keyX, keyY); 5026a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5036a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 505d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka public void onLongPressed(PointerTrackerQueue queue) { 506d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka mKeyAlreadyProcessed = true; 507d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka queue.remove(this); 508d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka } 509d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka 5101d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { 5111d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (ENABLE_ASSERTION) checkAssertion(queue); 512dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (DEBUG_EVENT) 513dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka printTouchEvent("onCancelEvt:", x, y, eventTime); 5141d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 5151d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka if (queue != null) 5161d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka queue.remove(this); 517baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka onCancelEventInternal(); 5181d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka } 5191d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka 520baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka private void onCancelEventInternal() { 5216a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelKeyTimers(); 522d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka mHandler.cancelShowKeyPreview(this); 523d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka dismissKeyPreview(); 524d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka setReleasedKeyGraphics(mKeyState.getKeyIndex()); 525cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka mIsInSlidingKeyInput = false; 5266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5276a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 5286a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public void repeatKey(int keyIndex) { 529c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka Key key = getKey(keyIndex); 530c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (key != null) { 531c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka detectAndSendKey(keyIndex, key.mX, key.mY); 532c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 5336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 535400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public int getLastX() { 5366e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getLastX(); 537400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka } 538400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka 539400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public int getLastY() { 5406e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getLastY(); 541400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka } 542400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka 54307221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka public long getDownTime() { 5446e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getDownTime(); 54507221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka } 54607221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka 5476e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private boolean isMinorMoveBounce(int x, int y, int newKey) { 548eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka if (mKeys == null || mKeyHysteresisDistanceSquared < 0) 5496a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new IllegalStateException("keyboard and/or hysteresis not set"); 5506e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int curKey = mKeyState.getKeyIndex(); 5516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (newKey == curKey) { 5526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return true; 553c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } else if (isValidKeyIndex(curKey)) { 554dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka return mKeys.get(curKey).squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared; 5556a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 5566a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return false; 5576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5596a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 560d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka private void showKeyPreview(int keyIndex) { 561967d6073bfd5f3d0e21512754d78d2e87c958e27Tadashi G. Takaoka final Key key = getKey(keyIndex); 562967d6073bfd5f3d0e21512754d78d2e87c958e27Tadashi G. Takaoka if (key != null && !key.mEnabled) 563967d6073bfd5f3d0e21512754d78d2e87c958e27Tadashi G. Takaoka return; 564c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka // The modifier key, such as shift key, should not be shown as preview when multi-touch is 565c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka // supported. On the other hand, if multi-touch is not supported, the modifier key should 566dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette // be shown as preview. If accessibility is turned on, the modifier key should be shown as 567dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette // preview. 568d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled) 569d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka return; 570d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka mProxy.showKeyPreview(keyIndex, this); 571d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka } 572d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka 573d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka private void dismissKeyPreview() { 574d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka mProxy.dismissKeyPreview(this); 5756a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5766a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 57766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka private void startLongPressTimer(int keyIndex) { 578dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette // Accessibility disables long press because users are likely to need to pause on a key 579dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette // for an unspecified duration in order to hear the key's spoken description. 580dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette if (mIsAccessibilityEnabled) { 581dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette return; 582dcade18113286a132a2f5fb508fdba836470c3fbAlan Viverette } 58366e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka Key key = getKey(keyIndex); 584967d6073bfd5f3d0e21512754d78d2e87c958e27Tadashi G. Takaoka if (!key.mEnabled) 585967d6073bfd5f3d0e21512754d78d2e87c958e27Tadashi G. Takaoka return; 586c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka if (key.mCode == Keyboard.CODE_SHIFT) { 5874189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this); 5882b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka } else if (key.mManualTemporaryUpperCaseCode != Keyboard.CODE_DUMMY 5892b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka && mKeyboard.isManualTemporaryUpperCase()) { 5902b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka // We need not start long press timer on the key which has manual temporary upper case 5912b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka // code defined and the keyboard is in manual temporary upper case mode. 5922b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka return; 5939e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka } else if (mKeyboardSwitcher.isInMomentaryAutoModeSwitchState()) { 5949e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka // We use longer timeout for sliding finger input started from the symbols mode key. 5955797cefca6a50cba36d873ca78b861486b6726d5Tadashi G. Takaoka mHandler.startLongPressTimer(mLongPressKeyTimeout * 3, keyIndex, this); 5964189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } else { 5974189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); 5984189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } 59966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka } 60066e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 601c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka private void detectAndSendKey(int index, int x, int y) { 60283e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka final Key key = getKey(index); 60383e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka if (key == null) { 6048aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka callListenerOnCancelInput(); 605dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka return; 606dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 607dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (key.mOutputText != null) { 608690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka callListenerOnTextInput(key); 609e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka callListenerOnRelease(key, key.mCode, false); 61083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka } else { 611c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka int code = key.mCode; 612dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final int[] codes = mKeyDetector.newCodeArray(); 613dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); 61466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 615dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // If keyboard is in manual temporary upper case state and key has manual temporary 616dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // shift code, alternate character code should be sent. 6172b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka if (mKeyboard.isManualTemporaryUpperCase() 6182b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka && key.mManualTemporaryUpperCaseCode != Keyboard.CODE_DUMMY) { 619dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka code = key.mManualTemporaryUpperCaseCode; 620dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka codes[0] = code; 621dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 62266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 623dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // Swap the first and second values in the codes array if the primary code is not the 624dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // first value but the second value in the array. This happens when key debouncing is 625dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka // in effect. 626dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka if (codes.length >= 2 && codes[0] != code && codes[1] == code) { 627dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka codes[1] = codes[0]; 628dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka codes[0] = code; 6296a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 630690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka callListenerOnCodeInput(key, code, codes, x, y); 631e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka callListenerOnRelease(key, code, false); 6326a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 6336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 6346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 6356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public CharSequence getPreviewText(Key key) { 636c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka return key.mLabel; 6376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 6383d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka 639dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private long mPreviousEventTime; 640dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 641dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private void printTouchEvent(String title, int x, int y, long eventTime) { 642dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); 643dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final Key key = getKey(keyIndex); 644c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka final String code = (key == null) ? "----" : keyCodePrintable(key.mCode); 645dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final long delta = eventTime - mPreviousEventTime; 646dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %3d(%s)", title, 647dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, keyIndex, code)); 648dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka mPreviousEventTime = eventTime; 649dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka } 650dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka 651dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka private static String keyCodePrintable(int primaryCode) { 652dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka final String modifier = isModifierCode(primaryCode) ? " modifier" : ""; 653dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka return String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode) + modifier; 6543d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka } 6556e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka} 656