PointerTracker.java revision a28a05e971cc242b338331a3b78276fa95188d19
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;
26c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
2772934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
285f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaokaimport com.android.inputmethod.latin.CollectionUtils;
29f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.latin.InputPointers;
30faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
315509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport com.android.inputmethod.latin.R;
329bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
336b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
355c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
36dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
37a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class PointerTracker implements PointerTrackerQueue.Element {
38dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
39dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
40dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
41dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
42faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
4340a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
440657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    /** True if {@link PointerTracker}s should handle gesture events. */
450657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    private static boolean sShouldHandleGesture = false;
468335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sMainDictionaryAvailable = false;
478335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByInputField = false;
488335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByUser = false;
499f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka    private static boolean sGestureOffWhileFastTyping = false;
50918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
51f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
52f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
53f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
54f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
55f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
67f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
68f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
69f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
70f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
71f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
72f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
73f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
74f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
75f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
76f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
77f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
789d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public interface DrawingProxy extends MoreKeysPanel.Controller {
796a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
80e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
81d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
82f0e83046f022b5594f98aef07fbda7f2f837f262Tadashi G. Takaoka        public void showGesturePreviewTrail(PointerTracker tracker, boolean isOldestTracker);
836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
852321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
86d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        public void startTypingStateTimer(Key typedKey);
8773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
889f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        public void startGestureOffWhileFastTypingTimer();
89a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
90a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker);
91a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code);
9298b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
930ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer();
94beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer();
950ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout();
962321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
9729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
9829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
9929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
100d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            public void startTypingStateTimer(Key typedKey) {}
10193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
10273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
10393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
1049f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka            public void startGestureOffWhileFastTypingTimer() {}
1059f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka            @Override
106a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker) {}
10729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
108a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(PointerTracker tracker) {}
109a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            @Override
110a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(int code) {}
11129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
11229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelLongPressTimer() {}
11329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
1140ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public void startDoubleTapTimer() {}
1150ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
116beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            public void cancelDoubleTapTimer() {}
117beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            @Override
1180ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public boolean isInDoubleTapTimeout() { return false; }
1190ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
12029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelKeyTimers() {}
12129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
1222321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    }
1232321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka
124a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    static final class PointerTrackerParams {
1255509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final boolean mSlidingKeyInputEnabled;
1265509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
1275509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final float mTouchNoiseThresholdDistance;
1285509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final int mTouchNoiseThresholdDistanceSquared;
1295509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1305509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
1315509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1325509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private PointerTrackerParams() {
1335509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
1345509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = 0;
1355509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdDistance = 0.0f;
1365509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdDistanceSquared = 0;
1375509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1385509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1395509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public PointerTrackerParams(TypedArray mainKeyboardViewAttr) {
1405509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
1415509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
1425509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
1435509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
1445509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            final float touchNouseThresholdDistance = mainKeyboardViewAttr.getDimension(
1455509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
1465509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdDistance = touchNouseThresholdDistance;
1475509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdDistanceSquared =
1485509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    (int)(touchNouseThresholdDistance * touchNouseThresholdDistance);
1495509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1505509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    }
1515509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
152160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
1535509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    private static PointerTrackerParams sParams;
154d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
1555c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1565f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
1575c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static PointerTrackerQueue sPointerTrackerQueue;
1585c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1595c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1606a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1610efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1620efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
163a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
164dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private KeyboardActionListener mListener = EMPTY_LISTENER;
165baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1665a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
167faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private int mKeyQuarterWidthSquared;
1686a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1696c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
1706c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
1716c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sGestureFirstDownTime;
172630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka    private static long sLastLetterTypingUpTime;
1736c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static final InputPointers sAggregratedPointers = new InputPointers(
1746c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            GestureStroke.DEFAULT_CAPACITY);
1756c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int sLastRecognitionPointSize = 0;
1766c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sLastRecognitionTime = 0;
1779580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
1788a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
1798a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
1808a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
1818a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
182e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
183e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
184e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
1858a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
1868a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
1878a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
1888a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
1898a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
1908a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
1916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1921a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
1931a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
1941a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
1959ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    // true if event is already translated to a key action.
196c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    private boolean mKeyAlreadyProcessed;
197c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
1989d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // true if this pointer has been long-pressed and is showing a more keys panel.
1999d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private boolean mIsShowingMoreKeysPanel;
2009ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
201cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    // true if this pointer is in sliding key input
2025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
203cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
20467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    // true if sliding key is allowed.
20567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
20667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
207996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    // ignore modifier key if true
208996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private boolean mIgnoreModifierKey;
209996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
210dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    // Empty {@link KeyboardActionListener}
211e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka    private static final KeyboardActionListener EMPTY_LISTENER =
212e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka            new KeyboardActionListener.Adapter();
2136e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka
214c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
215f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
216d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    public static void init(boolean hasDistinctMultitouch,
21762b8dddb6ddb057555a1665759f9cf197e480c9fTadashi G. Takaoka            boolean needsPhantomSuddenMoveEventHack) {
2185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (hasDistinctMultitouch) {
2195c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = new PointerTrackerQueue();
2205c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        } else {
2215c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = null;
2225c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
223d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
2245509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = PointerTrackerParams.DEFAULT;
225160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
22693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
2275509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    public static void setParameters(final TypedArray mainKeyboardViewAttr) {
2285509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
2296a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
2306a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
2318335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
2328335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
2339f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka                && !sGestureOffWhileFastTyping
2348335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
2358335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
2368335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
2378335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
2388335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
2398335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
240694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
2418335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
2428335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
2438335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
2448335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
245694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
2468335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
2478335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
248918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
249918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
2509f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka    public static void setGestureOffWhileFastTyping() {
2519f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        sGestureOffWhileFastTyping = true;
2529f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        updateGestureHandlingMode();
2539f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka    }
2549f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka
2559f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka    public static void clearGestureOffWhileFastTyping() {
2569f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        sGestureOffWhileFastTyping = false;
2579f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        updateGestureHandlingMode();
2589f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka    }
2599f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka
260694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
261b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
2625c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2635c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
2645c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
2655c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
2665c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
2675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2695c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
2705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2725c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
2735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
2745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
276694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
277afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
278afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
279afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
2805c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
2815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2835c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
284694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
285afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
286afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
287afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
2885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
2895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
2905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
2915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
292918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
2938335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
2948335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
2955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2965c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
297dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
298afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
299afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
300afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
301e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
3025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
305694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private PointerTracker(final int id, final KeyEventHandler handler) {
306694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (handler == null) {
3075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
308694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
3095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
310c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(id);
3115c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
3125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
3135c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
3145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
3156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3171a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
318694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
3196c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
320eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
321eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
322e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
323e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
3247dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            Log.d(TAG, "onPress    : " + KeyDetector.printableCode(key)
3252a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey
326e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled=" + key.isEnabled());
327e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
32893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
329996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
33093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
331e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
3322a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onPressKey(key.mCode);
333690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
334690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
335d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
336690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
337690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
338690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
339dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
340dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
341690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
342690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
343694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
344694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final int y) {
345e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
3466bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
34729d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
3482013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
34929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            Log.d(TAG, "onCodeInput: " + Keyboard.printableCode(code)
35029d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                    + " text=" + key.getOutputText() + " x=" + x + " y=" + y
3516bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
352e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled=" + key.isEnabled());
3532013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
3549bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
3559bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
3569bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
3579bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
358e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
359996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
360e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
3616bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
3626bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
3638cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            if (code == Keyboard.CODE_OUTPUT_TEXT) {
36429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                mListener.onTextInput(key.getOutputText());
3659f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka                mTimerProxy.startGestureOffWhileFastTypingTimer();
3668cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            } else if (code != Keyboard.CODE_UNSPECIFIED) {
367ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
3689f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka                mTimerProxy.startGestureOffWhileFastTypingTimer();
3692013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
37093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
371dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
372dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
373690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
374690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
375694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
376694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
3776c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
378eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
379eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
380e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
381e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
3827dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            Log.d(TAG, "onRelease  : " + Keyboard.printableCode(primaryCode)
383e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
384e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled="+ key.isEnabled());
385e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
3869bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
3879bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
3889bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
3899bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
390e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
391996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
392e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
39393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
3942a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
39593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
396dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
397dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
3988aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
399694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
4008aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            Log.d(TAG, "onCancelInput");
401694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
4029bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
4039bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
4049bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
4058aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
406dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
407dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
408694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
409a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
4105a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
411c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(mKeyboard.mMostCommonKeyWidth);
4128a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
4138a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
4148a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
4158a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
4168a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
41744972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
4188a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
4198da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
420faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka        mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
4215a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
4225a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
4230cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
424cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
425cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
426cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
427cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
428e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
429e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
430dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
431dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
4320cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
4332aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
434e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
4352aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
4362aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
437694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
438723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
4392aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
4402aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
441694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setReleasedKeyGraphics(final Key key) {
442d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
4436bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
444faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
445faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
446faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
4476bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
448faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
449faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
450faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
451faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
452faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
453faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
4542013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4552013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
456faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
4572013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
458faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
45929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
460faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
461faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
462faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
463faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
464faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
46529d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
466faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
4672013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4682013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
4696a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
4706a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
472694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setPressedKeyGraphics(final Key key) {
4736bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
4746bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
4756bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
4766bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
4776bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
4786bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
4796bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
4806bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
481faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
482faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
483faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
4846c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!key.noKeyPreview() && !sInGesture) {
485faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
486faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
487faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
488faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
489faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
490faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
491faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
492faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
4932013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4942013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
495faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
4962013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
49773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
49829d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
499faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
500faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
501faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
502faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
503faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
50429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
505faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
5062013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
5072013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
508d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
509c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
510c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
511694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updateReleaseKeyGraphics(final Key key) {
512faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
513faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
514faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
515faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
516694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updatePressKeyGraphics(final Key key) {
517faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
518faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
519faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
520faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
521c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
522c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return mGestureStrokeWithPreviewPoints;
5232f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
5242f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
5258a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastX() {
5268a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastX;
5278a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5288a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
5298a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastY() {
5308a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastY;
5318a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5328a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
5338a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
5348a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
5358a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5368a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
537694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
5388a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
5398a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
5408a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5418a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
542694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
5438a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
5448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
545723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
5468a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5478a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
548694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
5498a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
5508a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5518a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
552694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
553e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
5548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
5558a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
556e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
5578a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5588a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
5596c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int getActivePointerTrackerCount() {
5606c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size();
5616c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
5626c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
5631645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka    private void mayStartBatchInput(final Key key) {
564c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
5659c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            return;
5669c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        }
5671645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        if (key == null || !Character.isLetter(key.mCode)) {
5681645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            return;
5691645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        }
5709c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
5719c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            Log.d(TAG, "onStartBatchInput");
5729580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
5739c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        sInGesture = true;
5749c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        mListener.onStartBatchInput();
575350fab6dc4d4d4aec272056004cbb5c039f5bf37Tadashi G. Takaoka        final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
576350fab6dc4d4d4aec272056004cbb5c039f5bf37Tadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
5776c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
5786c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
579157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka    private void mayUpdateBatchInput(final long eventTime, final Key key) {
580157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka        if (key != null) {
581157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            synchronized (sAggregratedPointers) {
582157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mGestureStrokeWithPreviewPoints.appendIncrementalBatchPoints(sAggregratedPointers);
583157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                final int size = sAggregratedPointers.getPointerSize();
584157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                if (size > sLastRecognitionPointSize
585157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                        && GestureStroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
586157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    sLastRecognitionPointSize = size;
587157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    sLastRecognitionTime = eventTime;
588157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    if (DEBUG_LISTENER) {
589157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                        Log.d(TAG, "onUpdateBatchInput: batchPoints=" + size);
590157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    }
591157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    mListener.onUpdateBatchInput(sAggregratedPointers);
5926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
5936c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
5949580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
595f0e83046f022b5594f98aef07fbda7f2f837f262Tadashi G. Takaoka        final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
596f0e83046f022b5594f98aef07fbda7f2f837f262Tadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
5979580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
5989580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
599c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private void mayEndBatchInput() {
6006c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        synchronized (sAggregratedPointers) {
601c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
602c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.reset();
6036c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
6046c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                if (DEBUG_LISTENER) {
6056c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                    Log.d(TAG, "onEndBatchInput: batchPoints="
6066c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                            + sAggregratedPointers.getPointerSize());
6076c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
6086c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sInGesture = false;
6096c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mListener.onEndBatchInput(sAggregratedPointers);
6106c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                clearBatchInputPointsOfAllPointerTrackers();
6116c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
6129580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
613350fab6dc4d4d4aec272056004cbb5c039f5bf37Tadashi G. Takaoka        final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
614350fab6dc4d4d4aec272056004cbb5c039f5bf37Tadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
6159580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
6169580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
6176c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static void abortBatchInput() {
6181e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        clearBatchInputPointsOfAllPointerTrackers();
6191e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    }
6201e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka
6216c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static void clearBatchInputPointsOfAllPointerTrackers() {
6226c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int trackersSize = sTrackers.size();
6236c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        for (int i = 0; i < trackersSize; ++i) {
6246c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            final PointerTracker tracker = sTrackers.get(i);
625c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            tracker.mGestureStrokeWithPreviewPoints.reset();
6269580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
6276c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        sAggregratedPointers.reset();
6286c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        sLastRecognitionPointSize = 0;
6296c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        sLastRecognitionTime = 0;
6309580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
6319580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
632694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
633694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
6348ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
6358ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
6368ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
6378ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
6388ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6398ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
6408ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
6418ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
6428ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6438ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
6443314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
6458ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6468ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
6478ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
6488ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6498ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
6508ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
6518ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
652694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long eventTime,
653694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
654694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
655dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
656694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
6571d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
658f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
65963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
660f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
661f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
662baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
6638a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
664160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
6658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dx = x - mLastX;
6668a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dy = y - mLastY;
667baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            final int distanceSquared = (dx * dx + dy * dy);
6685509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            if (distanceSquared < sParams.mTouchNoiseThresholdDistanceSquared) {
669faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
670faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
671faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                            + " distance=" + distanceSquared);
6729bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
6739bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distanceSquared);
6749bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
675d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
676baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
677baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
678baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
679baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
680eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
6816c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
6821d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
683e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            if (key != null && key.isModifier()) {
6841d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // Before processing a down event of modifier key, all pointers already being
6851d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // tracked should be released.
6861d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                queue.releaseAllPointers(eventTime);
6871d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
6881d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.add(this);
6891d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
6901d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
6916c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!sShouldHandleGesture) {
6926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
6936c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
694b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        // A gesture should start only from the letter key.
695b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
696b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka                && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode);
697b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
698b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
6996c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sGestureFirstDownTime = eventTime;
7009580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
701c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            onGestureDownEvent(x, y, eventTime);
702eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
7031d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
7041d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
705c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private void onGestureDownEvent(final int x, final int y, final long eventTime) {
706c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        mIsDetectingGesture = true;
707630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setLastLetterTypingTime(eventTime, sLastLetterTypingUpTime);
708c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime);
709c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown,
71002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                true /* isMajorEvent */);
711c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    }
712c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka
713694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
714e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
71567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
71632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
717160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
718c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
71932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
7201a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
721c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka        mKeyAlreadyProcessed = false;
722cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
723996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIgnoreModifierKey = false;
724e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
7251a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
726e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
7271a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
7282a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
729e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
730e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
731996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
732e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
733e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
734e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            setPressedKeyGraphics(key);
7356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
7366a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
7376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
738694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startSlidingKeyInput(final Key key) {
739e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
740e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            mIgnoreModifierKey = key.isModifier();
741e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
742996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
743996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
744996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
7456c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
74602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
7476c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
7486c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (mIsDetectingGesture) {
74902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent);
7501645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            mayStartBatchInput(key);
751157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            if (sInGesture) {
752157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mayUpdateBatchInput(eventTime, key);
7539580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
7549580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7559580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7569580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
757694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
758694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
759dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
760694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
761694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyAlreadyProcessed) {
762e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
763694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
764baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
7656c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture && me != null) {
766eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
767eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
768eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
769eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
770eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
771eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
772eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
7736c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
77402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
775eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
776eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
777eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
7786c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
7796c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7806c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
7816c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
7828a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
7838a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
784e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
785e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onMoveKey(x, y);
786eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
7876c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture) {
7886c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
78902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key);
7906c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
7916c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mIgnoreModifierKey = true;
7926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mTimerProxy.cancelLongPressTimer();
7936c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mIsInSlidingKeyInput = true;
7946c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
7956c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
7966c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
7976c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
798eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
799eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
800e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
801c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka            if (oldKey == null) {
8029e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
8039e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
8041a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // This onPress call may have changed keyboard layout. Those cases are detected at
805e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                // {@link #setKeyboard}. In those cases, we should update key according to the
8061a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // new keyboard layout.
8072a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
808e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    key = onMoveKey(x, y);
809e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                }
810e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                onMoveToNewKey(key, x, y);
811e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                startLongPressTimer(key);
812e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setPressedKeyGraphics(key);
813e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            } else if (isMajorEnoughMoveToBeOnNewKey(x, y, key)) {
8149e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key from the previous key, we must call
8159e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onRelease() first to notify that the previous key has been released, then call
8169e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onPress() to notify that the new key is being pressed.
817e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
818e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
819996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
8202321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka                mTimerProxy.cancelKeyTimers();
821e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                startRepeatKey(key);
82267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
8231a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // This onPress call may have changed keyboard layout. Those cases are detected
824e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    // at {@link #setKeyboard}. In those cases, we should update key according
8251a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // to the new keyboard layout.
8262a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                    if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
827e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                        key = onMoveKey(x, y);
828e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    }
829e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    onMoveToNewKey(key, x, y);
830e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    startLongPressTimer(key);
831e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    setPressedKeyGraphics(key);
83267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
833faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // HACK: On some devices, quick successive touches may be translated to sudden
834faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move by touch panel firmware. This hack detects the case and translates the
835faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move event to successive up and down events.
836faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dx = x - lastX;
837faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dy = y - lastY;
838faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int lastMoveSquared = dx * dx + dy * dy;
839c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                    // TODO: Should find a way to balance gesture detection and this hack.
840d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                    if (sNeedsPhantomSuddenMoveEventHack
841c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                            && lastMoveSquared >= mKeyQuarterWidthSquared
8426c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                            && !mIsDetectingGesture) {
843d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        if (DEBUG_MODE) {
844d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                            Log.w(TAG, String.format("onMoveEvent:"
845d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                                    + " phantom sudden move event is translated to "
846faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                                    + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
847d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        }
848c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        // TODO: This should be moved to outside of this nested if-clause?
8499bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                        if (ProductionFlag.IS_EXPERIMENTAL) {
8509bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
8519bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                        }
852630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka                        onUpEventInternal(eventTime);
853faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                        onDownEventInternal(x, y, eventTime);
854faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    } else {
8555e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // HACK: If there are currently multiple touches, register the key even if
8565e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // the finger slides off the key. This defends against noise from some
8575e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // touch panels when there are close multiple touches.
8585e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // Caveat: When in chording input mode with a modifier key, we don't use
8595e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // this hack.
8606c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                        if (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null
8616c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
862630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka                            onUpEventInternal(eventTime);
8635e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        }
8646c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                        if (!mIsDetectingGesture) {
865c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                            mKeyAlreadyProcessed = true;
866c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        }
867e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                        setReleasedKeyGraphics(oldKey);
868faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    }
86967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
870c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
8716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
872e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, key)) {
8739e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid out from the previous key, we must call onRelease() to
8749e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // notify that the previous key has been released.
875e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
876e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
877996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
87898b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka                mTimerProxy.cancelLongPressTimer();
87967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
880e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    onMoveToNewKey(key, x, y);
88167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
8826c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                    if (!mIsDetectingGesture) {
883c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        mKeyAlreadyProcessed = true;
884c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                    }
88567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
88607221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
8876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
8886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
8896a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
890694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onUpEvent(final int x, final int y, final long eventTime) {
891694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
892dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
893694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
8941d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
8955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
8961d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
8976c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (!sInGesture) {
898eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                if (mCurrentKey != null && mCurrentKey.isModifier()) {
899eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    // Before processing an up event of modifier key, all pointers already being
900eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    // tracked should be released.
901eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    queue.releaseAllPointersExcept(this, eventTime);
902eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                } else {
903eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    queue.releaseAllPointersOlderThan(this, eventTime);
904eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                }
9051d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
9061d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
907630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        onUpEventInternal(eventTime);
9086c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (queue != null) {
9096c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            queue.remove(this);
9106c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
9111d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9121d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
913d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
914d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
915d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
9160cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
917694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
918694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
9190cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka            printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
920694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
921630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        onUpEventInternal(eventTime);
922d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
9231d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9241d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
925630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka    private void onUpEventInternal(final long eventTime) {
9262321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
927cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
9286c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
9296c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
9306c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
9315a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
9326c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        setReleasedKeyGraphics(currentKey);
9339d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
9349d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
9359d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
9369ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
937eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
9386c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
9396c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
9406c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.mCode, true);
941eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
942c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mayEndBatchInput();
943eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
944eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
9456c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        // This event will be recognized as a regular code input. Clear unused possible batch points
9466c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        // so they are not mistakenly displayed as preview.
9470c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        clearBatchInputPointsOfAllPointerTrackers();
948694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyAlreadyProcessed) {
949d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
950694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
9516c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (currentKey != null && !currentKey.isRepeatable()) {
9526c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            detectAndSendKey(currentKey, mKeyX, mKeyY);
953630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka            final int code = currentKey.mCode;
954630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka            if (Keyboard.isLetterCode(code) && code != Keyboard.CODE_SPACE) {
955630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka                sLastLetterTypingUpTime = eventTime;
956630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka            }
9576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9596a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
960694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) {
9611e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        abortBatchInput();
9629ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        onLongPressed();
9639d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mIsShowingMoreKeysPanel = true;
964cc3500b0c8f11e8de1ad7376dda466d30637e462Tadashi G. Takaoka        onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
9659ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
9669ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
967906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
968d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
969e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
9705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
9711ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        if (queue != null) {
972d00d963b9d47c1bba6f65534033a33fe7c30dde5Tadashi G. Takaoka            queue.remove(this);
9731ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        }
974d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
975d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
976694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onCancelEvent(final int x, final int y, final long eventTime) {
977694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
978dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
979694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
9801d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
9815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
982c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (queue != null) {
983d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka            queue.releaseAllPointersExcept(this, eventTime);
9841d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
985c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        }
986baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
9871d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9881d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
989baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
9902321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
991e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
992cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
9939d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
9949d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
9959d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
9969ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
9976a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9986a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
999694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
10006c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (key != null && key.isRepeatable() && !sInGesture) {
10016662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv            onRegisterKey(key);
1002a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startKeyRepeatTimer(this);
1003a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        }
1004a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
1005a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
1006694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onRegisterKey(final Key key) {
1007c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
1008e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            detectAndSendKey(key, key.mX, key.mY);
1009d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
1010c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
10116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1013694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final Key newKey) {
1014694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyDetector == null) {
1015a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
1016694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1017694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
10186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1019e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1020e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        } else if (curKey != null) {
1021e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            return curKey.squaredDistanceToEdge(x, y)
1022a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka                    >= mKeyDetector.getKeyHysteresisDistanceSquared();
10236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
1024e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
10256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
10266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10276a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1028694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
10296c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (key != null && key.isLongPressEnabled() && !sInGesture) {
1030a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startLongPressTimer(this);
10314189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        }
103266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
103366e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
1034694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y) {
103583e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
10368aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1037dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1038dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
103966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
1040694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final int code = key.mCode;
1041ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        callListenerOnCodeInput(key, code, x, y);
10422013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        callListenerOnRelease(key, code, false);
10436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1045694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1046694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1047723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1048e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
1049e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
1050d7edd1cbb5dfacacdd39766e379fb60d2b7c6b73Tadashi G. Takaoka                (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, eventTime, code));
1051dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
10526e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1053