PointerTracker.java revision 8335c59ea7715f3dbc6625f128a7a038f314a89f
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
194daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport android.graphics.Canvas;
204daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport android.graphics.Paint;
21e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaokaimport android.os.SystemClock;
22c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport android.util.Log;
238ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaokaimport android.view.MotionEvent;
24fe824948bb5c07dcd0a47221cb5b7b4f2f4365caTadashi G. Takaokaimport android.view.View;
254692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaokaimport android.widget.TextView;
26c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
27918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
28f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStroke;
2972934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
30f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.latin.InputPointers;
31faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
328335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaokaimport com.android.inputmethod.latin.Utils;
339bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
346b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
365c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
37dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
387ae1fd02d40c8c026a411f1037753725868c611eTadashi G. Takaokapublic class PointerTracker implements PointerTrackerQueue.Element {
39dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
40dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
41dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
42dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
43faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
4440a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
450657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    /** True if {@link PointerTracker}s should handle gesture events. */
460657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    private static boolean sShouldHandleGesture = false;
478335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sMainDictionaryAvailable = false;
488335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByInputField = false;
498335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByUser = false;
50918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
511e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
529580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
53f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
54f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
55f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
67f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
68f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
69f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
70f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
71f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
72f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
73f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
74f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
75f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
76f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
77f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
78f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
79f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
809d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public interface DrawingProxy extends MoreKeysPanel.Controller {
816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
824692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        public TextView inflateKeyPreviewText();
83e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
84d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
854daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang        public void showGestureTrail(PointerTracker tracker);
866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
882321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
89d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        public void startTypingStateTimer(Key typedKey);
9073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
91a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
92a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker);
93a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code);
9498b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
950ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer();
96beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer();
970ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout();
982321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
9929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
10029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
10129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
102d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            public void startTypingStateTimer(Key typedKey) {}
10393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
10473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
10593246652638f423d5220449f65495dea0639c750Tadashi 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
124160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
125c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static MainKeyboardView.PointerTrackerParams sParams;
1265c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sTouchNoiseThresholdDistanceSquared;
127d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
1285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
129b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
1300c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang    private static final InputPointers sAggregratedPointers = new InputPointers(
1310c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang            GestureStroke.DEFAULT_CAPACITY);
1325c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static PointerTrackerQueue sPointerTrackerQueue;
1331e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    // HACK: Change gesture detection criteria depending on this variable.
1341e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    // TODO: Find more comprehensive ways to detect a gesture start.
1351e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    // True when the previous user input was a gesture input, not a typing input.
1361e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    private static boolean sWasInGesture;
1375c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1385c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1400efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1410efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
142a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
143dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private KeyboardActionListener mListener = EMPTY_LISTENER;
144baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1455a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
146faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private int mKeyQuarterWidthSquared;
1474692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    private final TextView mKeyPreviewText;
1486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1499580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private boolean mIsAlphabetKeyboard;
1509580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private boolean mIsPossibleGesture = false;
1519580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private boolean mInGesture = false;
1529580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
1531e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    // TODO: Remove these variables
1549580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private int mLastRecognitionPointSize = 0;
1559580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private long mLastRecognitionTime = 0;
1569580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
1578a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
1588a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
1598a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
1608a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
161e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
162e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
163e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
1648a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
1658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
1668a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
1678a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
1688a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
1698a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
1706a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1711a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
1721a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
1731a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
1749ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    // true if event is already translated to a key action.
175c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    private boolean mKeyAlreadyProcessed;
176c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
1779d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // true if this pointer has been long-pressed and is showing a more keys panel.
1789d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private boolean mIsShowingMoreKeysPanel;
1799ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
180cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    // true if this pointer is in sliding key input
1815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
182cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
18367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    // true if sliding key is allowed.
18467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
18567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
186996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    // ignore modifier key if true
187996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private boolean mIgnoreModifierKey;
188996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
189dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    // Empty {@link KeyboardActionListener}
190e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka    private static final KeyboardActionListener EMPTY_LISTENER =
191e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka            new KeyboardActionListener.Adapter();
1926e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka
193f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    private final GestureStroke mGestureStroke;
194f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
195d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    public static void init(boolean hasDistinctMultitouch,
19662b8dddb6ddb057555a1665759f9cf197e480c9fTadashi G. Takaoka            boolean needsPhantomSuddenMoveEventHack) {
1975c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (hasDistinctMultitouch) {
1985c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = new PointerTrackerQueue();
1995c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        } else {
2005c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = null;
2015c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
202d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
2035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
204c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        setParameters(MainKeyboardView.PointerTrackerParams.DEFAULT);
205160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
20693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
207c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    public static void setParameters(MainKeyboardView.PointerTrackerParams params) {
208160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        sParams = params;
2095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sTouchNoiseThresholdDistanceSquared = (int)(
210160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance);
2116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
2126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
2138335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
2148335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
2158335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
2168335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
2178335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
2188335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
2198335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
2208335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
2218335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    public static void setMainDictionaryAvailability(boolean mainDictionaryAvailable) {
2228335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
2238335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
2248335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
2258335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
2268335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(boolean gestureHandlingEnabledByUser) {
2278335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
2288335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
229918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
230918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
2315c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
232b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
2335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2345c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
2355c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
2365c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
2375c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
2385c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2395c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2405c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
2415c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2425c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2435c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
2445c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
2455c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2465c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2475c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void setKeyboardActionListener(KeyboardActionListener listener) {
248afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
249afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
250afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
2515c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
2525c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2535c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2545c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2558335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    public static void setKeyDetector(KeyDetector keyDetector) {
256afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
257afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
258afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
2595c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
2605c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
2615c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
2625c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
263918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
2648335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
2658335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
2665c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void dismissAllKeyPreviews() {
269afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
270afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
271afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
272fe824948bb5c07dcd0a47221cb5b7b4f2f4365caTadashi G. Takaoka            tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
273e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
2745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2779580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    // TODO: To handle multi-touch gestures we may want to move this method to
2789580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    // {@link PointerTrackerQueue}.
2799580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private static InputPointers getIncrementalBatchPoints() {
280afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
281afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
282afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
2830c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang            tracker.mGestureStroke.appendIncrementalBatchPoints(sAggregratedPointers);
284f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
2850c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        return sAggregratedPointers;
286f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
287f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
2889580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    // TODO: To handle multi-touch gestures we may want to move this method to
2899580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    // {@link PointerTrackerQueue}.
2909580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private static InputPointers getAllBatchPoints() {
291afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
292afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
293afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
2940c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang            tracker.mGestureStroke.appendAllBatchPoints(sAggregratedPointers);
295f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
2960c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        return sAggregratedPointers;
297f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
298f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
2999580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    // TODO: To handle multi-touch gestures we may want to move this method to
3009580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    // {@link PointerTrackerQueue}.
3011e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    public static void clearBatchInputPointsOfAllPointerTrackers() {
302afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
303afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
304afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
3059580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            tracker.mGestureStroke.reset();
306f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
3070c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        sAggregratedPointers.reset();
308f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
309f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
310f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    private PointerTracker(int id, KeyEventHandler handler) {
3115c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (handler == null)
3125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
3135c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
314f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        mGestureStroke = new GestureStroke(id);
3155c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
3165c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
3175c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
3185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
3194692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText();
3204692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    }
3214692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka
3224692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    public TextView getKeyPreviewText() {
3234692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        return mKeyPreviewText;
3246a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3261a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
3272a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
3289580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (mInGesture) {
329eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
330eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
331e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
332e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
3337dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            Log.d(TAG, "onPress    : " + KeyDetector.printableCode(key)
3342a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey
335e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled=" + key.isEnabled());
336e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
33793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
338996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
33993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
340e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
3412a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onPressKey(key.mCode);
342690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
343690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
344d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
345690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
346690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
347690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
348dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
349dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
350690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
351690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
352ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    private void callListenerOnCodeInput(Key key, int primaryCode, int x, int y) {
353e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
3546bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
3556bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final int code = altersCode ? key.mAltCode : primaryCode;
3562013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
3578cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            Log.d(TAG, "onCodeInput: " + Keyboard.printableCode(code) + " text=" + key.mOutputText
358ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                    + " x=" + x + " y=" + y
3596bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
360e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled=" + key.isEnabled());
3612013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
3629bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
3639bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
3649bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
3659bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
366e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
367996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
368e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
3696bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
3706bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
3718cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            if (code == Keyboard.CODE_OUTPUT_TEXT) {
3728cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka                mListener.onTextInput(key.mOutputText);
3738cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            } else if (code != Keyboard.CODE_UNSPECIFIED) {
374ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
3752013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
37693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
377dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
378dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
379690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
380690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
381e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
3829580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (mInGesture) {
383eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
384eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
385e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
386e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
3877dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            Log.d(TAG, "onRelease  : " + Keyboard.printableCode(primaryCode)
388e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
389e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled="+ key.isEnabled());
390e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
3919bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
3929bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
3939bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
3949bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
395e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
396996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
397e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
39893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
3992a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
40093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
401dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
402dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
4038aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
404dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
4058aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            Log.d(TAG, "onCancelInput");
4069bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
4079bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
4089bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
4098aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
410dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
411dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
41246286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka    private void setKeyDetectorInner(KeyDetector keyDetector) {
413a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
4145a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
4159580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard();
416f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        mGestureStroke.setGestureSampleLength(
417f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka                mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
4188a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
4198a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
4208a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
4218a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
4228a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
42344972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
4248a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
4258da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
426faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka        mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
4275a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
4285a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
4290cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
430cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
431cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
432cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
433cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
434e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
435e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
436dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
437dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
4380cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
4392aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
440e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
4412aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
4422aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
443e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKeyOn(int x, int y) {
444723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
4452aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
4462aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
447e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void setReleasedKeyGraphics(Key key) {
448d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
4496bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
450faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
451faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
452faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
4536bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
454faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
455faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
456faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
457faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
458faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
459faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
4602013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4612013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
462faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
4632013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
464faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
465faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final int altCode = key.mAltCode;
466faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
467faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
468faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
469faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
470faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
471faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (k != key && k.mAltCode == altCode) {
472faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
4732013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4742013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
4756a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
4766a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4776a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
478e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void setPressedKeyGraphics(Key key) {
4796bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
4806bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
4816bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
4826bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
4836bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
4846bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
4856bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
4866bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
487faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
488faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
489faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
4909580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (!key.noKeyPreview() && !mInGesture) {
491faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
492faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
493faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
494faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
495faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
496faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
497faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
498faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
4992013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
5002013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
501faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
5022013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
50373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
504faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final int altCode = key.mAltCode;
505faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
506faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
507faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
508faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
509faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
510faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (k != key && k.mAltCode == altCode) {
511faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
5122013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
5132013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
514d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
515c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
516c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
517faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    private void updateReleaseKeyGraphics(Key key) {
518faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
519faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
520faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
521faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
522faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    private void updatePressKeyGraphics(Key key) {
523faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
524faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
525faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
526faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
5272f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    public void drawGestureTrail(Canvas canvas, Paint paint) {
5282f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang        if (mInGesture) {
5292f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang            mGestureStroke.drawGestureTrail(canvas, paint, mLastX, mLastY);
5302f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang        }
5312f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
5322f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
5338a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastX() {
5348a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastX;
5358a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5368a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
5378a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastY() {
5388a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastY;
5398a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5408a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
5418a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
5428a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
5438a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
545e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key onDownKey(int x, int y, long eventTime) {
5468a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
5478a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
5488a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5498a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
550e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key onMoveKeyInternal(int x, int y) {
5518a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
5528a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
553723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
5548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5558a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
556e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key onMoveKey(int x, int y) {
5578a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
5588a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5598a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
560e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key onMoveToNewKey(Key newKey, int x, int y) {
561e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
5628a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
5638a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
564e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
5658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5668a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
5679580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private void startBatchInput() {
5689580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (DEBUG_LISTENER) {
5699580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            Log.d(TAG, "onStartBatchInput");
5709580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
5719580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        mInGesture = true;
5729580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        mListener.onStartBatchInput();
5739580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
5749580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
5759580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private void updateBatchInput(InputPointers batchPoints) {
5769580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (DEBUG_LISTENER) {
5779580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
5789580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
5799580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        mListener.onUpdateBatchInput(batchPoints);
5809580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
5819580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
5829580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private void endBatchInput(InputPointers batchPoints) {
5839580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (DEBUG_LISTENER) {
5849580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize());
5859580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
5869580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        mListener.onEndBatchInput(batchPoints);
5871e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        clearBatchInputRecognitionStateOfThisPointerTracker();
5881e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        clearBatchInputPointsOfAllPointerTrackers();
5891e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        sWasInGesture = true;
5909580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
5919580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
5929580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private void abortBatchInput() {
5931e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        clearBatchInputRecognitionStateOfThisPointerTracker();
5941e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        clearBatchInputPointsOfAllPointerTrackers();
5951e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    }
5961e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka
5971e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka    private void clearBatchInputRecognitionStateOfThisPointerTracker() {
5989580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        mIsPossibleGesture = false;
5999580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        mInGesture = false;
6001e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        mLastRecognitionPointSize = 0;
6011e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        mLastRecognitionTime = 0;
6029580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
6039580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
6049580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private boolean updateBatchInputRecognitionState(long eventTime, int size) {
6059580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (size > mLastRecognitionPointSize
6061e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka                && eventTime > mLastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME) {
6079580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            mLastRecognitionPointSize = size;
6089580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            mLastRecognitionTime = eventTime;
6099580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            return true;
6109580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
6119580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        return false;
6129580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
6139580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
6148ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    public void processMotionEvent(int action, int x, int y, long eventTime,
6158ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            KeyEventHandler handler) {
6168ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
6178ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
6188ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
6198ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
6208ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6218ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
6228ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
6238ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
6248ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6258ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
6263314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
6278ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6288ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
6298ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
6308ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6318ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
6328ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
6338ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
634f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) {
635dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
636dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
6371d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
638f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
63963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
640f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
641f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
642baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
6438a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
644160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
6458a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dx = x - mLastX;
6468a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dy = y - mLastY;
647baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            final int distanceSquared = (dx * dx + dy * dy);
6485c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            if (distanceSquared < sTouchNoiseThresholdDistanceSquared) {
649faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
650faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
651faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                            + " distance=" + distanceSquared);
6529bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
6539bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distanceSquared);
6549bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
655d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
656baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
657baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
658baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
659baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
6605c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
661eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
6621d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
663e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            if (key != null && key.isModifier()) {
6641d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // Before processing a down event of modifier key, all pointers already being
6651d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // tracked should be released.
6661d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                queue.releaseAllPointers(eventTime);
6671d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
6681d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.add(this);
6691d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
6701d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
671eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (queue != null && queue.size() == 1) {
6729580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            mIsPossibleGesture = false;
6739580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            // A gesture should start only from the letter key.
6740657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka            if (sShouldHandleGesture && mIsAlphabetKeyboard && !mIsShowingMoreKeysPanel
6750657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka                    && key != null && Keyboard.isLetterCode(key.mCode)) {
6769580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka                mIsPossibleGesture = true;
6770c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang                // TODO: pointer times should be relative to first down even in entire batch input
6780c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang                // instead of resetting to 0 for each new down event.
6799580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka                mGestureStroke.addPoint(x, y, 0, false);
6809580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
681eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
6821d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
6831d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
684baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onDownEventInternal(int x, int y, long eventTime) {
685e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
68667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
68732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
688160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
689c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
69032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
6911a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
692c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka        mKeyAlreadyProcessed = false;
693cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
694996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIgnoreModifierKey = false;
695e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
6961a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
697e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
6981a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
6992a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
700e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
701e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
702996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
703e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
704e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
705e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            setPressedKeyGraphics(key);
7066a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
7076a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
7086a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
709996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private void startSlidingKeyInput(Key key) {
710e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
711e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            mIgnoreModifierKey = key.isModifier();
712e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
713996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
714996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
715996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
7169580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime,
7179580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            boolean isHistorical, Key key) {
7189580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        final int gestureTime = (int)(eventTime - tracker.getDownTime());
7190657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka        if (sShouldHandleGesture && mIsPossibleGesture) {
7209580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            final GestureStroke stroke = mGestureStroke;
7219580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            stroke.addPoint(x, y, gestureTime, isHistorical);
7221e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka            if (!mInGesture && stroke.isStartOfAGesture(gestureTime, sWasInGesture)) {
7239580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka                startBatchInput();
7249580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
7259580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7269580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
7279580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (key != null && mInGesture) {
7289580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            final InputPointers batchPoints = getIncrementalBatchPoints();
7294daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang            mDrawingProxy.showGestureTrail(this);
7309580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
7319580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka                updateBatchInput(batchPoints);
7329580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
7339580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7349580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7359580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
7363314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka    public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) {
737dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_MOVE_EVENT)
738dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
739e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka        if (mKeyAlreadyProcessed)
740e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
741baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
742eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (me != null) {
743eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
744eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
745eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
746eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
747eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
748eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
749eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
7509580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka                onGestureMoveEvent(this, historicalX, historicalY, historicalTime,
751eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                        true /* isHistorical */, null);
752eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
753eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
754eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
7558a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
7568a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
757e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
758e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onMoveKey(x, y);
759eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
760eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        // Register move event on gesture tracker.
7619580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        onGestureMoveEvent(this, x, y, eventTime, false /* isHistorical */, key);
7629580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (mInGesture) {
763eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mIgnoreModifierKey = true;
764eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mTimerProxy.cancelLongPressTimer();
765eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mIsInSlidingKeyInput = true;
766eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mCurrentKey = null;
767eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            setReleasedKeyGraphics(oldKey);
768eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
769eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
770e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
771c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka            if (oldKey == null) {
7729e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
7739e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
7741a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // This onPress call may have changed keyboard layout. Those cases are detected at
775e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                // {@link #setKeyboard}. In those cases, we should update key according to the
7761a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // new keyboard layout.
7772a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
778e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    key = onMoveKey(x, y);
779e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                }
780e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                onMoveToNewKey(key, x, y);
781e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                startLongPressTimer(key);
782e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setPressedKeyGraphics(key);
783e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            } else if (isMajorEnoughMoveToBeOnNewKey(x, y, key)) {
7849e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key from the previous key, we must call
7859e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onRelease() first to notify that the previous key has been released, then call
7869e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onPress() to notify that the new key is being pressed.
787e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
788e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
789996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
7902321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka                mTimerProxy.cancelKeyTimers();
791e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                startRepeatKey(key);
79267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
7931a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // This onPress call may have changed keyboard layout. Those cases are detected
794e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    // at {@link #setKeyboard}. In those cases, we should update key according
7951a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // to the new keyboard layout.
7962a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                    if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
797e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                        key = onMoveKey(x, y);
798e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    }
799e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    onMoveToNewKey(key, x, y);
800e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    startLongPressTimer(key);
801e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    setPressedKeyGraphics(key);
80267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
803faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // HACK: On some devices, quick successive touches may be translated to sudden
804faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move by touch panel firmware. This hack detects the case and translates the
805faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move event to successive up and down events.
806faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dx = x - lastX;
807faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dy = y - lastY;
808faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int lastMoveSquared = dx * dx + dy * dy;
809c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                    // TODO: Should find a way to balance gesture detection and this hack.
810d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                    if (sNeedsPhantomSuddenMoveEventHack
811c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                            && lastMoveSquared >= mKeyQuarterWidthSquared
812c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                            && !mIsPossibleGesture) {
813d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        if (DEBUG_MODE) {
814d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                            Log.w(TAG, String.format("onMoveEvent:"
815d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                                    + " phantom sudden move event is translated to "
816faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                                    + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
817d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        }
818c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        // TODO: This should be moved to outside of this nested if-clause?
8199bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                        if (ProductionFlag.IS_EXPERIMENTAL) {
8209bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
8219bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                        }
8220cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka                        onUpEventInternal();
823faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                        onDownEventInternal(x, y, eventTime);
824faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    } else {
8255e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // HACK: If there are currently multiple touches, register the key even if
8265e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // the finger slides off the key. This defends against noise from some
8275e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // touch panels when there are close multiple touches.
8285e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // Caveat: When in chording input mode with a modifier key, we don't use
8295e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // this hack.
8305e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        if (me != null && me.getPointerCount() > 1
8315e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
8320cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka                            onUpEventInternal();
8335e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        }
834c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        if (!mIsPossibleGesture) {
835c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                            mKeyAlreadyProcessed = true;
836c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        }
837e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                        setReleasedKeyGraphics(oldKey);
838faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    }
83967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
840c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
8416a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
842e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, key)) {
8439e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid out from the previous key, we must call onRelease() to
8449e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // notify that the previous key has been released.
845e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
846e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
847996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
84898b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka                mTimerProxy.cancelLongPressTimer();
84967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
850e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    onMoveToNewKey(key, x, y);
85167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
852c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                    if (!mIsPossibleGesture) {
853c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        mKeyAlreadyProcessed = true;
854c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                    }
85567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
85607221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
8576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
8586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
8596a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
860906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onUpEvent(int x, int y, long eventTime) {
861dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
862dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
8631d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
8645c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
8651d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
8669580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            if (!mInGesture) {
867eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                if (mCurrentKey != null && mCurrentKey.isModifier()) {
868eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    // Before processing an up event of modifier key, all pointers already being
869eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    // tracked should be released.
870eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    queue.releaseAllPointersExcept(this, eventTime);
871eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                } else {
872eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    queue.releaseAllPointersOlderThan(this, eventTime);
873eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                }
8741d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
8751d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
8761d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
8770cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka        onUpEventInternal();
8781d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
8791d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
880d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
881d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
882d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
8830cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
8840cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    public void onPhantomUpEvent(long eventTime) {
885c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (DEBUG_EVENT)
8860cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka            printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
8870cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka        onUpEventInternal();
888d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
8891d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
8901d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
8910cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    private void onUpEventInternal() {
8922321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
893cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
894c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka        mIsPossibleGesture = false;
8955a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
8965a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
8979d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
8989d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
8999d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
9009ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
901eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
9029580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (mInGesture) {
903eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Register up event on gesture tracker.
9049580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            // TODO: Figure out how to deal with multiple fingers that are in gesture, sliding,
9059580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            // and/or tapping mode?
9069580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            endBatchInput(getAllBatchPoints());
907eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            if (mCurrentKey != null) {
908eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
9099580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka                mCurrentKey = null;
910eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
9114daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang            mDrawingProxy.showGestureTrail(this);
912eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
913eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
9140c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        // This event will be recognized as a regular code input. Clear unused batch points so they
9150c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        // are not mistakenly included in the next batch event.
9160c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        clearBatchInputPointsOfAllPointerTrackers();
917d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        if (mKeyAlreadyProcessed)
918d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
9190d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka        if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
9205a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka            detectAndSendKey(mCurrentKey, mKeyX, mKeyY);
9216a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
924e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka    public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) {
9251e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        abortBatchInput();
9269ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        onLongPressed();
9279d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mIsShowingMoreKeysPanel = true;
928cc3500b0c8f11e8de1ad7376dda466d30637e462Tadashi G. Takaoka        onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
9299ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
9309ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
931906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
932d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
933e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
9345c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
9351ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        if (queue != null) {
936d00d963b9d47c1bba6f65534033a33fe7c30dde5Tadashi G. Takaoka            queue.remove(this);
9371ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        }
938d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
939d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
940906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onCancelEvent(int x, int y, long eventTime) {
941dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
942dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
9431d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
9445c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
945c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (queue != null) {
946d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka            queue.releaseAllPointersExcept(this, eventTime);
9471d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
948c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        }
949baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
9501d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9511d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
952baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
9532321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
954e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
955cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
9569d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
9579d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
9589d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
9599ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
9606a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9616a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
962e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void startRepeatKey(Key key) {
9639580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (key != null && key.isRepeatable() && !mInGesture) {
9646662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv            onRegisterKey(key);
965a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startKeyRepeatTimer(this);
966a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        }
967a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
968a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
9696662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv    public void onRegisterKey(Key key) {
970c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
971e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            detectAndSendKey(key, key.mX, key.mY);
972d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
973c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
9746a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9756a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
976e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, Key newKey) {
977b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        if (mKeyDetector == null)
978a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
979e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key curKey = mCurrentKey;
9806a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
981e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
982e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        } else if (curKey != null) {
983e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            return curKey.squaredDistanceToEdge(x, y)
984a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka                    >= mKeyDetector.getKeyHysteresisDistanceSquared();
9856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
986e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
9876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9896a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
990e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void startLongPressTimer(Key key) {
9919580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        if (key != null && key.isLongPressEnabled() && !mInGesture) {
992a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startLongPressTimer(this);
9934189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        }
99466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
99566e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
996e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void detectAndSendKey(Key key, int x, int y) {
99783e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
9988aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
999dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1000dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
100166e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
10022013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        int code = key.mCode;
1003ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        callListenerOnCodeInput(key, code, x, y);
10042013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        callListenerOnRelease(key, code, false);
10051e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        sWasInGesture = false;
10066a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10076a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1008dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private void printTouchEvent(String title, int x, int y, long eventTime) {
1009723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1010e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
1011e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
1012d7edd1cbb5dfacacdd39766e379fb60d2b7c6b73Tadashi G. Takaoka                (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, eventTime, code));
1013dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
10146e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1015