16a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka/*
28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2010 The Android Open Source Project
36a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
76a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi 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
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka */
166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
19536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaokaimport android.content.res.Resources;
205509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport android.content.res.TypedArray;
21e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaokaimport android.os.SystemClock;
22536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaokaimport android.util.DisplayMetrics;
23c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport android.util.Log;
248ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaokaimport android.view.MotionEvent;
25c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
26918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
27f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStroke;
2880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokeParams;
29c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
3005124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints.GestureStrokePreviewParams;
3172934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
32240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaokaimport com.android.inputmethod.latin.Constants;
33f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.latin.InputPointers;
34faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
355509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport com.android.inputmethod.latin.R;
369bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
37212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaokaimport com.android.inputmethod.latin.settings.Settings;
38e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils;
39e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CoordinateUtils;
40536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaokaimport com.android.inputmethod.latin.utils.ResourceUtils;
416b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
426a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
435c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
44dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
45a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class PointerTracker implements PointerTrackerQueue.Element {
46dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
47dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
48dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
49dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
50b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG || DEBUG_EVENT;
5140a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
520657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    /** True if {@link PointerTracker}s should handle gesture events. */
530657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    private static boolean sShouldHandleGesture = false;
548335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sMainDictionaryAvailable = false;
558335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByInputField = false;
568335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByUser = false;
57918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
67ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracke
68f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
69f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
70f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
71f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
72f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
73f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
74f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
75f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
76f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
77f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
78f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
79f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
80f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
81f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
82f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
83f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
84f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
85fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    public interface DrawingProxy {
866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
87e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
88d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
89547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        public void showSlidingKeyInputPreview(PointerTracker tracker);
9008d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        public void dismissSlidingKeyInputPreview();
91212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        public void showGestureTrail(PointerTracker tracker, boolean showsFloatingPreviewText);
926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
936a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
942321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
95d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        public void startTypingStateTimer(Key typedKey);
9673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
97ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay);
98212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker, int delay);
9998b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
1002a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public void startDoubleTapShiftKeyTimer();
1012a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public void cancelDoubleTapShiftKeyTimer();
1022a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public boolean isInDoubleTapShiftKeyTimeout();
1032321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
10472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        public void startUpdateBatchInputTimer(PointerTracker tracker);
105915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        public void cancelUpdateBatchInputTimer(PointerTracker tracker);
1062db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka        public void cancelAllUpdateBatchInputTimers();
10729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
10829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
10929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
110d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            public void startTypingStateTimer(Key typedKey) {}
11193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
11273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
11393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
114ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay) {}
11529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
116212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            public void startLongPressTimer(PointerTracker tracker, int delay) {}
117a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            @Override
11829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelLongPressTimer() {}
11929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
1202a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            public void startDoubleTapShiftKeyTimer() {}
1210ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
1222a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            public void cancelDoubleTapShiftKeyTimer() {}
123beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            @Override
1242a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            public boolean isInDoubleTapShiftKeyTimeout() { return false; }
1250ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
12629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelKeyTimers() {}
12772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            @Override
12872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            public void startUpdateBatchInputTimer(PointerTracker tracker) {}
12972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            @Override
130915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            public void cancelUpdateBatchInputTimer(PointerTracker tracker) {}
131915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            @Override
1322db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka            public void cancelAllUpdateBatchInputTimers() {}
13329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
1342321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    }
1352321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka
136a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    static final class PointerTrackerParams {
1375509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final boolean mSlidingKeyInputEnabled;
1385509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
139b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public final int mTouchNoiseThresholdDistance;
1403623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public final int mSuppressKeyPreviewAfterBatchInputDuration;
1418126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        public final int mKeyRepeatStartTimeout;
1428126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        public final int mKeyRepeatInterval;
143212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        public final int mLongPressShiftLockTimeout;
1445509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1455509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
1465509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1475509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private PointerTrackerParams() {
1485509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
1495509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = 0;
150b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = 0;
1513623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = 0;
1528126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            mKeyRepeatStartTimeout = 0;
1538126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            mKeyRepeatInterval = 0;
154212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mLongPressShiftLockTimeout = 0;
1555509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1565509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
15780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
1585509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
1595509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
1605509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
1615509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
162b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
1635509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
1643623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
1653623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
1668126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
1678126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
1688126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
1698126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatInterval, 0);
170212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mLongPressShiftLockTimeout = mainKeyboardViewAttr.getInt(
171212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                    R.styleable.MainKeyboardView_longPressShiftLockTimeout, 0);
1725509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1735509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    }
1745509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
175160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
1765509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    private static PointerTrackerParams sParams;
17780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    private static GestureStrokeParams sGestureStrokeParams;
17805124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka    private static GestureStrokePreviewParams sGesturePreviewParams;
179d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
180b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // Move this threshold to resource.
181b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // TODO: Device specific parameter would be better for device specific hack?
182b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
183536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    // This hack is applied to certain classes of tablets.
184536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    // See {@link #needsProximateBogusDownMoveUpEventHack(Resources)}.
185536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static boolean sNeedsProximateBogusDownMoveUpEventHack;
1865c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1875f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
18893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
1895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1920efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1930efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
194a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
195f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka    private KeyboardActionListener mListener = KeyboardActionListener.EMPTY_LISTENER;
196baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1975a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
198b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private int mPhantonSuddenMoveThreshold;
199b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
2006a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
2016c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
2026c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
2036c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sGestureFirstDownTime;
2043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private static TimeRecorder sTimeRecorder;
2056c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static final InputPointers sAggregratedPointers = new InputPointers(
2066c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            GestureStroke.DEFAULT_CAPACITY);
20758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
20858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
2099580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
210b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static final class BogusMoveEventDetector {
211b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Move these thresholds to resource.
212d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        // These thresholds' unit is a diagonal length of a key.
213d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
214d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
215b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
216b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mAccumulatedDistanceThreshold;
217b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mRadiusThreshold;
218b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
219b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Accumulated distance from actual and artificial down keys.
220b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int mAccumulatedDistanceFromDownKey;
221b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownX;
222b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownY;
223b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
224d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
225d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
226b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceThreshold = (int)(
227d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
228d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
229b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
230b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
231b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onActualDownEvent(final int x, final int y) {
232b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownX = x;
233b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownY = y;
234b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
235b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
236b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onDownKey() {
237b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey = 0;
238b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
239b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
240b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onMoveKey(final int distance) {
241b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey += distance;
242b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
243b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
244d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public boolean hasTraveledLongDistance(final int x, final int y) {
245d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dx = Math.abs(x - mActualDownX);
246d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dy = Math.abs(y - mActualDownY);
247d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // A bogus move event should be a horizontal movement. A vertical movement might be
248d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // a sloppy typing and should be ignored.
249d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
250b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
251b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
252b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int getDistanceFromDownEvent(final int x, final int y) {
253b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistance(x, y, mActualDownX, mActualDownY);
254b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
255b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
256b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isCloseToActualDownEvent(final int x, final int y) {
257b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
258b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
259b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
260b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2613623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    static final class TimeRecorder {
2623623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mSuppressKeyPreviewAfterBatchInputDuration;
2633623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mStaticTimeThresholdAfterFastTyping; // msec
2643623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastTypingTime;
2653623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastLetterTypingTime;
2663623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastBatchInputTime;
2673623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2683623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
2693623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                final GestureStrokeParams gestureStrokeParams) {
2703623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration =
2713623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
2723623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping =
2733623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
2743623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2753623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
276b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isInFastTyping(final long eventTime) {
277b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
278b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
279b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
280b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2817a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        private boolean wasLastInputTyping() {
2827a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return mLastTypingTime >= mLastBatchInputTime;
2833623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2843623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2853623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onCodeInput(final int code, final long eventTime) {
2867a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // Record the letter typing time when
2877a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 1. Letter keys are typed successively without any batch input in between.
2887a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 2. A letter key is typed within the threshold time since the last any key typing.
2897a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 3. A non-letter key is typed within the threshold time since the last letter key
2907a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // typing.
2917a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (Character.isLetter(code)) {
2927a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                if (wasLastInputTyping()
2937a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                        || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
2947a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2953623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            } else {
2973623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
2983623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    // This non-letter typing should be treated as a part of fast typing.
2997a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
3003623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
3013623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            }
3027a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastTypingTime = eventTime;
3033623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3053623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onEndBatchInput(final long eventTime) {
3067a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastBatchInputTime = eventTime;
3073623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3083623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3093623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public long getLastLetterTypingTime() {
3103623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            return mLastLetterTypingTime;
3113623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3123623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3133623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
3147a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return !wasLastInputTyping()
3153623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
3163623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3173623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    }
3183623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3198a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
3208a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
321547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    private int[] mDownCoordinates = CoordinateUtils.newInstance();
3228a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
3238a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
324e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
325e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
326e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
3278a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
3288a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
3298a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3308a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
3318a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
3328a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
3336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3341a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
3351a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
3361a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
33713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    // true if this pointer is no longer triggering any action because it has been canceled.
33813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    private boolean mIsTrackingForActionDisabled;
339c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
34035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    // the more keys panel currently being shown. equals null if no panel is active.
34135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private MoreKeysPanel mMoreKeysPanel;
3429ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
343212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    private static final int MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT = 3;
344f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input.
3455c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
346f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input from a modifier key,
347f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // so that further modifier keys should be ignored.
348f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    boolean mIsInSlidingKeyInputFromModifier;
349a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka    // if not a NOT_A_CODE, the key of this code is repeating
350a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka    private int mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
351cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
352f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if a sliding key input is allowed.
35367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
35467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
355c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
356f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
357536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static final int SMALL_TABLET_SMALLEST_WIDTH = 600; // dp
358536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static final int LARGE_TABLET_SMALLEST_WIDTH = 768; // dp
359536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka
360536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static boolean needsProximateBogusDownMoveUpEventHack(final Resources res) {
361536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // The proximate bogus down move up event hack is needed for a device such like,
362536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // 1) is large tablet, or 2) is small tablet and the screen density is less than hdpi.
363536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // Though it seems odd to use screen density as criteria of the quality of the touch
364536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // screen, the small table that has a less density screen than hdpi most likely has been
365536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // made with the touch screen that needs the hack.
366536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final int sw = res.getConfiguration().smallestScreenWidthDp;
367536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean isLargeTablet = (sw >= LARGE_TABLET_SMALLEST_WIDTH);
368536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean isSmallTablet =
369536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                (sw >= SMALL_TABLET_SMALLEST_WIDTH && sw < LARGE_TABLET_SMALLEST_WIDTH);
370536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final int densityDpi = res.getDisplayMetrics().densityDpi;
371536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean hasLowDensityScreen = (densityDpi < DisplayMetrics.DENSITY_HIGH);
372536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean needsTheHack = isLargeTablet || (isSmallTablet && hasLowDensityScreen);
373536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        if (DEBUG_MODE) {
374536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka            Log.d(TAG, "needsProximateBogusDownMoveUpEventHack=" + needsTheHack
375536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                    + " smallestScreenWidthDp=" + sw + " densityDpi=" + densityDpi);
376536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        }
377536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        return needsTheHack;
378536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    }
379536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka
380536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    public static void init(final Resources res) {
381536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
382536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                ResourceUtils.getDeviceOverrideValue(
383536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                        res, R.array.phantom_sudden_move_event_device_list));
384536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        sNeedsProximateBogusDownMoveUpEventHack = needsProximateBogusDownMoveUpEventHack(res);
3855509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = PointerTrackerParams.DEFAULT;
38680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = GestureStrokeParams.DEFAULT;
38705124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka        sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT;
3883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
389160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
39093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3915509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    public static void setParameters(final TypedArray mainKeyboardViewAttr) {
3925509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
39380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
39405124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka        sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
3953623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
3966a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3976a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3988335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
3998335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
4008335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
4018335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
4028335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
4038335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
4048335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
4058335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
406694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
4078335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
4088335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
4098335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
4108335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
411694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
4128335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
4138335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
414918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
415918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
416694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
417b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
4185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4195c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
4205c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
4215c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
4225c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
4235c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4245c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4255c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
4265c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4275c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
42993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.isAnyInSlidingKeyInput();
4305c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4315c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4325c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    public static void cancelAllPointerTrackers() {
4335c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        sPointerTrackerQueue.cancelAllPointerTrackers();
4345c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    }
4355c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka
436694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
437afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
438afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
439afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
4405c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
4415c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4425c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4435c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
444694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
445afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
446afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
447afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
4485c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
4495c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
4505c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
4515c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
452918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
4538335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
4548335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
4555c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4565c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
457dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
458afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
459afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
460afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
461e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
4625c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4635c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4645c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
465ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    public static void dismissAllMoreKeysPanels() {
466ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        final int trackersSize = sTrackers.size();
467ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        for (int i = 0; i < trackersSize; ++i) {
468ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            final PointerTracker tracker = sTrackers.get(i);
469ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (tracker.isShowingMoreKeysPanel()) {
470ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel.dismissMoreKeysPanel();
471ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel = null;
472ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
473ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
474ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    }
475ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
476694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private PointerTracker(final int id, final KeyEventHandler handler) {
477694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (handler == null) {
4785c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
479694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
4805c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
48180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
48205124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka                id, sGestureStrokeParams, sGesturePreviewParams);
483b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        setKeyEventHandler(handler);
484b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka    }
485b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka
486b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka    private void setKeyEventHandler(final KeyEventHandler handler) {
4875c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
4885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
4895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
4905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
4916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
4931a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
4948126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key,
495ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka            final int repeatCount) {
49613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // While gesture input is going on, this method should be a no-operation. But when gesture
49713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
49813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // are set to false. To keep this method is a no-operation,
49913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // <code>mIsTrackingForActionDisabled</code> should also be taken account of.
50013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
501eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
502eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
5038dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
504e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
5058126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onPress    : %s%s%s%s", mPointerId,
50658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    KeyDetector.printableCode(key),
50758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ignoreModifierKey ? " ignoreModifier" : "",
5088126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka                    key.isEnabled() ? "" : " disabled",
509ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka                    repeatCount > 0 ? " repeatCount=" + repeatCount : ""));
510e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
51193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
512996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
51393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
514e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
5157dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            mListener.onPressKey(key.getCode(), repeatCount, getActivePointerTrackerCount() == 1);
516690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
517690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
518d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
519690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
520690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
521690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
522dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
523dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
524690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
525690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
526694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
5273623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            final int y, final long eventTime) {
5288dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
5296bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
53029d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
5312013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
532240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            final String output = code == Constants.CODE_OUTPUT_TEXT
533240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    ? key.getOutputText() : Constants.printableCode(code);
53458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
53558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    output, ignoreModifierKey ? " ignoreModifier" : "",
53658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
5372013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
5389c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5399bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
5409bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
5419bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
542e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
543996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
544e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
5456bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
5466bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
5473623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            sTimeRecorder.onCodeInput(code, eventTime);
548240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
54929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                mListener.onTextInput(key.getOutputText());
550240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            } else if (code != Constants.CODE_UNSPECIFIED) {
551ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
5522013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
55393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
554dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
555dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
556f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may be in shifted state and the
557690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
558694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
559694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
56013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}.
56113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
562eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
563eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
5648dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
565e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
56658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
567240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    Constants.printableCode(primaryCode),
56858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
56958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ?  "": " disabled"));
570e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
5719c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5729bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
5739bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
5749bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
575e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
576996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
577e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
57893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
5792a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
58093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
581dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
582dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
58341016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    private void callListenerOnFinishSlidingInput() {
58441016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (DEBUG_LISTENER) {
58541016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId));
58641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
58741016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        mListener.onFinishSlidingInput();
58841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    }
58941016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka
5908aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
591694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
59258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
593694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
5949c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5959bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
5969bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5978aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
598dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
599dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
600694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
60158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
60258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
60358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return;
60458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
605a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
6065a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
607d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyWidth = mKeyboard.mMostCommonKeyWidth;
608d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyHeight = mKeyboard.mMostCommonKeyHeight;
609b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
6108a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
6118a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
6128a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
6138a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
6148a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
61544972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
6168a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
617b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
618d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
6195a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
6205a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
6210cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
622cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
623cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
624cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
625cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
626e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
627e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
628dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
629dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
6300cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
6312aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
632e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
6332aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
6342aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
635694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
636723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
6372aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
6382aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
639694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setReleasedKeyGraphics(final Key key) {
640d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
6416bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
642faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
643faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
644faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
6456bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
646faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
647faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
648faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
649faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
650faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
651faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
6522013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6532013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
654faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6552013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
656faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
65729d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
658faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
659faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
660faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
661faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
662faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
66329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
664faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
6652013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6662013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
6676a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6686a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6696a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
67029d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
67129d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!sShouldHandleGesture) return false;
6723623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
67329d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    }
67429d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka
67529d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void setPressedKeyGraphics(final Key key, final long eventTime) {
6766bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
6776bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
6786bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
6796bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
6806bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
6816bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
6826bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
6836bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
684faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
685faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
686faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
68729d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
688faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
689faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
690faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
691faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
692faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
693faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
694faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
695faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
6962013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6972013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
698faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6992013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
70073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
70129d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
702faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
703faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
704faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
705faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
706faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
70729d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
708faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
7092013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
7102013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
711d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
712c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
713c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
714694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updateReleaseKeyGraphics(final Key key) {
715faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
716faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
717faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
718faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
719694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updatePressKeyGraphics(final Key key) {
720faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
721faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
722faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
723faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
724c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
725c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return mGestureStrokeWithPreviewPoints;
7262f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
7272f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
728547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getLastCoordinates(final int[] outCoords) {
729547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(outCoords, mLastX, mLastY);
7308a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7318a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
7328a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
7338a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
7348a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7358a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
736547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getDownCoordinates(final int[] outCoords) {
737547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.copy(outCoords, mDownCoordinates);
738547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
739547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
740694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
7418a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
742547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(mDownCoordinates, x, y);
743b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onDownKey();
7448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
7458a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7468a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
747b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static int getDistance(final int x1, final int y1, final int x2, final int y2) {
748b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        return (int)Math.hypot(x1 - x2, y1 - y2);
749b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
750b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
751694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
752b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
7538a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
7548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
755723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
7568a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7578a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
758694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
7598a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
7608a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7618a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
762694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
763e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
7648a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
7658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
766e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
7678a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7688a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
7696c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int getActivePointerTrackerCount() {
77093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.size();
7716c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7726c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
773212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    private boolean isOldestTrackerInQueue() {
774ab334eb64669e909f0a401fddffa891962002602Tadashi G. Takaoka        return sPointerTrackerQueue.getOldestElement() == this;
77513ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    }
77613ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka
7771645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka    private void mayStartBatchInput(final Key key) {
778c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
7799c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            return;
7809c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        }
7817dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (key == null || !Character.isLetter(key.getCode())) {
7821645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            return;
7831645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        }
7849c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
78558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
7869580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7879c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        sInGesture = true;
78858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
78958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sAggregratedPointers.reset();
79058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionPointSize = 0;
79158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionTime = 0;
79258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mListener.onStartBatchInput();
793ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            dismissAllMoreKeysPanels();
79458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
795ad181915f78235bc09e88c85ed9df669801b8442Tadashi G. Takaoka        mTimerProxy.cancelLongPressTimer();
796212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        // A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
797212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        mDrawingProxy.showGestureTrail(
798212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
7996c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
8006c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
80172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    public void updateBatchInputByTimer(final long eventTime) {
80272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
80372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.duplicateLastPointWith(gestureTime);
80472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        updateBatchInput(eventTime);
80572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
80672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
807157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka    private void mayUpdateBatchInput(final long eventTime, final Key key) {
808157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka        if (key != null) {
80972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            updateBatchInput(eventTime);
81072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
81113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
8126f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
8136f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
814212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        // A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
815212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        mDrawingProxy.showGestureTrail(
816212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
81772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
81872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
81972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    private void updateBatchInput(final long eventTime) {
82072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
82172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
82272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            stroke.appendIncrementalBatchPoints(sAggregratedPointers);
82372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int size = sAggregratedPointers.getPointerSize();
82472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            if (size > sLastRecognitionPointSize
82572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
82672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                if (DEBUG_LISTENER) {
82772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
82872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                            size));
8296c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
83072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
83172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mListener.onUpdateBatchInput(sAggregratedPointers);
832b1e6eeea57794518997f449a4e4b947be4f74b0eJean Chalard                // The listener may change the size of the pointers (when auto-committing
833b1e6eeea57794518997f449a4e4b947be4f74b0eJean Chalard                // for example), so we need to get the size from the pointers again.
834b1e6eeea57794518997f449a4e4b947be4f74b0eJean Chalard                sLastRecognitionPointSize = sAggregratedPointers.getPointerSize();
835b1e6eeea57794518997f449a4e4b947be4f74b0eJean Chalard                sLastRecognitionTime = eventTime;
8366c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
8379580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
8389580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8399580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
84029d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void mayEndBatchInput(final long eventTime) {
8416c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        synchronized (sAggregratedPointers) {
842c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
8436c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
8446c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sInGesture = false;
8453623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                sTimeRecorder.onEndBatchInput(eventTime);
846b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka                mTimerProxy.cancelAllUpdateBatchInputTimers();
84713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                if (!mIsTrackingForActionDisabled) {
848b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    if (DEBUG_LISTENER) {
849b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                        Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
850b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                                mPointerId, sAggregratedPointers.getPointerSize()));
851b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    }
852b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    mListener.onEndBatchInput(sAggregratedPointers);
853b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                }
8546c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
8559580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
85613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
8576f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
8586f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
859212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        // A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
860212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        mDrawingProxy.showGestureTrail(
861212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
8629580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8639580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
8648e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    private void cancelBatchInput() {
8655c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllPointerTrackers();
8668353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        mIsDetectingGesture = false;
8677c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        if (!sInGesture) {
8687c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka            return;
8697c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        }
8708e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sInGesture = false;
8718e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        if (DEBUG_LISTENER) {
8728e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
8738e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        }
8748e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        mListener.onCancelBatchInput();
8758e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    }
8768e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka
877e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    public void processMotionEvent(final MotionEvent me, final KeyEventHandler handler) {
878e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int action = me.getActionMasked();
879e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final long eventTime = me.getEventTime();
880e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
881e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka            final int pointerCount = me.getPointerCount();
882e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka            for (int index = 0; index < pointerCount; index++) {
883e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                final int id = me.getPointerId(index);
884e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                final PointerTracker tracker = getPointerTracker(id, handler);
885e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                final int x = (int)me.getX(index);
886e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                final int y = (int)me.getY(index);
887e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                tracker.onMoveEvent(x, y, eventTime, me);
888e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka            }
889e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka            return;
890e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        }
891e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int index = me.getActionIndex();
892e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int x = (int)me.getX(index);
893e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int y = (int)me.getY(index);
8948ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
8958ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
8968ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
8978ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
8988ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8998ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
9008ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
9018ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
9028ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
9038ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
9048ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
9058ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
9068ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
9078ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
9088ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
909e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    private void onDownEvent(final int x, final int y, final long eventTime,
910694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
911694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
912dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
913694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
914b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        setKeyEventHandler(handler);
915baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
9168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
917160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
918b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distance = getDistance(x, y, mLastX, mLastY);
919b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distance < sParams.mTouchNoiseThresholdDistance) {
920faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
921b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.w(TAG, String.format("[%d] onDownEvent:"
922b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            + " ignore potential noise: time=%d distance=%d",
923b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, deltaT, distance));
9249c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
925b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
9269bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
92713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
928baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
929baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
930baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
931baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
932eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
933b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onActualDownEvent(x, y);
93493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (key != null && key.isModifier()) {
93593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // Before processing a down event of modifier key, all pointers already being
93693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // tracked should be released.
93793b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            sPointerTrackerQueue.releaseAllPointers(eventTime);
9381d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
93993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.add(this);
9401d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
9416c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!sShouldHandleGesture) {
9426c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
9436c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
9440938fb6913c10d32f0a870210fc208c7bbfe282cSatoshi Kataoka        // A gesture should start only from a non-modifier key. Note that the gesture detection is
9450938fb6913c10d32f0a870210fc208c7bbfe282cSatoshi Kataoka        // disabled when the key is repeating.
946b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
9470938fb6913c10d32f0a870210fc208c7bbfe282cSatoshi Kataoka                && key != null && !key.isModifier();
948b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
949b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
9506c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sGestureFirstDownTime = eventTime;
9519580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
95258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
9533623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    sTimeRecorder.getLastLetterTypingTime());
954eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
9551d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9561d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
95735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private boolean isShowingMoreKeysPanel() {
95835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        return (mMoreKeysPanel != null);
95935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
96035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
961694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
962e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
96367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
96432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
965160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
966c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
96732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
9681a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
96913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = false;
970f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
971e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
9721a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
973e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
9741a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
975ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
976e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
977e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
978996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
979e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
980e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
98129d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            setPressedKeyGraphics(key, eventTime);
9826a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
985694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startSlidingKeyInput(final Key key) {
986e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
987f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka            mIsInSlidingKeyInputFromModifier = key.isModifier();
988e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
989996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
990996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
991996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
992f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    private void resetSlidingKeyInput() {
993f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
994f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInputFromModifier = false;
99508d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        mDrawingProxy.dismissSlidingKeyInputPreview();
996f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    }
997f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka
9986c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
99902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
10006c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
10016c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (mIsDetectingGesture) {
1002b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            final int beforeLength = mGestureStrokeWithPreviewPoints.getLength();
1003b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard(
1004b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    x, y, gestureTime, isMajorEvent);
1005b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            if (mGestureStrokeWithPreviewPoints.getLength() > beforeLength) {
1006b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
1007b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            }
10088353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            // If the move event goes out from valid batch input area, cancel batch input.
1009b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            if (!onValidArea) {
10108e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka                cancelBatchInput();
1011b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                return;
1012b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            }
1013ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // If the MoreKeysPanel is showing then do not attempt to enter gesture mode. However,
1014ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // the gestured touch points are still being recorded in case the panel is dismissed.
1015ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (isShowingMoreKeysPanel()) {
1016ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                return;
1017ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
10181645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            mayStartBatchInput(key);
1019157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            if (sInGesture) {
1020157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mayUpdateBatchInput(eventTime, key);
10219580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
10229580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
10239580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
10249580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
1025e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    private void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
1026694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
1027dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
1028694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
102913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
1030e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
1031694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1032baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
10336c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture && me != null) {
1034eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
1035eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
1036eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
1037eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
1038eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
1039eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
1040eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
10416c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
104202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
1043eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
1044eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1045ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
1046ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
1047ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedX = mMoreKeysPanel.translateX(x);
1048ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedY = mMoreKeysPanel.translateY(y);
1049ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
1050ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            onMoveKey(x, y);
1051212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            if (mIsInSlidingKeyInputFromModifier) {
1052212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                mDrawingProxy.showSlidingKeyInputPreview(this);
1053212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            }
1054ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            return;
1055ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
10566c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
10576c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
10586c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
10593c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processSlidingKeyInput(final Key newKey, final int x, final int y,
10603c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime) {
10613c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // This onPress call may have changed keyboard layout. Those cases are detected
10623c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // at {@link #setKeyboard}. In those cases, we should update key according
10633c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // to the new keyboard layout.
10643c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        Key key = newKey;
1065ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
10663c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            key = onMoveKey(x, y);
10673c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
10683c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onMoveToNewKey(key, x, y);
106913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
107013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            return;
107113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        }
10723c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        startLongPressTimer(key);
10733c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        setPressedKeyGraphics(key, eventTime);
10743c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10753c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10763c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
10773c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10783c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10793c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
10803c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " phantom sudden move event (distance=%d) is translated to "
10813c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
10823c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    getDistance(x, y, lastX, lastY),
10837dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.getCode()),
10847dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                    x, y, Constants.printableCode(key.getCode())));
10853c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
10863c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // TODO: This should be moved to outside of this nested if-clause?
10879c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
10883c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
10893c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
109035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10913c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10923c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10933c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10943c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
10953c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10963c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10973c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(
10983c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
10993c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float radiusRatio =
11003c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
11013c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    / keyDiagonal;
11023c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
11033c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " bogus down-move-up event (raidus=%.2f key diagonal) is "
11043c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
11053c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mPointerId, radiusRatio,
11067dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.getCode()),
11077dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                    x, y, Constants.printableCode(key.getCode())));
11083c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
110935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
11103c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
11113c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
11123c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
1113831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    private void processSildeOutFromOldKey(final Key oldKey) {
1114831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        setReleasedKeyGraphics(oldKey);
11157dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        callListenerOnRelease(oldKey, oldKey.getCode(), true /* withSliding */);
1116831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        startSlidingKeyInput(oldKey);
1117831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1118831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    }
1119831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka
11203c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void slideFromOldKeyToNewKey(final Key key, final int x, final int y,
11218b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
11228b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid in to the new key from the previous key, we must call
11238b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onRelease() first to notify that the previous key has been released, then call
11248b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onPress() to notify that the new key is being pressed.
1125831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
11268b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        startRepeatKey(key);
11278b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
11283c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            processSlidingKeyInput(key, x, y, eventTime);
11292a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
11302a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive touches may be reported as a sudden move by
11312a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // touch panel firmware. This hack detects such cases and translates the move event to
11322a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // successive up and down events.
11332a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // TODO: Should find a way to balance gesture detection and this hack.
11342a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsPhantomSuddenMoveEventHack
11352a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
11362a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
11372a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
11382a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive proximate touches may be reported as a bogus
11392a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // down-move-up event by touch panel firmware. This hack detects such cases and breaks
11402a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // these events into separate up and down events.
11412a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime)
11422a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
11432a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
11442a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
11452a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: If there are currently multiple touches, register the key even if the finger
11462a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // slides off the key. This defends against noise from some touch panels when there are
11472a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // close multiple touches.
11482a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // Caveat: When in chording input mode with a modifier key, we don't use this hack.
114993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        else if (getActivePointerTrackerCount() > 1
11502a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
11512a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (DEBUG_MODE) {
11522a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                Log.w(TAG, String.format("[%d] onMoveEvent:"
11532a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                        + " detected sliding finger while multi touching", mPointerId));
11543c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            }
11552a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
115613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            cancelTrackingForAction();
11572a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
11582a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        } else {
11592a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (!mIsDetectingGesture) {
116013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
11618b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
11622a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
11638b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
11648b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
11658b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
11668b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    private void slideOutFromOldKey(final Key oldKey, final int x, final int y) {
11678b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid out from the previous key, we must call onRelease() to
11688b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // notify that the previous key has been released.
1169831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
11708b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
11718b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onMoveToNewKey(null, x, y);
11728b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        } else {
11738b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (!mIsDetectingGesture) {
117413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
11758b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
11768b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
11778b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
11788b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
11796c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
11808a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
11818a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
1182e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
11838b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        final Key newKey = onMoveKey(x, y);
1184eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11856c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture) {
11866c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
11878b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
11886c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
11896c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
11906c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
11916c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
11926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
1193eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1194eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11958b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (newKey != null) {
11963c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11978b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
11983c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            } else if (oldKey == null) {
1199831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
1200831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
1201831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                processSlidingKeyInput(newKey, x, y, eventTime);
1202c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
1203831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        } else { // newKey == null
12048b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
12058b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideOutFromOldKey(oldKey, x, y);
120607221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
12076a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1208212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        if (mIsInSlidingKeyInputFromModifier) {
1209212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mDrawingProxy.showSlidingKeyInputPreview(this);
1210212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
12116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1213e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    private void onUpEvent(final int x, final int y, final long eventTime) {
1214694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1215dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
1216694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
12171d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1218915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        mTimerProxy.cancelUpdateBatchInputTimer(this);
121993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (!sInGesture) {
122093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            if (mCurrentKey != null && mCurrentKey.isModifier()) {
122193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
122293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // tracked should be released.
122393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
122493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            } else {
122593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
12261d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
12271d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
122835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
122993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
12301d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12311d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1232d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
1233d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
1234d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
12350cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
1236694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
1237694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1238547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka            printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
1239694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
124035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
124135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
124235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
124335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(mLastX, mLastY, eventTime);
124413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
12451d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12461d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
124735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private void onUpEventInternal(final int x, final int y, final long eventTime) {
12482321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1249e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
125041016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        final boolean isInSlidingKeyInputFromModifier = mIsInSlidingKeyInputFromModifier;
1251f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
12526c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
12536c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
12546c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
1255a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka        final int currentRepeatingKeyCode = mCurrentRepeatingKeyCode;
1256a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka        mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
12575a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
12586c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        setReleasedKeyGraphics(currentKey);
125935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
126035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
126113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            if (!mIsTrackingForActionDisabled) {
126235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedX = mMoreKeysPanel.translateX(x);
126335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedY = mMoreKeysPanel.translateY(y);
126435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
126535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            }
126635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
126735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel = null;
126835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
12699ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
1270eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
12716c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
12726c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
12737dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.getCode(), true /* withSliding */);
1274eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
127529d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            mayEndBatchInput(eventTime);
1276eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
1277eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
127858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
127913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
1280d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
1281694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1282a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka        if (currentKey != null && currentKey.isRepeatable()
1283a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka                && (currentKey.getCode() == currentRepeatingKeyCode) && !isInSlidingKeyInput) {
1284e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka            return;
12856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1286e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
128741016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (isInSlidingKeyInputFromModifier) {
128841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            callListenerOnFinishSlidingInput();
128941016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
12906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
129233482a9b9ccf605c63fab7c9b8273a240bbc2035Tadashi G. Takaoka    public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
129335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        setReleasedKeyGraphics(mCurrentKey);
129433482a9b9ccf605c63fab7c9b8273a240bbc2035Tadashi G. Takaoka        final int translatedX = panel.translateX(mLastX);
129533482a9b9ccf605c63fab7c9b8273a240bbc2035Tadashi G. Takaoka        final int translatedY = panel.translateY(mLastY);
129633482a9b9ccf605c63fab7c9b8273a240bbc2035Tadashi G. Takaoka        panel.onDownEvent(translatedX, translatedY, mPointerId, SystemClock.uptimeMillis());
129735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel = panel;
12989ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
12999ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
1300b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    @Override
130113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    public void cancelTrackingForAction() {
13028353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
13038353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            return;
13048353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        }
130513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = true;
1306b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    }
1307b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
1308906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
1309547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        resetSlidingKeyInput();
131013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
1311e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
131293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
1313d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
1314d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
1315e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    private void onCancelEvent(final int x, final int y, final long eventTime) {
1316694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1317dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
1318694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
13191d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
13207c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        cancelBatchInput();
13215c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllPointerTrackers();
13228e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.releaseAllPointers(eventTime);
1323baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
13241d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
13251d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1326baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
13272321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1328e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
1329f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
1330ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
1331ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
1332ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel = null;
1333ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
13346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1336b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
1337b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final Key newKey) {
1338694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyDetector == null) {
1339a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
1340694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1341694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
13426a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1343e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1344a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1345a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (curKey == null /* && newKey != null */) {
1346a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1347a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1348a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        // Here curKey points to the different key from newKey.
1349a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
1350a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                mIsInSlidingKeyInputFromModifier);
1351a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
1352a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
1353a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1354a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
1355a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        / mKeyboard.mMostCommonKeyWidth;
1356a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1357a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
1358b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1359a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1360a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1361a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
1362a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && sTimeRecorder.isInFastTyping(eventTime)
1363a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
1364a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1365a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float keyDiagonal = (float)Math.hypot(
1366a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
1367a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float lengthFromDownRatio =
1368a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
1369a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1370a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        + " %.2f key diagonal from virtual down point",
1371a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mPointerId, lengthFromDownRatio));
1372b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1373e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
13746a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1375a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        return false;
13766a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13776a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1378694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
137943ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (sInGesture) return;
138043ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (key == null) return;
138143ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (!key.isLongPressEnabled()) return;
138243ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // Caveat: Please note that isLongPressEnabled() can be true even if the current key
138343ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // doesn't have its more keys. (e.g. spacebar, globe key)
138443ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // We always need to start the long press timer if the key has its more keys regardless of
138543ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // whether or not we are in the sliding input mode.
13867dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (mIsInSlidingKeyInput && key.getMoreKeys() == null) return;
1387212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        final int delay;
13887dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        switch (key.getCode()) {
1389212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        case Constants.CODE_SHIFT:
1390212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            delay = sParams.mLongPressShiftLockTimeout;
1391212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            break;
1392212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        default:
1393212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout;
1394212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            if (mIsInSlidingKeyInputFromModifier) {
1395212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                // We use longer timeout for sliding finger input started from the modifier key.
1396212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                delay = longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
1397212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            } else {
1398212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                delay = longpressTimeout;
1399212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            }
1400212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            break;
1401212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
1402212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        mTimerProxy.startLongPressTimer(this, delay);
140366e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
140466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
14053623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
140683e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
14078aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1408dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1409dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
141066e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
14117dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int code = key.getCode();
14123623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        callListenerOnCodeInput(key, code, x, y, eventTime);
141341016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        callListenerOnRelease(key, code, false /* withSliding */);
14146a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
14156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
14168126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
14178126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        if (sInGesture) return;
14188126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        if (key == null) return;
14198126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        if (!key.isRepeatable()) return;
14208126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        // Don't start key repeat when we are in sliding input mode.
14218126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        if (mIsInSlidingKeyInput) return;
1422ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        final int startRepeatCount = 1;
1423ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        mTimerProxy.startKeyRepeatTimer(this, startRepeatCount, sParams.mKeyRepeatStartTimeout);
14248126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka    }
14258126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka
1426ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka    public void onKeyRepeat(final int code, final int repeatCount) {
14278126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        final Key key = getKey();
14287dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (key == null || key.getCode() != code) {
1429a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka            mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
14308126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            return;
14318126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        }
1432a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka        mCurrentRepeatingKeyCode = code;
14330938fb6913c10d32f0a870210fc208c7bbfe282cSatoshi Kataoka        mIsDetectingGesture = false;
1434ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        final int nextRepeatCount = repeatCount + 1;
1435ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        mTimerProxy.startKeyRepeatTimer(this, nextRepeatCount, sParams.mKeyRepeatInterval);
1436ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount);
14378126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis());
14388126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka    }
14398126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka
1440694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1441694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1442723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1443e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
144458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
144513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                (mIsTrackingForActionDisabled ? "-" : " "), title, x, y, eventTime, code));
1446dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
14476e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1448