PointerTracker.java revision 41016acacfa21354f59ed51db1f85ea3d99bf6a4
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
195509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport android.content.res.TypedArray;
20e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaokaimport android.os.SystemClock;
21c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport android.util.Log;
228ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaokaimport android.view.MotionEvent;
23c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
24918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
25f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStroke;
2680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokeParams;
27c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
2805124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints.GestureStrokePreviewParams;
2972934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
305f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaokaimport com.android.inputmethod.latin.CollectionUtils;
31240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaokaimport com.android.inputmethod.latin.Constants;
32547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaokaimport com.android.inputmethod.latin.CoordinateUtils;
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;
376b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
386a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
395c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
40dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
41a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class PointerTracker implements PointerTrackerQueue.Element {
42dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
43dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
44dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
45dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
46b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG || DEBUG_EVENT;
4740a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
480657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    /** True if {@link PointerTracker}s should handle gesture events. */
490657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    private static boolean sShouldHandleGesture = false;
508335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sMainDictionaryAvailable = false;
518335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByInputField = false;
528335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByUser = false;
53918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
54f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
55f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
67f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
68f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
69f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
70f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
71f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
72f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
73f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
74f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
75f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
76f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
77f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
78f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
79f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
80f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
81fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    public interface DrawingProxy {
826a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
83e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
84d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
85547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        public void showSlidingKeyInputPreview(PointerTracker tracker);
8608d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        public void dismissSlidingKeyInputPreview();
87cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        public void showGestureTrail(PointerTracker tracker);
886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
896a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
902321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
91d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        public void startTypingStateTimer(Key typedKey);
9273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
93a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
94a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker);
95a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code);
9698b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
970ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer();
98beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer();
990ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout();
1002321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
10172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        public void startUpdateBatchInputTimer(PointerTracker tracker);
102915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        public void cancelUpdateBatchInputTimer(PointerTracker tracker);
1032db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka        public void cancelAllUpdateBatchInputTimers();
10429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
10529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
10629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
107d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            public void startTypingStateTimer(Key typedKey) {}
10893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
10973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
11093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
111a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker) {}
11229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
113a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(PointerTracker tracker) {}
114a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            @Override
115a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(int code) {}
11629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
11729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelLongPressTimer() {}
11829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
1190ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public void startDoubleTapTimer() {}
1200ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
121beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            public void cancelDoubleTapTimer() {}
122beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            @Override
1230ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public boolean isInDoubleTapTimeout() { 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
170b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // This hack might be device specific.
171b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final boolean sNeedsProximateBogusDownMoveUpEventHack = true;
1725c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1735f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
17493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
1755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1776a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1780efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1790efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
180a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
18184ac8f31fbe30f03c14a85ffb8c827edb6e47ab0Tadashi G. Takaoka    private KeyboardActionListener mListener = KeyboardActionListener.Adapter.EMPTY_LISTENER;
182baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1835a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
184b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private int mPhantonSuddenMoveThreshold;
185b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
1866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1876c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
1886c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
1896c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sGestureFirstDownTime;
1903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private static TimeRecorder sTimeRecorder;
1916c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static final InputPointers sAggregratedPointers = new InputPointers(
1926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            GestureStroke.DEFAULT_CAPACITY);
19358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
19458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
1959580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
196b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static final class BogusMoveEventDetector {
197b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Move these thresholds to resource.
198d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        // These thresholds' unit is a diagonal length of a key.
199d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
200d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
201b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
202b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mAccumulatedDistanceThreshold;
203b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mRadiusThreshold;
204b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
205b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Accumulated distance from actual and artificial down keys.
206b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int mAccumulatedDistanceFromDownKey;
207b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownX;
208b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownY;
209b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
210d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
211d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
212b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceThreshold = (int)(
213d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
214d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
215b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
216b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
217b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onActualDownEvent(final int x, final int y) {
218b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownX = x;
219b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownY = y;
220b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
221b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
222b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onDownKey() {
223b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey = 0;
224b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
225b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
226b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onMoveKey(final int distance) {
227b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey += distance;
228b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
229b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
230d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public boolean hasTraveledLongDistance(final int x, final int y) {
231d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dx = Math.abs(x - mActualDownX);
232d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dy = Math.abs(y - mActualDownY);
233d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // A bogus move event should be a horizontal movement. A vertical movement might be
234d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // a sloppy typing and should be ignored.
235d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
236b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
237b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
238b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int getDistanceFromDownEvent(final int x, final int y) {
239b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistance(x, y, mActualDownX, mActualDownY);
240b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
241b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
242b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isCloseToActualDownEvent(final int x, final int y) {
243b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
244b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
245b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
246b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2473623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    static final class TimeRecorder {
2483623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mSuppressKeyPreviewAfterBatchInputDuration;
2493623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mStaticTimeThresholdAfterFastTyping; // msec
2503623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastTypingTime;
2513623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastLetterTypingTime;
2523623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastBatchInputTime;
2533623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2543623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
2553623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                final GestureStrokeParams gestureStrokeParams) {
2563623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration =
2573623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
2583623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping =
2593623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
2603623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2613623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
262b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isInFastTyping(final long eventTime) {
263b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
264b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
265b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
266b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2677a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        private boolean wasLastInputTyping() {
2687a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return mLastTypingTime >= mLastBatchInputTime;
2693623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2703623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2713623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onCodeInput(final int code, final long eventTime) {
2727a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // Record the letter typing time when
2737a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 1. Letter keys are typed successively without any batch input in between.
2747a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 2. A letter key is typed within the threshold time since the last any key typing.
2757a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 3. A non-letter key is typed within the threshold time since the last letter key
2767a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // typing.
2777a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (Character.isLetter(code)) {
2787a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                if (wasLastInputTyping()
2797a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                        || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
2807a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2813623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2823623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            } else {
2833623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
2843623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    // This non-letter typing should be treated as a part of fast typing.
2857a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2863623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2873623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            }
2887a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastTypingTime = eventTime;
2893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2913623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onEndBatchInput(final long eventTime) {
2927a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastBatchInputTime = eventTime;
2933623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2943623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2953623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public long getLastLetterTypingTime() {
2963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            return mLastLetterTypingTime;
2973623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2983623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2993623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
3007a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return !wasLastInputTyping()
3013623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
3023623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
3033623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    }
3043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3058a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
3068a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
307547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    private int[] mDownCoordinates = CoordinateUtils.newInstance();
3088a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
3098a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
310e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
311e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
312e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
3138a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
3148a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
3158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
3178a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
3188a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
3196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3201a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
3211a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
3221a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
32313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    // true if this pointer is no longer triggering any action because it has been canceled.
32413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    private boolean mIsTrackingForActionDisabled;
325c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
32635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    // the more keys panel currently being shown. equals null if no panel is active.
32735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private MoreKeysPanel mMoreKeysPanel;
3289ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
329f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input.
3305c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
331f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input from a modifier key,
332f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // so that further modifier keys should be ignored.
333f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    boolean mIsInSlidingKeyInputFromModifier;
334cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
335f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if a sliding key input is allowed.
33667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
33767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
338c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
339f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
34093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    public static void init(final boolean needsPhantomSuddenMoveEventHack) {
341d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
3425509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = PointerTrackerParams.DEFAULT;
34380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = GestureStrokeParams.DEFAULT;
34405124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka        sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT;
3453623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
346160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
34793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3485509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    public static void setParameters(final TypedArray mainKeyboardViewAttr) {
3495509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
35080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
35105124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka        sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
3523623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
3536a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3546a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3558335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
3568335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
3578335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
3588335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
3598335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
3608335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3618335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
3628335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
363694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
3648335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
3658335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
3668335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3678335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
368694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
3698335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
3708335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
371918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
372918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
373694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
374b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
3755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
3775c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
3785c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
3795c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
3805c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
3835c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3845c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3855c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
38693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.isAnyInSlidingKeyInput();
3875c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
389694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
390afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
391afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
392afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
3935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
3945c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3965c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
397694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
398afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
399afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
400afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
4015c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
4025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
4035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
4045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
405918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
4068335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
4078335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
4085c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
410dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
411afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
412afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
413afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
414e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
4155c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4165c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4175c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
418ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    public static void dismissAllMoreKeysPanels() {
419ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        final int trackersSize = sTrackers.size();
420ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        for (int i = 0; i < trackersSize; ++i) {
421ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            final PointerTracker tracker = sTrackers.get(i);
422ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (tracker.isShowingMoreKeysPanel()) {
423ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel.dismissMoreKeysPanel();
424ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel = null;
425ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
426ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
427ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    }
428ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
429694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private PointerTracker(final int id, final KeyEventHandler handler) {
430694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (handler == null) {
4315c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
432694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
4335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
43480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
43505124d019352d1aca08de95dbfbd5510b5e9e92cTadashi G. Takaoka                id, sGestureStrokeParams, sGesturePreviewParams);
4365c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
4375c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
4385c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
4395c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
4406a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4416a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
4421a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
443694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
44413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // While gesture input is going on, this method should be a no-operation. But when gesture
44513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
44613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // are set to false. To keep this method is a no-operation,
44713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // <code>mIsTrackingForActionDisabled</code> should also be taken account of.
44813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
449eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
450eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
4518dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
452e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
45358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onPress    : %s%s%s", mPointerId,
45458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    KeyDetector.printableCode(key),
45558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ignoreModifierKey ? " ignoreModifier" : "",
45658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ? "" : " disabled"));
457e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
45893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
459996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
46093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
461e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
46241016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            mListener.onPressKey(key.mCode, getActivePointerTrackerCount() == 1);
463690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
464690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
465d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
466690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
467690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
468690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
469dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
470dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
471690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
472690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
473694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
4743623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            final int y, final long eventTime) {
4758dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
4766bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
47729d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
4782013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
479240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            final String output = code == Constants.CODE_OUTPUT_TEXT
480240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    ? key.getOutputText() : Constants.printableCode(code);
48158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
48258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    output, ignoreModifierKey ? " ignoreModifier" : "",
48358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
4842013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
4859c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
4869bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
4879bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
4889bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
489e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
490996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
491e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
4926bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
4936bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
4943623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            sTimeRecorder.onCodeInput(code, eventTime);
495240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
49629d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                mListener.onTextInput(key.getOutputText());
497240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            } else if (code != Constants.CODE_UNSPECIFIED) {
498ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
4992013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
50093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
501dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
502dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
503f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may be in shifted state and the
504690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
505694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
506694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
50713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}.
50813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
509eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
510eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
5118dfbb740e1015af0cd339a183dd333a5f53c52c4Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
512e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
51358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
514240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    Constants.printableCode(primaryCode),
51558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
51658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ?  "": " disabled"));
517e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
5189c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5199bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
5209bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
5219bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
522e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
523996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
524e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
52593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
5262a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
52793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
528dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
529dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
53041016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    private void callListenerOnFinishSlidingInput() {
53141016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (DEBUG_LISTENER) {
53241016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId));
53341016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
53441016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        mListener.onFinishSlidingInput();
53541016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    }
53641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka
5378aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
538694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
53958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
540694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
5419c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5429bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
5439bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5448aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
545dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
546dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
547694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
54858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
54958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
55058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return;
55158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
552a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
5535a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
554d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyWidth = mKeyboard.mMostCommonKeyWidth;
555d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyHeight = mKeyboard.mMostCommonKeyHeight;
556b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
5578a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
5588a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
5598a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
5608a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
5618a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
56244972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
5638a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
564b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
565d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
5665a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
5675a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
5680cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
569cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
570cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
571cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
572cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
573547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public boolean isInSlidingKeyInputFromModifier() {
574547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        return mIsInSlidingKeyInputFromModifier;
575547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
576547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
577e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
578e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
579dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
580dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
5810cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
5822aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
583e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
5842aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
5852aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
586694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
587723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
5882aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
5892aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
590694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setReleasedKeyGraphics(final Key key) {
591d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
5926bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
593faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
594faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
595faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
5966bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
597faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
598faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
599faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
600faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
601faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
602faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
6032013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6042013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
605faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6062013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
607faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
60829d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
609faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
610faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
611faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
612faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
613faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
61429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
615faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
6162013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6172013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
6186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6206a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
62129d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
62229d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!sShouldHandleGesture) return false;
6233623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
62429d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    }
62529d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka
62629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void setPressedKeyGraphics(final Key key, final long eventTime) {
6276bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
6286bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
6296bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
6306bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
6316bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
6326bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
6336bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
6346bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
635faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
636faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
637faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
63829d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
639faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
640faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
641faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
642faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
643faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
644faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
645faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
646faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
6472013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6482013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
649faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6502013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
65173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
65229d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
653faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
654faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
655faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
656faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
657faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
65829d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
659faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
6602013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6612013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
662d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
663c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
664c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
665694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updateReleaseKeyGraphics(final Key key) {
666faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
667faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
668faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
669faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
670694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updatePressKeyGraphics(final Key key) {
671faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
672faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
673faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
674faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
675c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
676c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return mGestureStrokeWithPreviewPoints;
6772f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
6782f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
679547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getLastCoordinates(final int[] outCoords) {
680547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(outCoords, mLastX, mLastY);
6818a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6828a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
6838a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
6848a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
6858a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6868a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
687547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getDownCoordinates(final int[] outCoords) {
688547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.copy(outCoords, mDownCoordinates);
689547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
690547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
691694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
6928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
693547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(mDownCoordinates, x, y);
694b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onDownKey();
6958a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
6968a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6978a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
698b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static int getDistance(final int x1, final int y1, final int x2, final int y2) {
699b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        return (int)Math.hypot(x1 - x2, y1 - y2);
700b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
701b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
702694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
703b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
7048a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
7058a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
706723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
7078a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7088a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
709694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
7108a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
7118a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7128a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
713694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
714e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
7158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
7168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
717e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
7188a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7198a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
7206c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int getActivePointerTrackerCount() {
72193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.size();
7226c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7236c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
724ab334eb64669e909f0a401fddffa891962002602Tadashi G. Takaoka    public boolean isOldestTrackerInQueue() {
725ab334eb64669e909f0a401fddffa891962002602Tadashi G. Takaoka        return sPointerTrackerQueue.getOldestElement() == this;
72613ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    }
72713ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka
7281645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka    private void mayStartBatchInput(final Key key) {
729c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
7309c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            return;
7319c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        }
7321645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        if (key == null || !Character.isLetter(key.mCode)) {
7331645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            return;
7341645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        }
7359c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
73658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
7379580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7389c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        sInGesture = true;
73958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
74058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sAggregratedPointers.reset();
74158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionPointSize = 0;
74258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionTime = 0;
74358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mListener.onStartBatchInput();
744ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            dismissAllMoreKeysPanels();
74558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
746ad181915f78235bc09e88c85ed9df669801b8442Tadashi G. Takaoka        mTimerProxy.cancelLongPressTimer();
747cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        mDrawingProxy.showGestureTrail(this);
7486c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7496c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
75072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    public void updateBatchInputByTimer(final long eventTime) {
75172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
75272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.duplicateLastPointWith(gestureTime);
75372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        updateBatchInput(eventTime);
75472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
75572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
756157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka    private void mayUpdateBatchInput(final long eventTime, final Key key) {
757157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka        if (key != null) {
75872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            updateBatchInput(eventTime);
75972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
76013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
7616f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
7626f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
763cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        mDrawingProxy.showGestureTrail(this);
76472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
76572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
76672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    private void updateBatchInput(final long eventTime) {
76772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
76872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
76972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            stroke.appendIncrementalBatchPoints(sAggregratedPointers);
77072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int size = sAggregratedPointers.getPointerSize();
77172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            if (size > sLastRecognitionPointSize
77272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
77372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                sLastRecognitionPointSize = size;
77472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                sLastRecognitionTime = eventTime;
77572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                if (DEBUG_LISTENER) {
77672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
77772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                            size));
7786c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
77972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
78072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mListener.onUpdateBatchInput(sAggregratedPointers);
7816c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
7829580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7839580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7849580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
78529d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void mayEndBatchInput(final long eventTime) {
7866c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        synchronized (sAggregratedPointers) {
787c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
7886c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
7896c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sInGesture = false;
7903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                sTimeRecorder.onEndBatchInput(eventTime);
791b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka                mTimerProxy.cancelAllUpdateBatchInputTimers();
79213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                if (!mIsTrackingForActionDisabled) {
793b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    if (DEBUG_LISTENER) {
794b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                        Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
795b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                                mPointerId, sAggregratedPointers.getPointerSize()));
796b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    }
797b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    mListener.onEndBatchInput(sAggregratedPointers);
798b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                }
7996c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
8009580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
80113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
8026f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
8036f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
804cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        mDrawingProxy.showGestureTrail(this);
8059580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8069580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
8078e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    private void cancelBatchInput() {
8088e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.cancelAllPointerTracker();
8098353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        mIsDetectingGesture = false;
8107c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        if (!sInGesture) {
8117c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka            return;
8127c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        }
8138e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sInGesture = false;
8148e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        if (DEBUG_LISTENER) {
8158e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
8168e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        }
8178e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        mListener.onCancelBatchInput();
8188e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    }
8198e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka
820694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
821694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
8228ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
8238ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
8248ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
8258ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
8268ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8278ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
8288ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
8298ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
8308ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8318ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
8323314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
8338ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8348ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
8358ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
8368ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8378ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
8388ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
8398ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
840694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long eventTime,
841694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
842694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
843dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
844694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
845f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
84663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
847f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
848f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
849baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
8508a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
851160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
852b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distance = getDistance(x, y, mLastX, mLastY);
853b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distance < sParams.mTouchNoiseThresholdDistance) {
854faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
855b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.w(TAG, String.format("[%d] onDownEvent:"
856b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            + " ignore potential noise: time=%d distance=%d",
857b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, deltaT, distance));
8589c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
859b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
8609bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
86113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
862baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
863baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
864baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
865baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
866eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
867b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onActualDownEvent(x, y);
86893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (key != null && key.isModifier()) {
86993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // Before processing a down event of modifier key, all pointers already being
87093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // tracked should be released.
87193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            sPointerTrackerQueue.releaseAllPointers(eventTime);
8721d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
87393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.add(this);
8741d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
8756c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!sShouldHandleGesture) {
8766c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
8776c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
8784df6549c1a5ae2cdc2cdfafdad1ec2a75881134cTadashi G. Takaoka        // A gesture should start only from a non-modifier key.
879b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
880ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                && key != null && !key.isModifier();
881b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
882b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
8836c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sGestureFirstDownTime = eventTime;
8849580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
88558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
8863623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    sTimeRecorder.getLastLetterTypingTime());
887eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
8881d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
8891d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
89035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private boolean isShowingMoreKeysPanel() {
89135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        return (mMoreKeysPanel != null);
89235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
89335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
894694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
895e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
89667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
89732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
898160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
899c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
90032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
9011a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
90213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = false;
903f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
904e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
9051a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
906e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
9071a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
9082a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
909e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
910e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
911996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
912e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
913e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
91429d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            setPressedKeyGraphics(key, eventTime);
9156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
918694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startSlidingKeyInput(final Key key) {
919e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
920f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka            mIsInSlidingKeyInputFromModifier = key.isModifier();
921e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
922996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
923996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
924996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
925f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    private void resetSlidingKeyInput() {
926f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
927f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInputFromModifier = false;
92808d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        mDrawingProxy.dismissSlidingKeyInputPreview();
929f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    }
930f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka
9316c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
93202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
9336c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
9346c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (mIsDetectingGesture) {
935b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            final int beforeLength = mGestureStrokeWithPreviewPoints.getLength();
936b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard(
937b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    x, y, gestureTime, isMajorEvent);
938b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            if (mGestureStrokeWithPreviewPoints.getLength() > beforeLength) {
939b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
940b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            }
9418353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            // If the move event goes out from valid batch input area, cancel batch input.
942b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            if (!onValidArea) {
9438e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka                cancelBatchInput();
944b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                return;
945b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            }
946ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // If the MoreKeysPanel is showing then do not attempt to enter gesture mode. However,
947ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // the gestured touch points are still being recorded in case the panel is dismissed.
948ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (isShowingMoreKeysPanel()) {
949ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                return;
950ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
9511645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            mayStartBatchInput(key);
952157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            if (sInGesture) {
953157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mayUpdateBatchInput(eventTime, key);
9549580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
9559580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
9569580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
9579580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
958694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
959694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
960dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
961694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
96213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
963e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
964694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
965baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
9666c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture && me != null) {
967eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
968eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
969eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
970eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
971eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
972eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
973eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
9746c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
97502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
976eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
977eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
978ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
979ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
980ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedX = mMoreKeysPanel.translateX(x);
981ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedY = mMoreKeysPanel.translateY(y);
982ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
983ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            onMoveKey(x, y);
984ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            mDrawingProxy.showSlidingKeyInputPreview(this);
985ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            return;
986ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
9876c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
9886c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
9896c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
9903c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processSlidingKeyInput(final Key newKey, final int x, final int y,
9913c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime) {
9923c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // This onPress call may have changed keyboard layout. Those cases are detected
9933c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // at {@link #setKeyboard}. In those cases, we should update key according
9943c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // to the new keyboard layout.
9953c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        Key key = newKey;
9963c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
9973c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            key = onMoveKey(x, y);
9983c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
9993c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onMoveToNewKey(key, x, y);
100013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
100113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            return;
100213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        }
10033c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        startLongPressTimer(key);
10043c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        setPressedKeyGraphics(key, eventTime);
10053c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10063c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10073c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
10083c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10093c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10103c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
10113c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " phantom sudden move event (distance=%d) is translated to "
10123c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
10133c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    getDistance(x, y, lastX, lastY),
10143c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
10153c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
10163c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
10173c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // TODO: This should be moved to outside of this nested if-clause?
10189c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
10193c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
10203c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
102135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10223c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10233c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10243c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10253c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
10263c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10273c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10283c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(
10293c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
10303c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float radiusRatio =
10313c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
10323c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    / keyDiagonal;
10333c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
10343c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " bogus down-move-up event (raidus=%.2f key diagonal) is "
10353c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
10363c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mPointerId, radiusRatio,
10373c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
10383c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
10393c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
104035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10413c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10423c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10433c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
1044831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    private void processSildeOutFromOldKey(final Key oldKey) {
1045831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        setReleasedKeyGraphics(oldKey);
104641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        callListenerOnRelease(oldKey, oldKey.mCode, true /* withSliding */);
1047831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        startSlidingKeyInput(oldKey);
1048831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1049831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    }
1050831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka
10513c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void slideFromOldKeyToNewKey(final Key key, final int x, final int y,
10528b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10538b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid in to the new key from the previous key, we must call
10548b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onRelease() first to notify that the previous key has been released, then call
10558b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onPress() to notify that the new key is being pressed.
1056831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
10578b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        startRepeatKey(key);
10588b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
10593c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            processSlidingKeyInput(key, x, y, eventTime);
10602a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
10612a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive touches may be reported as a sudden move by
10622a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // touch panel firmware. This hack detects such cases and translates the move event to
10632a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // successive up and down events.
10642a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // TODO: Should find a way to balance gesture detection and this hack.
10652a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsPhantomSuddenMoveEventHack
10662a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
10672a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
10682a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
10692a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive proximate touches may be reported as a bogus
10702a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // down-move-up event by touch panel firmware. This hack detects such cases and breaks
10712a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // these events into separate up and down events.
10722a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime)
10732a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
10742a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
10752a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
10762a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: If there are currently multiple touches, register the key even if the finger
10772a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // slides off the key. This defends against noise from some touch panels when there are
10782a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // close multiple touches.
10792a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // Caveat: When in chording input mode with a modifier key, we don't use this hack.
108093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        else if (getActivePointerTrackerCount() > 1
10812a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
10822a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (DEBUG_MODE) {
10832a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                Log.w(TAG, String.format("[%d] onMoveEvent:"
10842a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                        + " detected sliding finger while multi touching", mPointerId));
10853c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            }
10862a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
108713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            cancelTrackingForAction();
10882a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
10892a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        } else {
10902a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (!mIsDetectingGesture) {
109113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
10928b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
10932a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
10948b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
10958b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
10968b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
10978b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    private void slideOutFromOldKey(final Key oldKey, final int x, final int y) {
10988b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid out from the previous key, we must call onRelease() to
10998b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // notify that the previous key has been released.
1100831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
11018b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
11028b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onMoveToNewKey(null, x, y);
11038b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        } else {
11048b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (!mIsDetectingGesture) {
110513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
11068b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
11078b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
11088b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
11098b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
11106c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
11118a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
11128a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
1113e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
11148b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        final Key newKey = onMoveKey(x, y);
1115eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11166c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture) {
11176c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
11188b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
11196c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
11206c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
11216c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
11226c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
11236c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
1124eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1125eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11268b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (newKey != null) {
11273c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11288b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
11293c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            } else if (oldKey == null) {
1130831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
1131831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
1132831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                processSlidingKeyInput(newKey, x, y, eventTime);
1133c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
1134831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        } else { // newKey == null
11358b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11368b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideOutFromOldKey(oldKey, x, y);
113707221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
11386a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1139547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        mDrawingProxy.showSlidingKeyInputPreview(this);
11406a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11416a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1142694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onUpEvent(final int x, final int y, final long eventTime) {
1143694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1144dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
1145694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
11461d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1147915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        mTimerProxy.cancelUpdateBatchInputTimer(this);
114893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (!sInGesture) {
114993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            if (mCurrentKey != null && mCurrentKey.isModifier()) {
115093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
115193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // tracked should be released.
115293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
115393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            } else {
115493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
11551d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
11561d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
115735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
115893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
11591d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
11601d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1161d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
1162d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
1163d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
11640cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
1165694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
1166694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1167547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka            printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
1168694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
116935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
117035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
117135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
117235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(mLastX, mLastY, eventTime);
117313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
11741d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
11751d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
117635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private void onUpEventInternal(final int x, final int y, final long eventTime) {
11772321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1178e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
117941016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        final boolean isInSlidingKeyInputFromModifier = mIsInSlidingKeyInputFromModifier;
1180f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
11816c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
11826c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
11836c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
11845a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
11856c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        setReleasedKeyGraphics(currentKey);
118635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
118735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
118813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            if (!mIsTrackingForActionDisabled) {
118935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedX = mMoreKeysPanel.translateX(x);
119035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedY = mMoreKeysPanel.translateY(y);
119135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
119235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            }
119335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
119435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel = null;
119535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
11969ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
1197eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11986c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
11996c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
120041016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.mCode, true /* withSliding */);
1201eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
120229d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            mayEndBatchInput(eventTime);
1203eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
1204eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
120558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
120613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
1207d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
1208694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1209e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (currentKey != null && currentKey.isRepeatable() && !isInSlidingKeyInput) {
1210e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka            // Repeatable key has been registered in {@link #onDownEventInternal(int,int,long)}.
1211e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka            return;
12126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1213e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
121441016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (isInSlidingKeyInputFromModifier) {
121541016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            callListenerOnFinishSlidingInput();
121641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
12176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
121935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    public void onShowMoreKeysPanel(final int translatedX, final int translatedY,
122035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final MoreKeysPanel panel) {
122135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        setReleasedKeyGraphics(mCurrentKey);
122235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final long eventTime = SystemClock.uptimeMillis();
122335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel = panel;
122435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, eventTime);
12259ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
12269ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
1227b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    @Override
122813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    public void cancelTrackingForAction() {
12298353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
12308353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            return;
12318353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        }
123213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = true;
1233b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    }
1234b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
1235906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
1236547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        resetSlidingKeyInput();
123713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
1238e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
123993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
1240d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
1241d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
1242694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onCancelEvent(final int x, final int y, final long eventTime) {
1243694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1244dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
1245694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
12461d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
12477c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        cancelBatchInput();
12488e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.cancelAllPointerTracker();
12498e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.releaseAllPointers(eventTime);
1250baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
12511d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12521d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1253baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
12542321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1255e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
1256f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
1257ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
1258ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
1259ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel = null;
1260ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
12616a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12626a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1263694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
1264ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (sInGesture) return;
1265ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (key == null) return;
1266ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        if (!key.isRepeatable()) return;
1267ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        // Don't start key repeat when we are in sliding input mode.
1268e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (mIsInSlidingKeyInput) return;
1269ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        onRegisterKey(key);
1270ad662adeef10c4b202f8f5f80037dd5b8fc9583dTadashi G. Takaoka        mTimerProxy.startKeyRepeatTimer(this);
1271a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
1272a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
1273694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onRegisterKey(final Key key) {
1274c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
12753623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
1276d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
1277c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
12786a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12796a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1280b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
1281b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final Key newKey) {
1282694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyDetector == null) {
1283a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
1284694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1285694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
12866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1287e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1288a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1289a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (curKey == null /* && newKey != null */) {
1290a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1291a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1292a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        // Here curKey points to the different key from newKey.
1293a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
1294a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                mIsInSlidingKeyInputFromModifier);
1295a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
1296a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
1297a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1298a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
1299a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        / mKeyboard.mMostCommonKeyWidth;
1300a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1301a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
1302b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1303a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1304a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1305a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
1306a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && sTimeRecorder.isInFastTyping(eventTime)
1307a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
1308a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1309a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float keyDiagonal = (float)Math.hypot(
1310a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
1311a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float lengthFromDownRatio =
1312a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
1313a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1314a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        + " %.2f key diagonal from virtual down point",
1315a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mPointerId, lengthFromDownRatio));
1316b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1317e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
13186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1319a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        return false;
13206a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13216a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1322694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
132343ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (sInGesture) return;
132443ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (key == null) return;
132543ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (!key.isLongPressEnabled()) return;
132643ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // Caveat: Please note that isLongPressEnabled() can be true even if the current key
132743ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // doesn't have its more keys. (e.g. spacebar, globe key)
132843ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // We always need to start the long press timer if the key has its more keys regardless of
132943ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // whether or not we are in the sliding input mode.
1330e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        if (mIsInSlidingKeyInput && key.mMoreKeys == null) return;
133143ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        mTimerProxy.startLongPressTimer(this);
133266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
133366e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
13343623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
133583e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
13368aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1337dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1338dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
133966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
1340694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final int code = key.mCode;
13413623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        callListenerOnCodeInput(key, code, x, y, eventTime);
134241016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        callListenerOnRelease(key, code, false /* withSliding */);
13436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1345694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1346694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1347723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1348e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
134958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
135013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                (mIsTrackingForActionDisabled ? "-" : " "), title, x, y, eventTime, code));
1351dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
13526e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1353