PointerTracker.java revision 5c095e59f679f726df1b6655fbbd73e310ac0dec
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;
37e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils;
38e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CoordinateUtils;
39536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaokaimport com.android.inputmethod.latin.utils.ResourceUtils;
406b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
416a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
425c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
43dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
44a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class PointerTracker implements PointerTrackerQueue.Element {
45dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
46dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
47dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
48dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
49b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG || DEBUG_EVENT;
5040a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
510657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    /** True if {@link PointerTracker}s should handle gesture events. */
520657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    private static boolean sShouldHandleGesture = false;
538335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sMainDictionaryAvailable = false;
548335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByInputField = false;
558335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByUser = false;
56918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
67f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
68f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
69f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
70f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
71f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
72f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
73f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
74f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
75f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
76f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
77f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
78f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
79f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
80f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
81f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
82f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
83f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
84fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    public interface DrawingProxy {
856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
86e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
87d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
88547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        public void showSlidingKeyInputPreview(PointerTracker tracker);
8908d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        public void dismissSlidingKeyInputPreview();
90cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        public void showGestureTrail(PointerTracker tracker);
916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
932321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
94d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        public void startTypingStateTimer(Key typedKey);
9573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
96a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
97a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker);
9898b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
992a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public void startDoubleTapShiftKeyTimer();
1002a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public void cancelDoubleTapShiftKeyTimer();
1012a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public boolean isInDoubleTapShiftKeyTimeout();
1022321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
10372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        public void startUpdateBatchInputTimer(PointerTracker tracker);
104915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        public void cancelUpdateBatchInputTimer(PointerTracker tracker);
1052db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka        public void cancelAllUpdateBatchInputTimers();
10629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
10729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
10829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
109d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            public void startTypingStateTimer(Key typedKey) {}
11093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
11173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
11293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
113a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker) {}
11429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
115a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(PointerTracker tracker) {}
116a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            @Override
11729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelLongPressTimer() {}
11829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
1192a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            public void startDoubleTapShiftKeyTimer() {}
1200ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
1212a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            public void cancelDoubleTapShiftKeyTimer() {}
122beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            @Override
1232a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            public boolean isInDoubleTapShiftKeyTimeout() { return false; }
1240ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
12529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelKeyTimers() {}
12672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            @Override
12772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            public void startUpdateBatchInputTimer(PointerTracker tracker) {}
12872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            @Override
129915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            public void cancelUpdateBatchInputTimer(PointerTracker tracker) {}
130915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            @Override
1312db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka            public void cancelAllUpdateBatchInputTimers() {}
13229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
1332321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    }
1342321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka
135a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    static final class PointerTrackerParams {
1365509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final boolean mSlidingKeyInputEnabled;
1375509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
138b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public final int mTouchNoiseThresholdDistance;
1393623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public final int mSuppressKeyPreviewAfterBatchInputDuration;
1405509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1415509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
1425509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1435509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private PointerTrackerParams() {
1445509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
1455509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = 0;
146b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = 0;
1473623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = 0;
1485509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1495509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
15080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
1515509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
1525509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
1535509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
1545509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
155b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
1565509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
1573623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
1583623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
1595509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1605509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    }
1615509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
162160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
1635509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    private static PointerTrackerParams sParams;
16480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    private static GestureStrokeParams sGestureStrokeParams;
16505124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka    private static GestureStrokePreviewParams sGesturePreviewParams;
166d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
167b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // Move this threshold to resource.
168b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // TODO: Device specific parameter would be better for device specific hack?
169b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
170536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    // This hack is applied to certain classes of tablets.
171536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    // See {@link #needsProximateBogusDownMoveUpEventHack(Resources)}.
172536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static boolean sNeedsProximateBogusDownMoveUpEventHack;
1735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1745f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
17593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
1765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1775c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1786a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1790efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1800efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
181a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
182f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka    private KeyboardActionListener mListener = KeyboardActionListener.EMPTY_LISTENER;
183baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1845a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
185b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private int mPhantonSuddenMoveThreshold;
186b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
1876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1886c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
1896c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
1906c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sGestureFirstDownTime;
1913623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private static TimeRecorder sTimeRecorder;
1926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static final InputPointers sAggregratedPointers = new InputPointers(
1936c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            GestureStroke.DEFAULT_CAPACITY);
19458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
19558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
1969580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
197b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static final class BogusMoveEventDetector {
198b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Move these thresholds to resource.
199d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        // These thresholds' unit is a diagonal length of a key.
200d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
201d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
202b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
203b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mAccumulatedDistanceThreshold;
204b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mRadiusThreshold;
205b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
206b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Accumulated distance from actual and artificial down keys.
207b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int mAccumulatedDistanceFromDownKey;
208b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownX;
209b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownY;
210b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
211d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
212d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
213b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceThreshold = (int)(
214d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
215d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
216b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
217b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
218b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onActualDownEvent(final int x, final int y) {
219b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownX = x;
220b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownY = y;
221b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
222b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
223b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onDownKey() {
224b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey = 0;
225b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
226b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
227b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onMoveKey(final int distance) {
228b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey += distance;
229b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
230b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
231d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public boolean hasTraveledLongDistance(final int x, final int y) {
232d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dx = Math.abs(x - mActualDownX);
233d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dy = Math.abs(y - mActualDownY);
234d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // A bogus move event should be a horizontal movement. A vertical movement might be
235d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // a sloppy typing and should be ignored.
236d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
237b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
238b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
239b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int getDistanceFromDownEvent(final int x, final int y) {
240b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistance(x, y, mActualDownX, mActualDownY);
241b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
242b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
243b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isCloseToActualDownEvent(final int x, final int y) {
244b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
245b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
246b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
247b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2483623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    static final class TimeRecorder {
2493623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mSuppressKeyPreviewAfterBatchInputDuration;
2503623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mStaticTimeThresholdAfterFastTyping; // msec
2513623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastTypingTime;
2523623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastLetterTypingTime;
2533623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastBatchInputTime;
2543623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2553623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
2563623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                final GestureStrokeParams gestureStrokeParams) {
2573623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration =
2583623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
2593623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping =
2603623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
2613623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2623623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
263b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isInFastTyping(final long eventTime) {
264b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
265b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
266b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
267b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2687a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        private boolean wasLastInputTyping() {
2697a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return mLastTypingTime >= mLastBatchInputTime;
2703623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2713623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2723623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onCodeInput(final int code, final long eventTime) {
2737a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // Record the letter typing time when
2747a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 1. Letter keys are typed successively without any batch input in between.
2757a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 2. A letter key is typed within the threshold time since the last any key typing.
2767a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 3. A non-letter key is typed within the threshold time since the last letter key
2777a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // typing.
2787a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (Character.isLetter(code)) {
2797a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                if (wasLastInputTyping()
2807a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                        || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
2817a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2823623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2833623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            } else {
2843623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
2853623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    // This non-letter typing should be treated as a part of fast typing.
2867a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2873623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            }
2897a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastTypingTime = eventTime;
2903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2913623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2923623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onEndBatchInput(final long eventTime) {
2937a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastBatchInputTime = eventTime;
2943623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2953623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public long getLastLetterTypingTime() {
2973623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            return mLastLetterTypingTime;
2983623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2993623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3003623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
3017a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return !wasLastInputTyping()
3023623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
3033623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    }
3053623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3068a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
3078a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
308547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    private int[] mDownCoordinates = CoordinateUtils.newInstance();
3098a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
3108a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
311e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
312e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
313e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
3148a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
3158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
3168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3178a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
3188a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
3198a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
3206a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3211a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
3221a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
3231a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
32413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    // true if this pointer is no longer triggering any action because it has been canceled.
32513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    private boolean mIsTrackingForActionDisabled;
326c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
32735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    // the more keys panel currently being shown. equals null if no panel is active.
32835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private MoreKeysPanel mMoreKeysPanel;
3299ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
330f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input.
3315c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
332f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input from a modifier key,
333f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // so that further modifier keys should be ignored.
334f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    boolean mIsInSlidingKeyInputFromModifier;
335cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
336f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if a sliding key input is allowed.
33767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
33867a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
339c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
340f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
341536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static final int SMALL_TABLET_SMALLEST_WIDTH = 600; // dp
342536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static final int LARGE_TABLET_SMALLEST_WIDTH = 768; // dp
343536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka
344536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    private static boolean needsProximateBogusDownMoveUpEventHack(final Resources res) {
345536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // The proximate bogus down move up event hack is needed for a device such like,
346536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // 1) is large tablet, or 2) is small tablet and the screen density is less than hdpi.
347536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // Though it seems odd to use screen density as criteria of the quality of the touch
348536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // screen, the small table that has a less density screen than hdpi most likely has been
349536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        // made with the touch screen that needs the hack.
350536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final int sw = res.getConfiguration().smallestScreenWidthDp;
351536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean isLargeTablet = (sw >= LARGE_TABLET_SMALLEST_WIDTH);
352536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean isSmallTablet =
353536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                (sw >= SMALL_TABLET_SMALLEST_WIDTH && sw < LARGE_TABLET_SMALLEST_WIDTH);
354536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final int densityDpi = res.getDisplayMetrics().densityDpi;
355536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean hasLowDensityScreen = (densityDpi < DisplayMetrics.DENSITY_HIGH);
356536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        final boolean needsTheHack = isLargeTablet || (isSmallTablet && hasLowDensityScreen);
357536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        if (DEBUG_MODE) {
358536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka            Log.d(TAG, "needsProximateBogusDownMoveUpEventHack=" + needsTheHack
359536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                    + " smallestScreenWidthDp=" + sw + " densityDpi=" + densityDpi);
360536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        }
361536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        return needsTheHack;
362536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    }
363536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka
364536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka    public static void init(final Resources res) {
365536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
366536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                ResourceUtils.getDeviceOverrideValue(
367536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka                        res, R.array.phantom_sudden_move_event_device_list));
368536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        sNeedsProximateBogusDownMoveUpEventHack = needsProximateBogusDownMoveUpEventHack(res);
3695509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = PointerTrackerParams.DEFAULT;
37080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = GestureStrokeParams.DEFAULT;
37105124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka        sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT;
3723623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
373160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
37493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3755509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    public static void setParameters(final TypedArray mainKeyboardViewAttr) {
3765509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
37780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
37805124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka        sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
3793623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
3806a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3828335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
3838335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
3848335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
3858335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
3868335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
3878335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3888335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
3898335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
390694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
3918335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
3928335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
3938335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3948335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
395694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
3968335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
3978335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
398918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
399918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
400694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
401b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
4025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
4045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
4055c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
4065c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
4075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4085c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
4105c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4115c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
41393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.isAnyInSlidingKeyInput();
4145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4155c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
4165c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    public static void cancelAllPointerTrackers() {
4175c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        sPointerTrackerQueue.cancelAllPointerTrackers();
4185c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    }
4195c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka
420694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
421afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
422afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
423afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
4245c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
4255c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4265c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4275c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
428694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
429afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
430afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
431afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
4325c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
4335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
4345c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
4355c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
436918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
4378335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
4388335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
4395c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4405c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
441dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
442afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
443afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
444afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
445e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
4465c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4475c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4485c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
449ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    public static void dismissAllMoreKeysPanels() {
450ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        final int trackersSize = sTrackers.size();
451ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        for (int i = 0; i < trackersSize; ++i) {
452ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            final PointerTracker tracker = sTrackers.get(i);
453ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (tracker.isShowingMoreKeysPanel()) {
454ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel.dismissMoreKeysPanel();
455ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel = null;
456ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
457ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
458ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    }
459ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
460694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private PointerTracker(final int id, final KeyEventHandler handler) {
461694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (handler == null) {
4625c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
463694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
4645c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
46580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
46605124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka                id, sGestureStrokeParams, sGesturePreviewParams);
4675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
4685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
4695c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
4705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
4716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
4731a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
474694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
47513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // While gesture input is going on, this method should be a no-operation. But when gesture
47613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
47713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // are set to false. To keep this method is a no-operation,
47813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // <code>mIsTrackingForActionDisabled</code> should also be taken account of.
47913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
480eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
481eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
4828dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
483e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
48458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onPress    : %s%s%s", mPointerId,
48558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    KeyDetector.printableCode(key),
48658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ignoreModifierKey ? " ignoreModifier" : "",
48758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ? "" : " disabled"));
488e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
48993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
490996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
49193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
492e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
4936455172a707a1137eb15db8073774982db9dd1faTadashi G. Takaoka            mListener.onPressKey(key.mCode, false /* isRepeatKey */,
4946455172a707a1137eb15db8073774982db9dd1faTadashi G. Takaoka                    getActivePointerTrackerCount() == 1);
495690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
496690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
497d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
498690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
499690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
500690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
501dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
502dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
503690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
504690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
505694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
5063623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            final int y, final long eventTime) {
5078dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
5086bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
50929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
5102013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
511240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            final String output = code == Constants.CODE_OUTPUT_TEXT
512240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    ? key.getOutputText() : Constants.printableCode(code);
51358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
51458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    output, ignoreModifierKey ? " ignoreModifier" : "",
51558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
5162013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
5179c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5189bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
5199bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
5209bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
521e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
522996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
523e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
5246bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
5256bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
5263623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            sTimeRecorder.onCodeInput(code, eventTime);
527240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
52829d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                mListener.onTextInput(key.getOutputText());
529240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            } else if (code != Constants.CODE_UNSPECIFIED) {
530ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
5312013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
53293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
533dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
534dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
535f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may be in shifted state and the
536690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
537694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
538694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
53913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}.
54013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
541eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
542eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
5438dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
544e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
54558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
546240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    Constants.printableCode(primaryCode),
54758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
54858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ?  "": " disabled"));
549e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
5509c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5519bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
5529bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
5539bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
554e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
555996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
556e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
55793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
5582a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
55993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
560dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
561dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
56241016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    private void callListenerOnFinishSlidingInput() {
56341016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (DEBUG_LISTENER) {
56441016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId));
56541016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
56641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        mListener.onFinishSlidingInput();
56741016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    }
56841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka
5698aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
570694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
57158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
572694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
5739c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5749bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
5759bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5768aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
577dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
578dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
579694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
58058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
58158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
58258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return;
58358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
584a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
5855a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
586d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyWidth = mKeyboard.mMostCommonKeyWidth;
587d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyHeight = mKeyboard.mMostCommonKeyHeight;
588b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
5898a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
5908a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
5918a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
5928a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
5938a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
59444972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
5958a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
596b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
597d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
5985a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
5995a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
6000cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
601cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
602cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
603cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
604cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
605547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public boolean isInSlidingKeyInputFromModifier() {
606547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        return mIsInSlidingKeyInputFromModifier;
607547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
608547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
609e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
610e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
611dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
612dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
6130cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
6142aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
615e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
6162aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
6172aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
618694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
619723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
6202aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
6212aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
622694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setReleasedKeyGraphics(final Key key) {
623d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
6246bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
625faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
626faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
627faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
6286bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
629faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
630faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
631faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
632faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
633faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
634faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
6352013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6362013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
637faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6382013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
639faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
64029d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
641faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
642faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
643faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
644faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
645faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
64629d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
647faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
6482013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6492013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
6506a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
65329d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
65429d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!sShouldHandleGesture) return false;
6553623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
65629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    }
65729d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka
65829d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void setPressedKeyGraphics(final Key key, final long eventTime) {
6596bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
6606bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
6616bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
6626bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
6636bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
6646bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
6656bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
6666bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
667faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
668faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
669faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
67029d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
671faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
672faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
673faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
674faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
675faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
676faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
677faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
678faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
6792013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6802013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
681faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6822013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
68373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
68429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
685faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
686faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
687faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
688faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
689faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
69029d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
691faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
6922013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6932013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
694d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
695c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
696c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
697694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updateReleaseKeyGraphics(final Key key) {
698faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
699faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
700faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
701faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
702694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updatePressKeyGraphics(final Key key) {
703faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
704faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
705faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
706faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
707c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
708c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return mGestureStrokeWithPreviewPoints;
7092f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
7102f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
711547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getLastCoordinates(final int[] outCoords) {
712547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(outCoords, mLastX, mLastY);
7138a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7148a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
7158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
7168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
7178a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7188a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
719547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getDownCoordinates(final int[] outCoords) {
720547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.copy(outCoords, mDownCoordinates);
721547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
722547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
723694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
7248a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
725547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(mDownCoordinates, x, y);
726b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onDownKey();
7278a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
7288a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7298a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
730b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static int getDistance(final int x1, final int y1, final int x2, final int y2) {
731b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        return (int)Math.hypot(x1 - x2, y1 - y2);
732b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
733b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
734694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
735b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
7368a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
7378a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
738723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
7398a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7408a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
741694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
7428a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
7438a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
745694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
746e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
7478a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
7488a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
749e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
7508a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7518a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
7526c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int getActivePointerTrackerCount() {
75393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.size();
7546c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7556c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
756ab334eb64669e909f0a401fddffa891962002602Tadashi G. Takaoka    public boolean isOldestTrackerInQueue() {
757ab334eb64669e909f0a401fddffa891962002602Tadashi G. Takaoka        return sPointerTrackerQueue.getOldestElement() == this;
75813ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    }
75913ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka
7601645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka    private void mayStartBatchInput(final Key key) {
761c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
7629c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            return;
7639c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        }
7641645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        if (key == null || !Character.isLetter(key.mCode)) {
7651645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            return;
7661645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        }
7679c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
76858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
7699580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7709c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        sInGesture = true;
77158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
77258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sAggregratedPointers.reset();
77358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionPointSize = 0;
77458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionTime = 0;
77558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mListener.onStartBatchInput();
776ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            dismissAllMoreKeysPanels();
77758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
778ad181915f78235bc09e88c85ed9df669801b8442Tadashi G. Takaoka        mTimerProxy.cancelLongPressTimer();
779cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        mDrawingProxy.showGestureTrail(this);
7806c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7816c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
78272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    public void updateBatchInputByTimer(final long eventTime) {
78372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
78472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.duplicateLastPointWith(gestureTime);
78572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        updateBatchInput(eventTime);
78672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
78772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
788157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka    private void mayUpdateBatchInput(final long eventTime, final Key key) {
789157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka        if (key != null) {
79072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            updateBatchInput(eventTime);
79172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
79213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
7936f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
7946f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
795cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        mDrawingProxy.showGestureTrail(this);
79672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
79772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
79872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    private void updateBatchInput(final long eventTime) {
79972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
80072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
80172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            stroke.appendIncrementalBatchPoints(sAggregratedPointers);
80272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int size = sAggregratedPointers.getPointerSize();
80372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            if (size > sLastRecognitionPointSize
80472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
80572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                sLastRecognitionPointSize = size;
80672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                sLastRecognitionTime = eventTime;
80772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                if (DEBUG_LISTENER) {
80872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
80972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                            size));
8106c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
81172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
81272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mListener.onUpdateBatchInput(sAggregratedPointers);
8136c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
8149580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
8159580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8169580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
81729d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void mayEndBatchInput(final long eventTime) {
8186c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        synchronized (sAggregratedPointers) {
819c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
8206c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
8216c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sInGesture = false;
8223623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                sTimeRecorder.onEndBatchInput(eventTime);
823b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka                mTimerProxy.cancelAllUpdateBatchInputTimers();
82413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                if (!mIsTrackingForActionDisabled) {
825b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    if (DEBUG_LISTENER) {
826b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                        Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
827b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                                mPointerId, sAggregratedPointers.getPointerSize()));
828b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    }
829b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    mListener.onEndBatchInput(sAggregratedPointers);
830b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                }
8316c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
8329580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
83313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
8346f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
8356f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
836cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        mDrawingProxy.showGestureTrail(this);
8379580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8389580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
8398e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    private void cancelBatchInput() {
8405c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllPointerTrackers();
8418353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        mIsDetectingGesture = false;
8427c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        if (!sInGesture) {
8437c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka            return;
8447c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        }
8458e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sInGesture = false;
8468e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        if (DEBUG_LISTENER) {
8478e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
8488e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        }
8498e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        mListener.onCancelBatchInput();
8508e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    }
8518e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka
852694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
853694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
8548ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
8558ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
8568ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
8578ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
8588ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8598ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
8608ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
8618ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
8628ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8638ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
8643314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
8658ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8668ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
8678ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
8688ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8698ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
8708ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
8718ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
872694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long eventTime,
873694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
874694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
875dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
876694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
877f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
87863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
879f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
880f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
881baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
8828a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
883160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
884b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distance = getDistance(x, y, mLastX, mLastY);
885b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distance < sParams.mTouchNoiseThresholdDistance) {
886faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
887b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.w(TAG, String.format("[%d] onDownEvent:"
888b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            + " ignore potential noise: time=%d distance=%d",
889b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, deltaT, distance));
8909c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
891b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
8929bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
89313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
894baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
895baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
896baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
897baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
898eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
899b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onActualDownEvent(x, y);
90093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (key != null && key.isModifier()) {
90193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // Before processing a down event of modifier key, all pointers already being
90293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // tracked should be released.
90393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            sPointerTrackerQueue.releaseAllPointers(eventTime);
9041d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
90593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.add(this);
9061d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
9076c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!sShouldHandleGesture) {
9086c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
9096c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
9104df6549c1a5ae2cdc2cdfafdad1ec2a75881134cTadashi G. Takaoka        // A gesture should start only from a non-modifier key.
911b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
912ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                && key != null && !key.isModifier();
913b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
914b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
9156c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sGestureFirstDownTime = eventTime;
9169580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
91758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
9183623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    sTimeRecorder.getLastLetterTypingTime());
919eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
9201d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9211d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
92235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private boolean isShowingMoreKeysPanel() {
92335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        return (mMoreKeysPanel != null);
92435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
92535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
926694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
927e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
92867a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
92932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
930160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
931c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
93232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
9331a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
93413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = false;
935f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
936e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
9371a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
938e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
9391a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
9402a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
941e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
942e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
943996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
944e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
945e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
94629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            setPressedKeyGraphics(key, eventTime);
9476a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9496a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
950694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startSlidingKeyInput(final Key key) {
951e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
952f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka            mIsInSlidingKeyInputFromModifier = key.isModifier();
953e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
954996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
955996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
956996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
957f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    private void resetSlidingKeyInput() {
958f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
959f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInputFromModifier = false;
96008d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        mDrawingProxy.dismissSlidingKeyInputPreview();
961f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    }
962f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka
9636c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
96402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
9656c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
9666c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (mIsDetectingGesture) {
967b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            final int beforeLength = mGestureStrokeWithPreviewPoints.getLength();
968b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard(
969b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    x, y, gestureTime, isMajorEvent);
970b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            if (mGestureStrokeWithPreviewPoints.getLength() > beforeLength) {
971b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
972b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            }
9738353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            // If the move event goes out from valid batch input area, cancel batch input.
974b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            if (!onValidArea) {
9758e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka                cancelBatchInput();
976b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                return;
977b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            }
978ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // If the MoreKeysPanel is showing then do not attempt to enter gesture mode. However,
979ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // the gestured touch points are still being recorded in case the panel is dismissed.
980ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (isShowingMoreKeysPanel()) {
981ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                return;
982ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
9831645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            mayStartBatchInput(key);
984157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            if (sInGesture) {
985157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mayUpdateBatchInput(eventTime, key);
9869580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
9879580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
9889580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
9899580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
990694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
991694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
992dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
993694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
99413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
995e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
996694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
997baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
9986c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture && me != null) {
999eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
1000eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
1001eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
1002eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
1003eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
1004eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
1005eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
10066c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
100702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
1008eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
1009eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1010ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
1011ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
1012ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedX = mMoreKeysPanel.translateX(x);
1013ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedY = mMoreKeysPanel.translateY(y);
1014ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
1015ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            onMoveKey(x, y);
1016ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            mDrawingProxy.showSlidingKeyInputPreview(this);
1017ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            return;
1018ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
10196c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
10206c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
10216c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
10223c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processSlidingKeyInput(final Key newKey, final int x, final int y,
10233c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime) {
10243c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // This onPress call may have changed keyboard layout. Those cases are detected
10253c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // at {@link #setKeyboard}. In those cases, we should update key according
10263c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // to the new keyboard layout.
10273c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        Key key = newKey;
10283c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
10293c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            key = onMoveKey(x, y);
10303c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
10313c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onMoveToNewKey(key, x, y);
103213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
103313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            return;
103413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        }
10353c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        startLongPressTimer(key);
10363c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        setPressedKeyGraphics(key, eventTime);
10373c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10383c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10393c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
10403c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10413c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10423c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
10433c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " phantom sudden move event (distance=%d) is translated to "
10443c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
10453c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    getDistance(x, y, lastX, lastY),
10463c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
10473c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
10483c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
10493c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // TODO: This should be moved to outside of this nested if-clause?
10509c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
10513c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
10523c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
105335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10543c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10553c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10563c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10573c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
10583c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10593c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10603c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(
10613c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
10623c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float radiusRatio =
10633c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
10643c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    / keyDiagonal;
10653c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
10663c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " bogus down-move-up event (raidus=%.2f key diagonal) is "
10673c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
10683c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mPointerId, radiusRatio,
10693c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
10703c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
10713c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
107235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10733c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10743c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10753c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
1076831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    private void processSildeOutFromOldKey(final Key oldKey) {
1077831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        setReleasedKeyGraphics(oldKey);
107841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        callListenerOnRelease(oldKey, oldKey.mCode, true /* withSliding */);
1079831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        startSlidingKeyInput(oldKey);
1080831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1081831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    }
1082831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka
10833c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void slideFromOldKeyToNewKey(final Key key, final int x, final int y,
10848b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10858b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid in to the new key from the previous key, we must call
10868b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onRelease() first to notify that the previous key has been released, then call
10878b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onPress() to notify that the new key is being pressed.
1088831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
10898b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        startRepeatKey(key);
10908b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
10913c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            processSlidingKeyInput(key, x, y, eventTime);
10922a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
10932a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive touches may be reported as a sudden move by
10942a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // touch panel firmware. This hack detects such cases and translates the move event to
10952a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // successive up and down events.
10962a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // TODO: Should find a way to balance gesture detection and this hack.
10972a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsPhantomSuddenMoveEventHack
10982a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
10992a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
11002a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
11012a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive proximate touches may be reported as a bogus
11022a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // down-move-up event by touch panel firmware. This hack detects such cases and breaks
11032a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // these events into separate up and down events.
11042a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime)
11052a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
11062a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
11072a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
11082a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: If there are currently multiple touches, register the key even if the finger
11092a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // slides off the key. This defends against noise from some touch panels when there are
11102a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // close multiple touches.
11112a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // Caveat: When in chording input mode with a modifier key, we don't use this hack.
111293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        else if (getActivePointerTrackerCount() > 1
11132a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
11142a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (DEBUG_MODE) {
11152a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                Log.w(TAG, String.format("[%d] onMoveEvent:"
11162a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                        + " detected sliding finger while multi touching", mPointerId));
11173c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            }
11182a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
111913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            cancelTrackingForAction();
11202a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
11212a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        } else {
11222a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (!mIsDetectingGesture) {
112313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
11248b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
11252a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
11268b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
11278b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
11288b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
11298b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    private void slideOutFromOldKey(final Key oldKey, final int x, final int y) {
11308b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid out from the previous key, we must call onRelease() to
11318b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // notify that the previous key has been released.
1132831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
11338b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
11348b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onMoveToNewKey(null, x, y);
11358b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        } else {
11368b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (!mIsDetectingGesture) {
113713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
11388b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
11398b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
11408b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
11418b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
11426c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
11438a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
11448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
1145e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
11468b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        final Key newKey = onMoveKey(x, y);
1147eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11486c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture) {
11496c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
11508b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
11516c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
11526c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
11536c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
11546c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
11556c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
1156eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1157eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11588b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (newKey != null) {
11593c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11608b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
11613c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            } else if (oldKey == null) {
1162831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
1163831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
1164831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                processSlidingKeyInput(newKey, x, y, eventTime);
1165c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
1166831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        } else { // newKey == null
11678b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11688b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideOutFromOldKey(oldKey, x, y);
116907221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
11706a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1171547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        mDrawingProxy.showSlidingKeyInputPreview(this);
11726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1174694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onUpEvent(final int x, final int y, final long eventTime) {
1175694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1176dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
1177694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
11781d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1179915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        mTimerProxy.cancelUpdateBatchInputTimer(this);
118093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (!sInGesture) {
118193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            if (mCurrentKey != null && mCurrentKey.isModifier()) {
118293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
118393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // tracked should be released.
118493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
118593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            } else {
118693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
11871d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
11881d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
118935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
119093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
11911d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
11921d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1193d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
1194d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
1195d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
11960cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
1197694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
1198694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1199547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka            printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
1200694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
120135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
120235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
120335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
120435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(mLastX, mLastY, eventTime);
120513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
12061d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12071d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
120835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private void onUpEventInternal(final int x, final int y, final long eventTime) {
12092321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1210e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
121141016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        final boolean isInSlidingKeyInputFromModifier = mIsInSlidingKeyInputFromModifier;
1212f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
12136c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
12146c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
12156c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
12165a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
12176c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        setReleasedKeyGraphics(currentKey);
121835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
121935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
122013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            if (!mIsTrackingForActionDisabled) {
122135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedX = mMoreKeysPanel.translateX(x);
122235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedY = mMoreKeysPanel.translateY(y);
122335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
122435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            }
122535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
122635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel = null;
122735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
12289ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
1229eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
12306c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
12316c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
123241016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.mCode, true /* withSliding */);
1233eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
123429d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            mayEndBatchInput(eventTime);
1235eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
1236eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
123758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
123813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
1239d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
1240694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1241e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (currentKey != null && currentKey.isRepeatable() && !isInSlidingKeyInput) {
1242e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka            // Repeatable key has been registered in {@link #onDownEventInternal(int,int,long)}.
1243e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka            return;
12446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1245e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
124641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (isInSlidingKeyInputFromModifier) {
124741016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            callListenerOnFinishSlidingInput();
124841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
12496a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12506a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
125135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    public void onShowMoreKeysPanel(final int translatedX, final int translatedY,
125235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final MoreKeysPanel panel) {
125335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        setReleasedKeyGraphics(mCurrentKey);
125435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final long eventTime = SystemClock.uptimeMillis();
125535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel = panel;
125635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, eventTime);
12579ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
12589ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
1259b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    @Override
126013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    public void cancelTrackingForAction() {
12618353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
12628353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            return;
12638353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        }
126413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = true;
1265b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    }
1266b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
1267906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
1268547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        resetSlidingKeyInput();
126913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
1270e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
127193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
1272d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
1273d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
1274694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onCancelEvent(final int x, final int y, final long eventTime) {
1275694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1276dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
1277694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
12781d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
12797c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        cancelBatchInput();
12805c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllPointerTrackers();
12818e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.releaseAllPointers(eventTime);
1282baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
12831d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12841d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1285baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
12862321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1287e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
1288f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
1289ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
1290ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
1291ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel = null;
1292ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
12936a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12946a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1295694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
1296ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (sInGesture) return;
1297ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (key == null) return;
1298ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (!key.isRepeatable()) return;
1299ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        // Don't start key repeat when we are in sliding input mode.
1300e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (mIsInSlidingKeyInput) return;
1301009488eaaf25f04ca841f7741dc8b270f7da9000Tadashi G. Takaoka        detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
1302f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        mTimerProxy.startKeyRepeatTimer(this);
13036a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1305b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
1306b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final Key newKey) {
1307694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyDetector == null) {
1308a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
1309694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1310694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
13116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1312e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1313a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1314a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (curKey == null /* && newKey != null */) {
1315a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1316a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1317a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        // Here curKey points to the different key from newKey.
1318a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
1319a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                mIsInSlidingKeyInputFromModifier);
1320a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
1321a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
1322a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1323a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
1324a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        / mKeyboard.mMostCommonKeyWidth;
1325a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1326a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
1327b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1328a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1329a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1330a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
1331a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && sTimeRecorder.isInFastTyping(eventTime)
1332a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
1333a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1334a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float keyDiagonal = (float)Math.hypot(
1335a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
1336a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float lengthFromDownRatio =
1337a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
1338a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1339a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        + " %.2f key diagonal from virtual down point",
1340a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mPointerId, lengthFromDownRatio));
1341b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1342e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
13436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1344a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        return false;
13456a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1347694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
134843ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (sInGesture) return;
134943ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (key == null) return;
135043ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (!key.isLongPressEnabled()) return;
135143ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // Caveat: Please note that isLongPressEnabled() can be true even if the current key
135243ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // doesn't have its more keys. (e.g. spacebar, globe key)
135343ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // We always need to start the long press timer if the key has its more keys regardless of
135443ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // whether or not we are in the sliding input mode.
1355e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (mIsInSlidingKeyInput && key.mMoreKeys == null) return;
135643ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        mTimerProxy.startLongPressTimer(this);
135766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
135866e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
13593623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
136083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
13618aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1362dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1363dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
136466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
1365694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final int code = key.mCode;
13663623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        callListenerOnCodeInput(key, code, x, y, eventTime);
136741016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        callListenerOnRelease(key, code, false /* withSliding */);
13686a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13696a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1370694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1371694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1372723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1373e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
137458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
137513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                (mIsTrackingForActionDisabled ? "-" : " "), title, x, y, eventTime, code));
1376dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
13776e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1378