PointerTracker.java revision 212165b0b8308802a461a6a526d367ba67b5567a
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.
67f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
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();
97a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
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
114a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker) {}
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;
141212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        public final int mLongPressShiftLockTimeout;
1425509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1435509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
1445509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1455509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private PointerTrackerParams() {
1465509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
1475509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = 0;
148b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = 0;
1493623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = 0;
150212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mLongPressShiftLockTimeout = 0;
1515509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1525509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
15380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
1545509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
1555509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
1565509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
1575509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
158b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
1595509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
1603623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
1613623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
162212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mLongPressShiftLockTimeout = mainKeyboardViewAttr.getInt(
163212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                    R.styleable.MainKeyboardView_longPressShiftLockTimeout, 0);
1645509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1655509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    }
1665509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
167160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
1685509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    private static PointerTrackerParams sParams;
16980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    private static GestureStrokeParams sGestureStrokeParams;
17005124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka    private static GestureStrokePreviewParams sGesturePreviewParams;
171d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
172b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // Move this threshold to resource.
173b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // TODO: Device specific parameter would be better for device specific hack?
174b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
175536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    // This hack is applied to certain classes of tablets.
176536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    // See {@link #needsProximateBogusDownMoveUpEventHack(Resources)}.
177536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static boolean sNeedsProximateBogusDownMoveUpEventHack;
1785c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1795f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
18093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
1815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1840efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1850efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
186a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
187f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka    private KeyboardActionListener mListener = KeyboardActionListener.EMPTY_LISTENER;
188baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1895a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
190b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private int mPhantonSuddenMoveThreshold;
191b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
1926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1936c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
1946c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
1956c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sGestureFirstDownTime;
1963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private static TimeRecorder sTimeRecorder;
1976c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static final InputPointers sAggregratedPointers = new InputPointers(
1986c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            GestureStroke.DEFAULT_CAPACITY);
19958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
20058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
2019580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
202b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static final class BogusMoveEventDetector {
203b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Move these thresholds to resource.
204d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        // These thresholds' unit is a diagonal length of a key.
205d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
206d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
207b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
208b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mAccumulatedDistanceThreshold;
209b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mRadiusThreshold;
210b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
211b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Accumulated distance from actual and artificial down keys.
212b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int mAccumulatedDistanceFromDownKey;
213b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownX;
214b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownY;
215b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
216d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
217d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
218b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceThreshold = (int)(
219d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
220d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
221b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
222b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
223b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onActualDownEvent(final int x, final int y) {
224b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownX = x;
225b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownY = y;
226b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
227b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
228b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onDownKey() {
229b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey = 0;
230b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
231b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
232b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onMoveKey(final int distance) {
233b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey += distance;
234b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
235b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
236d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public boolean hasTraveledLongDistance(final int x, final int y) {
237d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dx = Math.abs(x - mActualDownX);
238d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dy = Math.abs(y - mActualDownY);
239d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // A bogus move event should be a horizontal movement. A vertical movement might be
240d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // a sloppy typing and should be ignored.
241d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
242b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
243b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
244b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int getDistanceFromDownEvent(final int x, final int y) {
245b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistance(x, y, mActualDownX, mActualDownY);
246b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
247b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
248b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isCloseToActualDownEvent(final int x, final int y) {
249b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
250b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
251b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
252b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2533623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    static final class TimeRecorder {
2543623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mSuppressKeyPreviewAfterBatchInputDuration;
2553623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mStaticTimeThresholdAfterFastTyping; // msec
2563623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastTypingTime;
2573623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastLetterTypingTime;
2583623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastBatchInputTime;
2593623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2603623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
2613623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                final GestureStrokeParams gestureStrokeParams) {
2623623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration =
2633623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
2643623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping =
2653623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
2663623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2673623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
268b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isInFastTyping(final long eventTime) {
269b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
270b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
271b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
272b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2737a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        private boolean wasLastInputTyping() {
2747a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return mLastTypingTime >= mLastBatchInputTime;
2753623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2763623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2773623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onCodeInput(final int code, final long eventTime) {
2787a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // Record the letter typing time when
2797a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 1. Letter keys are typed successively without any batch input in between.
2807a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 2. A letter key is typed within the threshold time since the last any key typing.
2817a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 3. A non-letter key is typed within the threshold time since the last letter key
2827a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // typing.
2837a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (Character.isLetter(code)) {
2847a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                if (wasLastInputTyping()
2857a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                        || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
2867a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2873623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            } else {
2893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
2903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    // This non-letter typing should be treated as a part of fast typing.
2917a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2923623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2933623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            }
2947a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastTypingTime = eventTime;
2953623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2973623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onEndBatchInput(final long eventTime) {
2987a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastBatchInputTime = eventTime;
2993623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3003623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3013623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public long getLastLetterTypingTime() {
3023623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            return mLastLetterTypingTime;
3033623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3053623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
3067a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return !wasLastInputTyping()
3073623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
3083623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3093623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    }
3103623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3118a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
3128a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
313547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    private int[] mDownCoordinates = CoordinateUtils.newInstance();
3148a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
3158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
316e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
317e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
318e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
3198a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
3208a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
3218a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3228a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
3238a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
3248a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
3256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3261a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
3271a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
3281a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
32913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    // true if this pointer is no longer triggering any action because it has been canceled.
33013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    private boolean mIsTrackingForActionDisabled;
331c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
33235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    // the more keys panel currently being shown. equals null if no panel is active.
33335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private MoreKeysPanel mMoreKeysPanel;
3349ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
335212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    private static final int MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT = 3;
336f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input.
3375c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
338f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input from a modifier key,
339f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // so that further modifier keys should be ignored.
340f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    boolean mIsInSlidingKeyInputFromModifier;
341cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
342f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if a sliding key input is allowed.
34367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
34467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
345c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
346f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
347536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static final int SMALL_TABLET_SMALLEST_WIDTH = 600; // dp
348536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static final int LARGE_TABLET_SMALLEST_WIDTH = 768; // dp
349536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka
350536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static boolean needsProximateBogusDownMoveUpEventHack(final Resources res) {
351536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // The proximate bogus down move up event hack is needed for a device such like,
352536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // 1) is large tablet, or 2) is small tablet and the screen density is less than hdpi.
353536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // Though it seems odd to use screen density as criteria of the quality of the touch
354536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // screen, the small table that has a less density screen than hdpi most likely has been
355536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // made with the touch screen that needs the hack.
356536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final int sw = res.getConfiguration().smallestScreenWidthDp;
357536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean isLargeTablet = (sw >= LARGE_TABLET_SMALLEST_WIDTH);
358536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean isSmallTablet =
359536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                (sw >= SMALL_TABLET_SMALLEST_WIDTH && sw < LARGE_TABLET_SMALLEST_WIDTH);
360536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final int densityDpi = res.getDisplayMetrics().densityDpi;
361536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean hasLowDensityScreen = (densityDpi < DisplayMetrics.DENSITY_HIGH);
362536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean needsTheHack = isLargeTablet || (isSmallTablet && hasLowDensityScreen);
363536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        if (DEBUG_MODE) {
364536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka            Log.d(TAG, "needsProximateBogusDownMoveUpEventHack=" + needsTheHack
365536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                    + " smallestScreenWidthDp=" + sw + " densityDpi=" + densityDpi);
366536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        }
367536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        return needsTheHack;
368536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    }
369536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka
370536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    public static void init(final Resources res) {
371536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
372536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                ResourceUtils.getDeviceOverrideValue(
373536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                        res, R.array.phantom_sudden_move_event_device_list));
374536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        sNeedsProximateBogusDownMoveUpEventHack = needsProximateBogusDownMoveUpEventHack(res);
3755509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = PointerTrackerParams.DEFAULT;
37680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = GestureStrokeParams.DEFAULT;
37705124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka        sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT;
3783623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
379160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
38093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3815509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    public static void setParameters(final TypedArray mainKeyboardViewAttr) {
3825509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
38380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
38405124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka        sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
3853623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
3866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3888335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
3898335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
3908335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
3918335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
3928335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
3938335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3948335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
3958335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
396694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
3978335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
3988335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
3998335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
4008335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
401694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
4028335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
4038335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
404918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
405918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
406694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
407b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
4085c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
4105c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
4115c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
4125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
4135c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4155c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
4165c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4175c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
41993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.isAnyInSlidingKeyInput();
4205c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4215c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4225c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    public static void cancelAllPointerTrackers() {
4235c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        sPointerTrackerQueue.cancelAllPointerTrackers();
4245c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    }
4255c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka
426694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
427afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
428afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
429afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
4305c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
4315c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4325c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
434694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
435afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
436afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
437afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
4385c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
4395c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
4405c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
4415c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
442918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
4438335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
4448335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
4455c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4465c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
447dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
448afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
449afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
450afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
451e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
4525c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4535c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4545c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
455ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    public static void dismissAllMoreKeysPanels() {
456ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        final int trackersSize = sTrackers.size();
457ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        for (int i = 0; i < trackersSize; ++i) {
458ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            final PointerTracker tracker = sTrackers.get(i);
459ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (tracker.isShowingMoreKeysPanel()) {
460ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel.dismissMoreKeysPanel();
461ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel = null;
462ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
463ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
464ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    }
465ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
466694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private PointerTracker(final int id, final KeyEventHandler handler) {
467694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (handler == null) {
4685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
469694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
4705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
47180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
47205124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka                id, sGestureStrokeParams, sGesturePreviewParams);
4735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
4745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
4755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
4765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
4776a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4786a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
4791a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
480694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
48113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // While gesture input is going on, this method should be a no-operation. But when gesture
48213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
48313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // are set to false. To keep this method is a no-operation,
48413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // <code>mIsTrackingForActionDisabled</code> should also be taken account of.
48513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
486eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
487eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
4888dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
489e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
49058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onPress    : %s%s%s", mPointerId,
49158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    KeyDetector.printableCode(key),
49258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ignoreModifierKey ? " ignoreModifier" : "",
49358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ? "" : " disabled"));
494e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
49593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
496996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
49793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
498e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
4996455172a707a1137eb15db8073774982db9dd1faTadashi G. Takaoka            mListener.onPressKey(key.mCode, false /* isRepeatKey */,
5006455172a707a1137eb15db8073774982db9dd1faTadashi G. Takaoka                    getActivePointerTrackerCount() == 1);
501690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
502690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
503d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
504690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
505690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
506690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
507dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
508dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
509690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
510690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
511694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
5123623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            final int y, final long eventTime) {
5138dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
5146bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
51529d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
5162013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
517240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            final String output = code == Constants.CODE_OUTPUT_TEXT
518240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    ? key.getOutputText() : Constants.printableCode(code);
51958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
52058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    output, ignoreModifierKey ? " ignoreModifier" : "",
52158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
5222013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
5239c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5249bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
5259bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
5269bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
527e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
528996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
529e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
5306bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
5316bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
5323623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            sTimeRecorder.onCodeInput(code, eventTime);
533240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
53429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                mListener.onTextInput(key.getOutputText());
535240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            } else if (code != Constants.CODE_UNSPECIFIED) {
536ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
5372013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
53893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
539dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
540dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
541f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may be in shifted state and the
542690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
543694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
544694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
54513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}.
54613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
547eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
548eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
5498dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
550e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
55158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
552240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    Constants.printableCode(primaryCode),
55358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
55458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ?  "": " disabled"));
555e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
5569c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5579bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
5589bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
5599bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
560e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
561996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
562e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
56393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
5642a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
56593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
566dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
567dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
56841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    private void callListenerOnFinishSlidingInput() {
56941016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (DEBUG_LISTENER) {
57041016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId));
57141016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
57241016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        mListener.onFinishSlidingInput();
57341016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    }
57441016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka
5758aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
576694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
57758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
578694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
5799c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5809bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
5819bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5828aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
583dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
584dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
585694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
58658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
58758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
58858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return;
58958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
590a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
5915a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
592d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyWidth = mKeyboard.mMostCommonKeyWidth;
593d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyHeight = mKeyboard.mMostCommonKeyHeight;
594b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
5958a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
5968a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
5978a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
5988a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
5998a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
60044972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
6018a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
602b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
603d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
6045a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
6055a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
6060cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
607cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
608cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
609cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
610cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
611e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
612e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
613dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
614dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
6150cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
6162aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
617e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
6182aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
6192aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
620694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
621723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
6222aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
6232aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
624694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setReleasedKeyGraphics(final Key key) {
625d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
6266bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
627faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
628faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
629faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
6306bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
631faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
632faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
633faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
634faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
635faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
636faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
6372013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6382013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
639faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6402013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
641faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
64229d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
643faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
644faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
645faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
646faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
647faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
64829d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
649faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
6502013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6512013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
6526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6536a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6546a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
65529d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
65629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!sShouldHandleGesture) return false;
6573623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
65829d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    }
65929d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka
66029d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void setPressedKeyGraphics(final Key key, final long eventTime) {
6616bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
6626bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
6636bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
6646bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
6656bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
6666bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
6676bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
6686bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
669faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
670faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
671faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
67229d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
673faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
674faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
675faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
676faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
677faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
678faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
679faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
680faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
6812013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6822013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
683faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6842013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
68573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
68629d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
687faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
688faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
689faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
690faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
691faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
69229d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
693faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
6942013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6952013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
696d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
697c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
698c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
699694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updateReleaseKeyGraphics(final Key key) {
700faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
701faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
702faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
703faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
704694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updatePressKeyGraphics(final Key key) {
705faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
706faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
707faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
708faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
709c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
710c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return mGestureStrokeWithPreviewPoints;
7112f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
7122f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
713547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getLastCoordinates(final int[] outCoords) {
714547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(outCoords, mLastX, mLastY);
7158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
7178a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
7188a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
7198a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7208a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
721547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getDownCoordinates(final int[] outCoords) {
722547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.copy(outCoords, mDownCoordinates);
723547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
724547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
725694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
7268a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
727547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(mDownCoordinates, x, y);
728b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onDownKey();
7298a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
7308a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7318a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
732b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static int getDistance(final int x1, final int y1, final int x2, final int y2) {
733b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        return (int)Math.hypot(x1 - x2, y1 - y2);
734b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
735b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
736694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
737b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
7388a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
7398a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
740723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
7418a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7428a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
743694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
7448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
7458a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7468a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
747694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
748e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
7498a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
7508a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
751e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
7528a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7538a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
7546c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int getActivePointerTrackerCount() {
75593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.size();
7566c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7576c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
758212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    private boolean isOldestTrackerInQueue() {
759ab334eb64669e909f0a401fddffa891962002602Tadashi G. Takaoka        return sPointerTrackerQueue.getOldestElement() == this;
76013ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    }
76113ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka
7621645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka    private void mayStartBatchInput(final Key key) {
763c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
7649c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            return;
7659c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        }
7661645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        if (key == null || !Character.isLetter(key.mCode)) {
7671645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            return;
7681645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        }
7699c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
77058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
7719580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7729c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        sInGesture = true;
77358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
77458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sAggregratedPointers.reset();
77558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionPointSize = 0;
77658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionTime = 0;
77758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mListener.onStartBatchInput();
778ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            dismissAllMoreKeysPanels();
77958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
780ad181915f78235bc09e88c85ed9df669801b8442Tadashi G. Takaoka        mTimerProxy.cancelLongPressTimer();
781212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        // A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
782212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        mDrawingProxy.showGestureTrail(
783212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
7846c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7856c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
78672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    public void updateBatchInputByTimer(final long eventTime) {
78772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
78872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.duplicateLastPointWith(gestureTime);
78972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        updateBatchInput(eventTime);
79072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
79172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
792157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka    private void mayUpdateBatchInput(final long eventTime, final Key key) {
793157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka        if (key != null) {
79472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            updateBatchInput(eventTime);
79572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
79613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
7976f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
7986f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
799212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        // A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
800212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        mDrawingProxy.showGestureTrail(
801212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
80272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
80372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
80472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    private void updateBatchInput(final long eventTime) {
80572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
80672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
80772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            stroke.appendIncrementalBatchPoints(sAggregratedPointers);
80872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int size = sAggregratedPointers.getPointerSize();
80972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            if (size > sLastRecognitionPointSize
81072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
81172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                sLastRecognitionPointSize = size;
81272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                sLastRecognitionTime = eventTime;
81372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                if (DEBUG_LISTENER) {
81472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
81572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                            size));
8166c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
81772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
81872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mListener.onUpdateBatchInput(sAggregratedPointers);
8196c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
8209580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
8219580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8229580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
82329d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void mayEndBatchInput(final long eventTime) {
8246c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        synchronized (sAggregratedPointers) {
825c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
8266c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
8276c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sInGesture = false;
8283623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                sTimeRecorder.onEndBatchInput(eventTime);
829b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka                mTimerProxy.cancelAllUpdateBatchInputTimers();
83013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                if (!mIsTrackingForActionDisabled) {
831b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    if (DEBUG_LISTENER) {
832b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                        Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
833b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                                mPointerId, sAggregratedPointers.getPointerSize()));
834b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    }
835b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    mListener.onEndBatchInput(sAggregratedPointers);
836b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                }
8376c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
8389580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
83913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
8406f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
8416f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
842212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        // A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
843212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        mDrawingProxy.showGestureTrail(
844212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
8459580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8469580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
8478e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    private void cancelBatchInput() {
8485c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllPointerTrackers();
8498353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        mIsDetectingGesture = false;
8507c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        if (!sInGesture) {
8517c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka            return;
8527c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        }
8538e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sInGesture = false;
8548e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        if (DEBUG_LISTENER) {
8558e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
8568e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        }
8578e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        mListener.onCancelBatchInput();
8588e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    }
8598e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka
860694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
861694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
8628ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
8638ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
8648ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
8658ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
8668ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8678ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
8688ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
8698ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
8708ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8718ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
8723314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
8738ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8748ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
8758ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
8768ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8778ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
8788ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
8798ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
880694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long eventTime,
881694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
882694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
883dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
884694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
885f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
88663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
887f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
888f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
889baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
8908a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
891160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
892b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distance = getDistance(x, y, mLastX, mLastY);
893b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distance < sParams.mTouchNoiseThresholdDistance) {
894faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
895b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.w(TAG, String.format("[%d] onDownEvent:"
896b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            + " ignore potential noise: time=%d distance=%d",
897b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, deltaT, distance));
8989c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
899b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
9009bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
90113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
902baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
903baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
904baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
905baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
906eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
907b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onActualDownEvent(x, y);
90893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (key != null && key.isModifier()) {
90993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // Before processing a down event of modifier key, all pointers already being
91093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // tracked should be released.
91193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            sPointerTrackerQueue.releaseAllPointers(eventTime);
9121d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
91393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.add(this);
9141d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
9156c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!sShouldHandleGesture) {
9166c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
9176c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
9184df6549c1a5ae2cdc2cdfafdad1ec2a75881134cTadashi G. Takaoka        // A gesture should start only from a non-modifier key.
919b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
920ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                && key != null && !key.isModifier();
921b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
922b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
9236c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sGestureFirstDownTime = eventTime;
9249580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
92558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
9263623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    sTimeRecorder.getLastLetterTypingTime());
927eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
9281d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9291d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
93035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private boolean isShowingMoreKeysPanel() {
93135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        return (mMoreKeysPanel != null);
93235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
93335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
934694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
935e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
93667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
93732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
938160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
939c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
94032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
9411a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
94213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = false;
943f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
944e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
9451a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
946e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
9471a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
9482a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
949e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
950e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
951996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
952e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
953e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
95429d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            setPressedKeyGraphics(key, eventTime);
9556a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9566a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
958694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startSlidingKeyInput(final Key key) {
959e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
960f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka            mIsInSlidingKeyInputFromModifier = key.isModifier();
961e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
962996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
963996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
964996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
965f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    private void resetSlidingKeyInput() {
966f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
967f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInputFromModifier = false;
96808d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        mDrawingProxy.dismissSlidingKeyInputPreview();
969f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    }
970f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka
9716c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
97202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
9736c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
9746c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (mIsDetectingGesture) {
975b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            final int beforeLength = mGestureStrokeWithPreviewPoints.getLength();
976b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard(
977b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    x, y, gestureTime, isMajorEvent);
978b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            if (mGestureStrokeWithPreviewPoints.getLength() > beforeLength) {
979b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
980b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            }
9818353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            // If the move event goes out from valid batch input area, cancel batch input.
982b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            if (!onValidArea) {
9838e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka                cancelBatchInput();
984b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                return;
985b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            }
986ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // If the MoreKeysPanel is showing then do not attempt to enter gesture mode. However,
987ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // the gestured touch points are still being recorded in case the panel is dismissed.
988ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (isShowingMoreKeysPanel()) {
989ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                return;
990ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
9911645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            mayStartBatchInput(key);
992157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            if (sInGesture) {
993157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mayUpdateBatchInput(eventTime, key);
9949580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
9959580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
9969580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
9979580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
998694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
999694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
1000dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
1001694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
100213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
1003e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
1004694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1005baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
10066c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture && me != null) {
1007eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
1008eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
1009eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
1010eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
1011eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
1012eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
1013eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
10146c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
101502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
1016eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
1017eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1018ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
1019ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
1020ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedX = mMoreKeysPanel.translateX(x);
1021ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedY = mMoreKeysPanel.translateY(y);
1022ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
1023ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            onMoveKey(x, y);
1024212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            if (mIsInSlidingKeyInputFromModifier) {
1025212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                mDrawingProxy.showSlidingKeyInputPreview(this);
1026212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            }
1027ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            return;
1028ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
10296c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
10306c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
10316c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
10323c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processSlidingKeyInput(final Key newKey, final int x, final int y,
10333c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime) {
10343c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // This onPress call may have changed keyboard layout. Those cases are detected
10353c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // at {@link #setKeyboard}. In those cases, we should update key according
10363c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // to the new keyboard layout.
10373c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        Key key = newKey;
10383c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
10393c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            key = onMoveKey(x, y);
10403c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
10413c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onMoveToNewKey(key, x, y);
104213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
104313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            return;
104413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        }
10453c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        startLongPressTimer(key);
10463c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        setPressedKeyGraphics(key, eventTime);
10473c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10483c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10493c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
10503c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10513c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10523c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
10533c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " phantom sudden move event (distance=%d) is translated to "
10543c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
10553c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    getDistance(x, y, lastX, lastY),
10563c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
10573c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
10583c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
10593c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // TODO: This should be moved to outside of this nested if-clause?
10609c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
10613c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
10623c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
106335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10643c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10653c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10663c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10673c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
10683c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10693c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10703c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(
10713c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
10723c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float radiusRatio =
10733c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
10743c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    / keyDiagonal;
10753c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
10763c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " bogus down-move-up event (raidus=%.2f key diagonal) is "
10773c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
10783c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mPointerId, radiusRatio,
10793c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
10803c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
10813c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
108235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10833c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10843c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10853c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
1086831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    private void processSildeOutFromOldKey(final Key oldKey) {
1087831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        setReleasedKeyGraphics(oldKey);
108841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        callListenerOnRelease(oldKey, oldKey.mCode, true /* withSliding */);
1089831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        startSlidingKeyInput(oldKey);
1090831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1091831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    }
1092831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka
10933c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void slideFromOldKeyToNewKey(final Key key, final int x, final int y,
10948b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10958b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid in to the new key from the previous key, we must call
10968b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onRelease() first to notify that the previous key has been released, then call
10978b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onPress() to notify that the new key is being pressed.
1098831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
10998b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        startRepeatKey(key);
11008b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
11013c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            processSlidingKeyInput(key, x, y, eventTime);
11022a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
11032a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive touches may be reported as a sudden move by
11042a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // touch panel firmware. This hack detects such cases and translates the move event to
11052a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // successive up and down events.
11062a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // TODO: Should find a way to balance gesture detection and this hack.
11072a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsPhantomSuddenMoveEventHack
11082a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
11092a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
11102a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
11112a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive proximate touches may be reported as a bogus
11122a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // down-move-up event by touch panel firmware. This hack detects such cases and breaks
11132a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // these events into separate up and down events.
11142a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime)
11152a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
11162a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
11172a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
11182a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: If there are currently multiple touches, register the key even if the finger
11192a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // slides off the key. This defends against noise from some touch panels when there are
11202a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // close multiple touches.
11212a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // Caveat: When in chording input mode with a modifier key, we don't use this hack.
112293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        else if (getActivePointerTrackerCount() > 1
11232a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
11242a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (DEBUG_MODE) {
11252a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                Log.w(TAG, String.format("[%d] onMoveEvent:"
11262a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                        + " detected sliding finger while multi touching", mPointerId));
11273c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            }
11282a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
112913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            cancelTrackingForAction();
11302a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
11312a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        } else {
11322a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (!mIsDetectingGesture) {
113313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
11348b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
11352a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
11368b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
11378b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
11388b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
11398b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    private void slideOutFromOldKey(final Key oldKey, final int x, final int y) {
11408b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid out from the previous key, we must call onRelease() to
11418b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // notify that the previous key has been released.
1142831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
11438b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
11448b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onMoveToNewKey(null, x, y);
11458b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        } else {
11468b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (!mIsDetectingGesture) {
114713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
11488b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
11498b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
11508b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
11518b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
11526c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
11538a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
11548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
1155e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
11568b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        final Key newKey = onMoveKey(x, y);
1157eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11586c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture) {
11596c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
11608b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
11616c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
11626c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
11636c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
11646c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
11656c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
1166eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1167eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11688b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (newKey != null) {
11693c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11708b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
11713c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            } else if (oldKey == null) {
1172831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
1173831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
1174831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                processSlidingKeyInput(newKey, x, y, eventTime);
1175c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
1176831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        } else { // newKey == null
11778b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11788b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideOutFromOldKey(oldKey, x, y);
117907221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
11806a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1181212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        if (mIsInSlidingKeyInputFromModifier) {
1182212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mDrawingProxy.showSlidingKeyInputPreview(this);
1183212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
11846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1186694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onUpEvent(final int x, final int y, final long eventTime) {
1187694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1188dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
1189694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
11901d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1191915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        mTimerProxy.cancelUpdateBatchInputTimer(this);
119293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (!sInGesture) {
119393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            if (mCurrentKey != null && mCurrentKey.isModifier()) {
119493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
119593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // tracked should be released.
119693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
119793b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            } else {
119893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
11991d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
12001d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
120135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
120293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
12031d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12041d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1205d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
1206d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
1207d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
12080cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
1209694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
1210694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1211547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka            printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
1212694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
121335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
121435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
121535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
121635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(mLastX, mLastY, eventTime);
121713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
12181d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12191d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
122035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private void onUpEventInternal(final int x, final int y, final long eventTime) {
12212321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1222e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
122341016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        final boolean isInSlidingKeyInputFromModifier = mIsInSlidingKeyInputFromModifier;
1224f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
12256c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
12266c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
12276c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
12285a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
12296c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        setReleasedKeyGraphics(currentKey);
123035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
123135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
123213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            if (!mIsTrackingForActionDisabled) {
123335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedX = mMoreKeysPanel.translateX(x);
123435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedY = mMoreKeysPanel.translateY(y);
123535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
123635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            }
123735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
123835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel = null;
123935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
12409ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
1241eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
12426c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
12436c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
124441016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.mCode, true /* withSliding */);
1245eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
124629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            mayEndBatchInput(eventTime);
1247eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
1248eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
124958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
125013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
1251d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
1252694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1253e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (currentKey != null && currentKey.isRepeatable() && !isInSlidingKeyInput) {
1254e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka            // Repeatable key has been registered in {@link #onDownEventInternal(int,int,long)}.
1255e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka            return;
12566a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1257e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
125841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (isInSlidingKeyInputFromModifier) {
125941016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            callListenerOnFinishSlidingInput();
126041016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
12616a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12626a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
126335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    public void onShowMoreKeysPanel(final int translatedX, final int translatedY,
126435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final MoreKeysPanel panel) {
126535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        setReleasedKeyGraphics(mCurrentKey);
126635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final long eventTime = SystemClock.uptimeMillis();
126735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel = panel;
126835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, eventTime);
12699ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
12709ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
1271b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    @Override
127213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    public void cancelTrackingForAction() {
12738353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
12748353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            return;
12758353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        }
127613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = true;
1277b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    }
1278b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
1279906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
1280547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        resetSlidingKeyInput();
128113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
1282e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
128393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
1284d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
1285d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
1286694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onCancelEvent(final int x, final int y, final long eventTime) {
1287694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1288dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
1289694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
12901d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
12917c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        cancelBatchInput();
12925c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllPointerTrackers();
12938e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.releaseAllPointers(eventTime);
1294baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
12951d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12961d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1297baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
12982321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1299e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
1300f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
1301ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
1302ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
1303ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel = null;
1304ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
13056a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13066a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1307694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
1308ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (sInGesture) return;
1309ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (key == null) return;
1310ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (!key.isRepeatable()) return;
1311ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        // Don't start key repeat when we are in sliding input mode.
1312e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (mIsInSlidingKeyInput) return;
1313009488eaaf25f04ca841f7741dc8b270f7da9000Tadashi G. Takaoka        detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
1314f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        mTimerProxy.startKeyRepeatTimer(this);
13156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1317b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
1318b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final Key newKey) {
1319694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyDetector == null) {
1320a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
1321694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1322694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
13236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1324e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1325a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1326a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (curKey == null /* && newKey != null */) {
1327a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1328a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1329a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        // Here curKey points to the different key from newKey.
1330a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
1331a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                mIsInSlidingKeyInputFromModifier);
1332a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
1333a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
1334a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1335a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
1336a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        / mKeyboard.mMostCommonKeyWidth;
1337a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1338a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
1339b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1340a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1341a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1342a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
1343a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && sTimeRecorder.isInFastTyping(eventTime)
1344a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
1345a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1346a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float keyDiagonal = (float)Math.hypot(
1347a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
1348a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float lengthFromDownRatio =
1349a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
1350a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1351a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        + " %.2f key diagonal from virtual down point",
1352a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mPointerId, lengthFromDownRatio));
1353b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1354e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
13556a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1356a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        return false;
13576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1359694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
136043ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (sInGesture) return;
136143ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (key == null) return;
136243ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (!key.isLongPressEnabled()) return;
136343ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // Caveat: Please note that isLongPressEnabled() can be true even if the current key
136443ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // doesn't have its more keys. (e.g. spacebar, globe key)
136543ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // We always need to start the long press timer if the key has its more keys regardless of
136643ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // whether or not we are in the sliding input mode.
1367e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (mIsInSlidingKeyInput && key.mMoreKeys == null) return;
1368212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        final int delay;
1369212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        switch (key.mCode) {
1370212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        case Constants.CODE_SHIFT:
1371212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            delay = sParams.mLongPressShiftLockTimeout;
1372212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            break;
1373212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        default:
1374212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout;
1375212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            if (mIsInSlidingKeyInputFromModifier) {
1376212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                // We use longer timeout for sliding finger input started from the modifier key.
1377212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                delay = longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
1378212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            } else {
1379212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                delay = longpressTimeout;
1380212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            }
1381212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            break;
1382212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
1383212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        mTimerProxy.startLongPressTimer(this, delay);
138466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
138566e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
13863623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
138783e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
13888aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1389dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1390dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
139166e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
1392694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final int code = key.mCode;
13933623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        callListenerOnCodeInput(key, code, x, y, eventTime);
139441016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        callListenerOnRelease(key, code, false /* withSliding */);
13956a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13966a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1397694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1398694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1399723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1400e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
140158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
140213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                (mIsTrackingForActionDisabled ? "-" : " "), title, x, y, eventTime, code));
1403dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
14046e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1405