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