PointerTracker.java revision 8353e751cae4a26d186fb645e9d3d40e1bc5d14b
16a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka/*
28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2010 The Android Open Source Project
36a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
46a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); you may not
56a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * use this file except in compliance with the License. You may obtain a copy of
66a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * the License at
76a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
86a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0
96a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
106a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
136a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * License for the specific language governing permissions and limitations under
146a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * the License.
156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka */
166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
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;
2872934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
295f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaokaimport com.android.inputmethod.latin.CollectionUtils;
30240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaokaimport com.android.inputmethod.latin.Constants;
31547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaokaimport com.android.inputmethod.latin.CoordinateUtils;
32f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.latin.InputPointers;
33faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
345509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport com.android.inputmethod.latin.R;
359bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
366b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
385c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
39dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
40a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class PointerTracker implements PointerTrackerQueue.Element {
41dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
42dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
43dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
44dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
45b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG || DEBUG_EVENT;
4640a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
470657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    /** True if {@link PointerTracker}s should handle gesture events. */
480657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    private static boolean sShouldHandleGesture = false;
498335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sMainDictionaryAvailable = false;
508335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByInputField = false;
518335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByUser = false;
52918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
53f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
54f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
55f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
67f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
68f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
69f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
70f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
71f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
72f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
73f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
74f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
75f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
76f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
77f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
78f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
79f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
80fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    public interface DrawingProxy {
816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
82e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
83d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
84547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        public void showSlidingKeyInputPreview(PointerTracker tracker);
8508d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        public void dismissSlidingKeyInputPreview();
86f0e83046f022b5594f98aef07fbda7f2f837f262Tadashi G. Takaoka        public void showGesturePreviewTrail(PointerTracker tracker, boolean isOldestTracker);
876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
892321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
90d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        public void startTypingStateTimer(Key typedKey);
9173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
92a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
93a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker);
94a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code);
9598b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
960ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer();
97beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer();
980ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout();
992321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
10072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        public void startUpdateBatchInputTimer(PointerTracker tracker);
1012db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka        public void cancelAllUpdateBatchInputTimers();
10229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
10329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
10429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
105d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            public void startTypingStateTimer(Key typedKey) {}
10693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
10773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
10893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
109a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker) {}
11029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
111a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(PointerTracker tracker) {}
112a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            @Override
113a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(int code) {}
11429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
11529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelLongPressTimer() {}
11629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
1170ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public void startDoubleTapTimer() {}
1180ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
119beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            public void cancelDoubleTapTimer() {}
120beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            @Override
1210ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public boolean isInDoubleTapTimeout() { return false; }
1220ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
12329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelKeyTimers() {}
12472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            @Override
12572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            public void startUpdateBatchInputTimer(PointerTracker tracker) {}
12672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            @Override
1272db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka            public void cancelAllUpdateBatchInputTimers() {}
12829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
1292321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    }
1302321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka
131a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    static final class PointerTrackerParams {
1325509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final boolean mSlidingKeyInputEnabled;
1335509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
134b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public final int mTouchNoiseThresholdDistance;
1353623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public final int mSuppressKeyPreviewAfterBatchInputDuration;
1365509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1375509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
1385509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1395509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private PointerTrackerParams() {
1405509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
1415509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = 0;
142b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = 0;
1433623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = 0;
1445509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1455509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
14680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
1475509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
1485509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
1495509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
1505509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
151b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
1525509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
1533623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
1543623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
1555509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1565509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    }
1575509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
158160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
1595509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    private static PointerTrackerParams sParams;
16080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    private static GestureStrokeParams sGestureStrokeParams;
161d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
162b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // Move this threshold to resource.
163b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // TODO: Device specific parameter would be better for device specific hack?
164b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
165b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // This hack might be device specific.
166b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final boolean sNeedsProximateBogusDownMoveUpEventHack = true;
1675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1685f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
16993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
1705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1730efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1740efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
175a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
176dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private KeyboardActionListener mListener = EMPTY_LISTENER;
177baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1785a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
179b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private int mPhantonSuddenMoveThreshold;
180b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
1816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1826c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
1836c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
1846c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sGestureFirstDownTime;
1853623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private static TimeRecorder sTimeRecorder;
1866c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static final InputPointers sAggregratedPointers = new InputPointers(
1876c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            GestureStroke.DEFAULT_CAPACITY);
18858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
18958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
1909580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
191b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static final class BogusMoveEventDetector {
192b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Move these thresholds to resource.
193d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        // These thresholds' unit is a diagonal length of a key.
194d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
195d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
196b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
197b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mAccumulatedDistanceThreshold;
198b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mRadiusThreshold;
199b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
200b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Accumulated distance from actual and artificial down keys.
201b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int mAccumulatedDistanceFromDownKey;
202b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownX;
203b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownY;
204b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
205d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
206d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
207b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceThreshold = (int)(
208d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
209d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
210b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
211b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
212b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onActualDownEvent(final int x, final int y) {
213b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownX = x;
214b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownY = y;
215b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
216b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
217b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onDownKey() {
218b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey = 0;
219b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
220b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
221b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onMoveKey(final int distance) {
222b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey += distance;
223b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
224b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
225d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public boolean hasTraveledLongDistance(final int x, final int y) {
226d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dx = Math.abs(x - mActualDownX);
227d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dy = Math.abs(y - mActualDownY);
228d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // A bogus move event should be a horizontal movement. A vertical movement might be
229d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // a sloppy typing and should be ignored.
230d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
231b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
232b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
233b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int getDistanceFromDownEvent(final int x, final int y) {
234b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistance(x, y, mActualDownX, mActualDownY);
235b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
236b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
237b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isCloseToActualDownEvent(final int x, final int y) {
238b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
239b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
240b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
241b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2423623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    static final class TimeRecorder {
2433623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mSuppressKeyPreviewAfterBatchInputDuration;
2443623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mStaticTimeThresholdAfterFastTyping; // msec
2453623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastTypingTime;
2463623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastLetterTypingTime;
2473623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastBatchInputTime;
2483623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2493623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
2503623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                final GestureStrokeParams gestureStrokeParams) {
2513623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration =
2523623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
2533623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping =
2543623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
2553623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2563623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
257b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isInFastTyping(final long eventTime) {
258b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
259b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
260b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
261b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2627a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        private boolean wasLastInputTyping() {
2637a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return mLastTypingTime >= mLastBatchInputTime;
2643623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2653623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2663623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onCodeInput(final int code, final long eventTime) {
2677a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // Record the letter typing time when
2687a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 1. Letter keys are typed successively without any batch input in between.
2697a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 2. A letter key is typed within the threshold time since the last any key typing.
2707a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 3. A non-letter key is typed within the threshold time since the last letter key
2717a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // typing.
2727a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (Character.isLetter(code)) {
2737a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                if (wasLastInputTyping()
2747a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                        || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
2757a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2763623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2773623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            } else {
2783623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
2793623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    // This non-letter typing should be treated as a part of fast typing.
2807a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2813623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2823623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            }
2837a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastTypingTime = eventTime;
2843623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2853623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2863623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onEndBatchInput(final long eventTime) {
2877a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastBatchInputTime = eventTime;
2883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public long getLastLetterTypingTime() {
2913623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            return mLastLetterTypingTime;
2923623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2933623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2943623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
2957a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return !wasLastInputTyping()
2963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
2973623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2983623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    }
2993623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
3008a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
3018a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
302547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    private int[] mDownCoordinates = CoordinateUtils.newInstance();
3038a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
3048a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
305e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
306e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
307e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
3088a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
3098a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
3108a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3118a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
3128a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
3138a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
3146a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3151a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
3161a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
3171a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
318b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    // true if this pointer is no longer tracking touch event.
319b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    private boolean mIsTrackingCanceled;
320c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
32135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    // the more keys panel currently being shown. equals null if no panel is active.
32235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private MoreKeysPanel mMoreKeysPanel;
3239ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
324f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input.
3255c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
326f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input from a modifier key,
327f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // so that further modifier keys should be ignored.
328f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    boolean mIsInSlidingKeyInputFromModifier;
329cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
330f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if a sliding key input is allowed.
33167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
33267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
333dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    // Empty {@link KeyboardActionListener}
334e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka    private static final KeyboardActionListener EMPTY_LISTENER =
335e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka            new KeyboardActionListener.Adapter();
3366e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka
337c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
338f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
33993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    public static void init(final boolean needsPhantomSuddenMoveEventHack) {
340d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
3415509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = PointerTrackerParams.DEFAULT;
34280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = GestureStrokeParams.DEFAULT;
3433623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
344160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
34593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3465509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    public static void setParameters(final TypedArray mainKeyboardViewAttr) {
3475509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
34880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
3493623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
3506a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3528335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
3538335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
3548335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
3558335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
3568335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
3578335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3588335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
3598335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
360694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
3618335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
3628335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
3638335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3648335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
365694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
3668335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
3678335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
368918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
369918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
370694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
371b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
3725c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
3745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
3755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
3765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
3775c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3785c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3795c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
3805c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
38393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.isAnyInSlidingKeyInput();
3845c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3855c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
386694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
387afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
388afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
389afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
3905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
3915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3925c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
394694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
395afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
396afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
397afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
3985c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
3995c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
4005c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
4015c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
402918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
4038335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
4048335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
4055c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4065c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
407dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
408afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
409afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
410afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
411e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
4125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4135c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
415ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    public static void dismissAllMoreKeysPanels() {
416ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        final int trackersSize = sTrackers.size();
417ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        for (int i = 0; i < trackersSize; ++i) {
418ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            final PointerTracker tracker = sTrackers.get(i);
419ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (tracker.isShowingMoreKeysPanel()) {
420ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel.dismissMoreKeysPanel();
421ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                tracker.mMoreKeysPanel = null;
422ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
423ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
424ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    }
425ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
426694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private PointerTracker(final int id, final KeyEventHandler handler) {
427694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (handler == null) {
4285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
429694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
4305c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
43180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
43280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                id, sGestureStrokeParams);
4335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
4345c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
4355c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
4365c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
4376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4386a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
4391a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
440694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
4416c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
442eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
443eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
444f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
445e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
44658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onPress    : %s%s%s", mPointerId,
44758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    KeyDetector.printableCode(key),
44858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ignoreModifierKey ? " ignoreModifier" : "",
44958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ? "" : " disabled"));
450e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
45193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
452996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
45393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
454e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
4552a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onPressKey(key.mCode);
456690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
457690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
458d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
459690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
460690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
461690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
462dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
463dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
464690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
465690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
466694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
4673623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            final int y, final long eventTime) {
468f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
4696bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
47029d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
4712013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
472240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            final String output = code == Constants.CODE_OUTPUT_TEXT
473240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    ? key.getOutputText() : Constants.printableCode(code);
47458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
47558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    output, ignoreModifierKey ? " ignoreModifier" : "",
47658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
4772013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
4789bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
4799bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
4809bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
4819bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
482e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
483996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
484e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
4856bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
4866bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
4873623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            sTimeRecorder.onCodeInput(code, eventTime);
488240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
48929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                mListener.onTextInput(key.getOutputText());
490240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            } else if (code != Constants.CODE_UNSPECIFIED) {
491ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
4922013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
49393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
494dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
495dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
496f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may be in shifted state and the
497690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
498694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
499694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
5006c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
501eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
502eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
503f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
504e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
50558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
506240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    Constants.printableCode(primaryCode),
50758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
50858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ?  "": " disabled"));
509e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
5109bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
5119bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
5129bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
5139bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
514e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
515996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
516e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
51793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
5182a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
51993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
520dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
521dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
5228aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
523694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
52458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
525694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
5269bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
5279bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
5289bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5298aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
530dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
531dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
532694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
53358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
53458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
53558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return;
53658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
537a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
5385a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
539d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyWidth = mKeyboard.mMostCommonKeyWidth;
540d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyHeight = mKeyboard.mMostCommonKeyHeight;
541b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
5428a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
5438a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
5448a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
5458a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
5468a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
54744972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
5488a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
549b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
550d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
5515a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
5525a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
5530cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
554cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
555cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
556cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
557cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
558547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public boolean isInSlidingKeyInputFromModifier() {
559547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        return mIsInSlidingKeyInputFromModifier;
560547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
561547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
562e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
563e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
564dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
565dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
5660cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
5672aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
568e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
5692aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
5702aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
571694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
572723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
5732aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
5742aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
575694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setReleasedKeyGraphics(final Key key) {
576d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
5776bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
578faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
579faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
580faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
5816bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
582faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
583faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
584faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
585faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
586faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
587faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
5882013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
5892013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
590faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
5912013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
592faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
59329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
594faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
595faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
596faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
597faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
598faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
59929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
600faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
6012013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6022013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
6036a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6056a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
60629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
60729d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!sShouldHandleGesture) return false;
6083623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
60929d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    }
61029d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka
61129d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void setPressedKeyGraphics(final Key key, final long eventTime) {
6126bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
6136bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
6146bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
6156bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
6166bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
6176bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
6186bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
6196bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
620faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
621faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
622faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
62329d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
624faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
625faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
626faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
627faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
628faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
629faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
630faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
631faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
6322013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6332013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
634faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6352013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
63673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
63729d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
638faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
639faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
640faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
641faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
642faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
64329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
644faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
6452013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6462013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
647d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
648c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
649c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
650694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updateReleaseKeyGraphics(final Key key) {
651faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
652faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
653faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
654faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
655694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updatePressKeyGraphics(final Key key) {
656faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
657faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
658faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
659faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
660c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
661c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return mGestureStrokeWithPreviewPoints;
6622f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
6632f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
664547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getLastCoordinates(final int[] outCoords) {
665547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(outCoords, mLastX, mLastY);
6668a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6678a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
6688a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
6698a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
6708a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6718a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
672547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    public void getDownCoordinates(final int[] outCoords) {
673547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.copy(outCoords, mDownCoordinates);
674547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
675547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
676694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
6778a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
678547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(mDownCoordinates, x, y);
679b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onDownKey();
6808a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
6818a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6828a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
683b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static int getDistance(final int x1, final int y1, final int x2, final int y2) {
684b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        return (int)Math.hypot(x1 - x2, y1 - y2);
685b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
686b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
687694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
688b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
6898a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
6908a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
691723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
6928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6938a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
694694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
6958a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
6968a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6978a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
698694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
699e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
7008a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
7018a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
702e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
7038a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
7048a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
7056c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int getActivePointerTrackerCount() {
70693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.size();
7076c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7086c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
70913ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    private static boolean isOldestTrackerInQueue(final PointerTracker tracker) {
71093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.getOldestElement() == tracker;
71113ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    }
71213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka
7131645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka    private void mayStartBatchInput(final Key key) {
714c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
7159c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            return;
7169c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        }
7171645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        if (key == null || !Character.isLetter(key.mCode)) {
7181645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            return;
7191645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        }
7209c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
72158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
7229580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7239c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        sInGesture = true;
72458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
72558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sAggregratedPointers.reset();
72658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionPointSize = 0;
72758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionTime = 0;
72858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mListener.onStartBatchInput();
729ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            dismissAllMoreKeysPanels();
73058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
731ad181915f78235bc09e88c85ed9df669801b8442Tadashi G. Takaoka        mTimerProxy.cancelLongPressTimer();
73213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
7336c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7346c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
73572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    public void updateBatchInputByTimer(final long eventTime) {
73672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
73772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        mGestureStrokeWithPreviewPoints.duplicateLastPointWith(gestureTime);
73872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        updateBatchInput(eventTime);
73972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
74072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
741157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka    private void mayUpdateBatchInput(final long eventTime, final Key key) {
742157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka        if (key != null) {
74372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            updateBatchInput(eventTime);
74472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
7456f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        if (mIsTrackingCanceled) {
7466f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
7476f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
74872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
74972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
75072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
75172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    private void updateBatchInput(final long eventTime) {
75272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
75372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
75472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            stroke.appendIncrementalBatchPoints(sAggregratedPointers);
75572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int size = sAggregratedPointers.getPointerSize();
75672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            if (size > sLastRecognitionPointSize
75772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
75872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                sLastRecognitionPointSize = size;
75972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                sLastRecognitionTime = eventTime;
76072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                if (DEBUG_LISTENER) {
76172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
76272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                            size));
7636c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
76472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mTimerProxy.startUpdateBatchInputTimer(this);
76572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                mListener.onUpdateBatchInput(sAggregratedPointers);
7666c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
7679580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7689580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7699580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
77029d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void mayEndBatchInput(final long eventTime) {
7716c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        synchronized (sAggregratedPointers) {
772c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
7736c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
7746c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sInGesture = false;
7753623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                sTimeRecorder.onEndBatchInput(eventTime);
776b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka                mTimerProxy.cancelAllUpdateBatchInputTimers();
777b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                if (!mIsTrackingCanceled) {
778b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    if (DEBUG_LISTENER) {
779b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                        Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
780b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                                mPointerId, sAggregratedPointers.getPointerSize()));
781b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    }
782b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    mListener.onEndBatchInput(sAggregratedPointers);
783b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                }
7846c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
7859580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7866f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        if (mIsTrackingCanceled) {
7876f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
7886f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
78913ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
7909580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7919580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
7928e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    private void cancelBatchInput() {
7938e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.cancelAllPointerTracker();
7948353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        mIsDetectingGesture = false;
7957c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        if (!sInGesture) {
7967c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka            return;
7977c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        }
7988e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sInGesture = false;
7998e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        if (DEBUG_LISTENER) {
8008e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
8018e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        }
8028e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        mListener.onCancelBatchInput();
8038e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    }
8048e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka
805694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
806694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
8078ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
8088ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
8098ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
8108ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
8118ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8128ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
8138ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
8148ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
8158ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8168ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
8173314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
8188ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8198ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
8208ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
8218ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
8228ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
8238ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
8248ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
825694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long eventTime,
826694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
827694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
828dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
829694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
830f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
83163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
832f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
833f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
834baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
8358a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
836160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
837b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distance = getDistance(x, y, mLastX, mLastY);
838b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distance < sParams.mTouchNoiseThresholdDistance) {
839faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
840b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.w(TAG, String.format("[%d] onDownEvent:"
841b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            + " ignore potential noise: time=%d distance=%d",
842b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, deltaT, distance));
8439bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
844b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
8459bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
846b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                cancelTracking();
847baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
848baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
849baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
850baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
851eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
852b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onActualDownEvent(x, y);
85393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (key != null && key.isModifier()) {
85493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // Before processing a down event of modifier key, all pointers already being
85593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // tracked should be released.
85693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            sPointerTrackerQueue.releaseAllPointers(eventTime);
8571d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
85893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.add(this);
8591d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
8606c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!sShouldHandleGesture) {
8616c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
8626c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
8634df6549c1a5ae2cdc2cdfafdad1ec2a75881134cTadashi G. Takaoka        // A gesture should start only from a non-modifier key.
864b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
865ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                && key != null && !key.isModifier();
866b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
867b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
8686c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sGestureFirstDownTime = eventTime;
8699580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
87058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
8713623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    sTimeRecorder.getLastLetterTypingTime());
872eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
8731d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
8741d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
87535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private boolean isShowingMoreKeysPanel() {
87635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        return (mMoreKeysPanel != null);
87735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
87835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
879694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
880e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
88167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
88232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
883160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
884c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
88532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
8861a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
887b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mIsTrackingCanceled = false;
888f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
889e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
8901a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
891e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
8921a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
8932a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
894e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
895e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
896996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
897e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
898e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
89929d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            setPressedKeyGraphics(key, eventTime);
9006a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9016a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9026a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
903694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startSlidingKeyInput(final Key key) {
904e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
905f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka            mIsInSlidingKeyInputFromModifier = key.isModifier();
906e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
907996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
908996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
909996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
910f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    private void resetSlidingKeyInput() {
911f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
912f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInputFromModifier = false;
91308d8a676c28f30a722629cb4713177064f6422e2Tadashi G. Takaoka        mDrawingProxy.dismissSlidingKeyInputPreview();
914f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    }
915f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka
9166c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
91702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
9186c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
9196c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (mIsDetectingGesture) {
920b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard(
921b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    x, y, gestureTime, isMajorEvent);
9228353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            // If the move event goes out from valid batch input area, cancel batch input.
923b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            if (!onValidArea) {
9248e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka                cancelBatchInput();
925b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                return;
926b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            }
927ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // If the MoreKeysPanel is showing then do not attempt to enter gesture mode. However,
928ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // the gestured touch points are still being recorded in case the panel is dismissed.
929ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            if (isShowingMoreKeysPanel()) {
930ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang                return;
931ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            }
9321645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            mayStartBatchInput(key);
933157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            if (sInGesture) {
934157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mayUpdateBatchInput(eventTime, key);
9359580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
9369580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
9379580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
9389580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
939694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
940694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
941dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
942694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
943b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        if (mIsTrackingCanceled) {
944e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
945694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
946baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
94735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
94835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            final int translatedX = mMoreKeysPanel.translateX(x);
94935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            final int translatedY = mMoreKeysPanel.translateY(y);
95035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
95135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
95235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
9536c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture && me != null) {
954eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
955eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
956eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
957eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
958eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
959eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
960eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
9616c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
96202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
963eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
964eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
965ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
966ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
967ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            // Do not handle sliding keys (or show key pop-ups) when the MoreKeysPanel is visible.
968ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            return;
969ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
9706c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
9716c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
9726c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
9733c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processSlidingKeyInput(final Key newKey, final int x, final int y,
9743c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime) {
9753c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // This onPress call may have changed keyboard layout. Those cases are detected
9763c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // at {@link #setKeyboard}. In those cases, we should update key according
9773c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // to the new keyboard layout.
9783c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        Key key = newKey;
9793c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
9803c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            key = onMoveKey(x, y);
9813c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
9823c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onMoveToNewKey(key, x, y);
9833c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        startLongPressTimer(key);
9843c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        setPressedKeyGraphics(key, eventTime);
9853c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
9863c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
9873c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
9883c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
9893c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
9903c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
9913c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " phantom sudden move event (distance=%d) is translated to "
9923c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
9933c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    getDistance(x, y, lastX, lastY),
9943c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
9953c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
9963c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
9973c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // TODO: This should be moved to outside of this nested if-clause?
9983c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (ProductionFlag.IS_EXPERIMENTAL) {
9993c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
10003c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
100135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10023c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10033c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10043c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
10053c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
10063c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10073c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
10083c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(
10093c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
10103c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float radiusRatio =
10113c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
10123c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    / keyDiagonal;
10133c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
10143c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " bogus down-move-up event (raidus=%.2f key diagonal) is "
10153c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
10163c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mPointerId, radiusRatio,
10173c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
10183c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
10193c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
102035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
10213c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
10223c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
10233c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
1024831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    private void processSildeOutFromOldKey(final Key oldKey) {
1025831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        setReleasedKeyGraphics(oldKey);
1026831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        callListenerOnRelease(oldKey, oldKey.mCode, true);
1027831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        startSlidingKeyInput(oldKey);
1028831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1029831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    }
1030831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka
10313c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void slideFromOldKeyToNewKey(final Key key, final int x, final int y,
10328b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
10338b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid in to the new key from the previous key, we must call
10348b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onRelease() first to notify that the previous key has been released, then call
10358b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onPress() to notify that the new key is being pressed.
1036831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
10378b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        startRepeatKey(key);
10388b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
10393c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            processSlidingKeyInput(key, x, y, eventTime);
10402a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
10412a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive touches may be reported as a sudden move by
10422a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // touch panel firmware. This hack detects such cases and translates the move event to
10432a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // successive up and down events.
10442a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // TODO: Should find a way to balance gesture detection and this hack.
10452a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsPhantomSuddenMoveEventHack
10462a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
10472a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
10482a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
10492a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive proximate touches may be reported as a bogus
10502a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // down-move-up event by touch panel firmware. This hack detects such cases and breaks
10512a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // these events into separate up and down events.
10522a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime)
10532a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
10542a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
10552a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
10562a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: If there are currently multiple touches, register the key even if the finger
10572a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // slides off the key. This defends against noise from some touch panels when there are
10582a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // close multiple touches.
10592a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // Caveat: When in chording input mode with a modifier key, we don't use this hack.
106093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        else if (getActivePointerTrackerCount() > 1
10612a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
10622a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (DEBUG_MODE) {
10632a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                Log.w(TAG, String.format("[%d] onMoveEvent:"
10642a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                        + " detected sliding finger while multi touching", mPointerId));
10653c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            }
10662a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
1067b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            cancelTracking();
10682a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
10692a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        } else {
10702a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (!mIsDetectingGesture) {
1071b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                cancelTracking();
10728b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
10732a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
10748b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
10758b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
10768b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
10778b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    private void slideOutFromOldKey(final Key oldKey, final int x, final int y) {
10788b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid out from the previous key, we must call onRelease() to
10798b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // notify that the previous key has been released.
1080831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
10818b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
10828b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onMoveToNewKey(null, x, y);
10838b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        } else {
10848b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (!mIsDetectingGesture) {
1085b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                cancelTracking();
10868b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
10878b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
10888b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
10898b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
10906c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
10918a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
10928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
1093e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
10948b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        final Key newKey = onMoveKey(x, y);
1095eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
10966c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture) {
10976c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
10988b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
10996c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
11006c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
11016c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
11026c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
11036c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
1104eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1105eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11068b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (newKey != null) {
11073c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11088b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
11093c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            } else if (oldKey == null) {
1110831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
1111831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
1112831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                processSlidingKeyInput(newKey, x, y, eventTime);
1113c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
1114831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        } else { // newKey == null
11158b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
11168b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideOutFromOldKey(oldKey, x, y);
111707221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
11186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1119547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        mDrawingProxy.showSlidingKeyInputPreview(this);
11206a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11216a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1122694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onUpEvent(final int x, final int y, final long eventTime) {
1123694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1124dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
1125694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
11261d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
112793b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (!sInGesture) {
112893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            if (mCurrentKey != null && mCurrentKey.isModifier()) {
112993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
113093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // tracked should be released.
113193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
113293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            } else {
113393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
11341d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
11351d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
113635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
113793b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
11381d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
11391d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1140d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
1141d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
1142d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
11430cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
1144694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
1145694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1146547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka            printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
1147694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
114835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
114935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
115035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
115135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(mLastX, mLastY, eventTime);
1152b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        cancelTracking();
11531d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
11541d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
115535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private void onUpEventInternal(final int x, final int y, final long eventTime) {
11562321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1157f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
11586c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
11596c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
11606c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
11615a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
11626c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        setReleasedKeyGraphics(currentKey);
116335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
116435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
116535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            if (!mIsTrackingCanceled) {
116635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedX = mMoreKeysPanel.translateX(x);
116735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedY = mMoreKeysPanel.translateY(y);
116835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
116935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            }
117035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
117135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mMoreKeysPanel = null;
117235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
11739ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
1174eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
11756c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
11766c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
11776c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.mCode, true);
1178eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
117929d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            mayEndBatchInput(eventTime);
1180eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
1181eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
118258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
1183b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        if (mIsTrackingCanceled) {
1184d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
1185694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
11866c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (currentKey != null && !currentKey.isRepeatable()) {
11873623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
11886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
11896a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
119135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    public void onShowMoreKeysPanel(final int translatedX, final int translatedY,
119235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final MoreKeysPanel panel) {
119335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        setReleasedKeyGraphics(mCurrentKey);
119435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final long eventTime = SystemClock.uptimeMillis();
119535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel = panel;
119635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mMoreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, eventTime);
11979ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
11989ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
1199b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    @Override
1200b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public void cancelTracking() {
12018353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
12028353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            return;
12038353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        }
1204b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mIsTrackingCanceled = true;
1205b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    }
1206b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
1207906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
1208547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        resetSlidingKeyInput();
1209b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        cancelTracking();
1210e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
121193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
1212d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
1213d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
1214694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onCancelEvent(final int x, final int y, final long eventTime) {
1215694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1216dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
1217694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
12181d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
12197c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        cancelBatchInput();
12208e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.cancelAllPointerTracker();
12218e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.releaseAllPointers(eventTime);
1222baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
12231d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
12241d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1225baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
12262321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1227e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
1228f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
1229ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
1230ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel.dismissMoreKeysPanel();
1231ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            mMoreKeysPanel = null;
1232ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
12336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1235694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
12366c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (key != null && key.isRepeatable() && !sInGesture) {
12376662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv            onRegisterKey(key);
1238a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startKeyRepeatTimer(this);
1239a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        }
1240a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
1241a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
1242694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onRegisterKey(final Key key) {
1243c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
12443623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
1245d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
1246c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
12476a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1249b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
1250b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final Key newKey) {
1251694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyDetector == null) {
1252a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
1253694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1254694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
12556a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1256e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1257a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1258a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (curKey == null /* && newKey != null */) {
1259a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1260a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1261a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        // Here curKey points to the different key from newKey.
1262a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
1263a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                mIsInSlidingKeyInputFromModifier);
1264a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
1265a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
1266a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1267a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
1268a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        / mKeyboard.mMostCommonKeyWidth;
1269a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1270a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
1271b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1272a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1273a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1274a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
1275a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && sTimeRecorder.isInFastTyping(eventTime)
1276a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
1277a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1278a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float keyDiagonal = (float)Math.hypot(
1279a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
1280a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float lengthFromDownRatio =
1281a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
1282a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1283a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        + " %.2f key diagonal from virtual down point",
1284a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mPointerId, lengthFromDownRatio));
1285b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1286e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
12876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1288a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        return false;
12896a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1291694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
12926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (key != null && key.isLongPressEnabled() && !sInGesture) {
1293a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startLongPressTimer(this);
12944189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        }
129566e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
129666e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
12973623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
129883e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
12998aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1300dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1301dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
130266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
1303694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final int code = key.mCode;
13043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        callListenerOnCodeInput(key, code, x, y, eventTime);
13052013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        callListenerOnRelease(key, code, false);
13066a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
13076a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1308694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1309694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1310723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1311e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
131258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
1313b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                (mIsTrackingCanceled ? "-" : " "), title, x, y, eventTime, code));
1314dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
13156e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1316