PointerTracker.java revision eea34598bf63f670f47d7b3f37b6436921e5fe02
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
19e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaokaimport android.os.SystemClock;
20c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport android.util.Log;
218ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaokaimport android.view.MotionEvent;
22fe824948bb5c07dcd0a47221cb5b7b4f2f4365caTadashi G. Takaokaimport android.view.View;
234692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaokaimport android.widget.TextView;
24c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
25eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyangimport com.android.inputmethod.keyboard.internal.GestureTracker;
2672934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
27faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
289bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridgeimport com.android.inputmethod.latin.ResearchLogger;
299bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
306a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
315c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
32dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaokapublic class PointerTracker {
34dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
35dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
36dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
37dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
38faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
3940a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
40f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
41f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
42f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
43f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
44f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
45f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
46f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
47f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
48f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
49f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
50f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
51f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
52f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
53f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
54f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
55f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
679d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public interface DrawingProxy extends MoreKeysPanel.Controller {
686a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
694692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        public TextView inflateKeyPreviewText();
70e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
71d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
742321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
7573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public void startTypingStateTimer();
7673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
77a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
78a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker);
79a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code);
8098b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
810ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer();
82beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer();
830ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout();
842321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
8529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
8629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
8729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
8873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public void startTypingStateTimer() {}
8993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
9073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
9193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
92a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker) {}
9329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
94a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(PointerTracker tracker) {}
95a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            @Override
96a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(int code) {}
9729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
9829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelLongPressTimer() {}
9929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
1000ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public void startDoubleTapTimer() {}
1010ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
102beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            public void cancelDoubleTapTimer() {}
103beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            @Override
1040ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public boolean isInDoubleTapTimeout() { return false; }
1050ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
10629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelKeyTimers() {}
10729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
1082321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    }
1092321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka
110160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
111160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private static LatinKeyboardView.PointerTrackerParams sParams;
1125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sTouchNoiseThresholdDistanceSquared;
113d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
1145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
115b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
1165c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static PointerTrackerQueue sPointerTrackerQueue;
1175c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1200efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1210efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
122a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
123dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private KeyboardActionListener mListener = EMPTY_LISTENER;
124baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1255a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
126faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private int mKeyQuarterWidthSquared;
1274692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    private final TextView mKeyPreviewText;
1286a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1298a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
1308a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
1318a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
1328a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
133e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
134e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
135e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
1368a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
1378a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
1388a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
1398a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
1408a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
1418a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
1426a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1431a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
1441a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
1451a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
1469ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    // true if event is already translated to a key action.
147c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    private boolean mKeyAlreadyProcessed;
148c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
1499d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // true if this pointer has been long-pressed and is showing a more keys panel.
1509d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private boolean mIsShowingMoreKeysPanel;
1519ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
152cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    // true if this pointer is in sliding key input
1535c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
154cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
15567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    // true if sliding key is allowed.
15667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
15767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
158996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    // ignore modifier key if true
159996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private boolean mIgnoreModifierKey;
160996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
161dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    // Empty {@link KeyboardActionListener}
162e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka    private static final KeyboardActionListener EMPTY_LISTENER =
163e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka            new KeyboardActionListener.Adapter();
1646e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka
165eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    // Gesture tracker singleton instance
166eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    private static final GestureTracker sGestureTracker = GestureTracker.getInstance();
167eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
168d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    public static void init(boolean hasDistinctMultitouch,
169d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka            boolean needsPhantomSuddenMoveEventHack) {
1705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (hasDistinctMultitouch) {
1715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = new PointerTrackerQueue();
1725c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        } else {
1735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = null;
1745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
175d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
1765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
177160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT);
178160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
17993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
180160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    public static void setParameters(LatinKeyboardView.PointerTrackerParams params) {
181160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        sParams = params;
1825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sTouchNoiseThresholdDistanceSquared = (int)(
183160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance);
1846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
1856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1865c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
187b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
1885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
1905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
1915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
1925c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
1935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1945c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
1965c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1975c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1985c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
1995c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
2005c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2015c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void setKeyboardActionListener(KeyboardActionListener listener) {
2035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
2045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
2055c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
206eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        GestureTracker.init(listener);
2075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2085c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void setKeyDetector(KeyDetector keyDetector) {
2105c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
2115c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
2125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
2135c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
2145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
215eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        sGestureTracker.setKeyboard(keyDetector.getKeyboard());
2165c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2175c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void dismissAllKeyPreviews() {
2195c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
220fe824948bb5c07dcd0a47221cb5b7b4f2f4365caTadashi G. Takaoka            tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
221e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
2225c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2235c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2245c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2255c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public PointerTracker(int id, KeyEventHandler handler) {
2265c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (handler == null)
2275c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
2285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
2295c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
2305c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
2315c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
2325c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
2334692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText();
2344692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    }
2354692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka
2364692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    public TextView getKeyPreviewText() {
2374692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        return mKeyPreviewText;
2386a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
2396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
2401a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
2412a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
242eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (sGestureTracker.isInGesture()) {
243eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
244eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
245e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
246e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
2477dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            Log.d(TAG, "onPress    : " + KeyDetector.printableCode(key)
2482a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey
249e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled=" + key.isEnabled());
250e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
25193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
252996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
25393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
254e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
2552a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onPressKey(key.mCode);
256690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
257690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
2580f87ca72f7c569f7db075701767e504c75952182Tadashi G. Takaoka            if (!key.altCodeWhileTyping() && !key.isModifier()) {
2590f87ca72f7c569f7db075701767e504c75952182Tadashi G. Takaoka                mTimerProxy.startTypingStateTimer();
2600f87ca72f7c569f7db075701767e504c75952182Tadashi G. Takaoka            }
261690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
262690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
263690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
264dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
265dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
266690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
267690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
268ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    private void callListenerOnCodeInput(Key key, int primaryCode, int x, int y) {
269e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
2706bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
2716bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final int code = altersCode ? key.mAltCode : primaryCode;
2722013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
2738cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            Log.d(TAG, "onCodeInput: " + Keyboard.printableCode(code) + " text=" + key.mOutputText
274ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                    + " x=" + x + " y=" + y
2756bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
276e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled=" + key.isEnabled());
2772013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
2789bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2799bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
2809bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
2819bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
282e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
283996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
284e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
2856bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
2866bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
2878cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            if (code == Keyboard.CODE_OUTPUT_TEXT) {
2888cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka                mListener.onTextInput(key.mOutputText);
2898cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            } else if (code != Keyboard.CODE_UNSPECIFIED) {
290ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
2912013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
29293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
293dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
294dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
295690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
296690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
297e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
298eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (sGestureTracker.isInGesture()) {
299eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
300eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
301e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
302e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
3037dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            Log.d(TAG, "onRelease  : " + Keyboard.printableCode(primaryCode)
304e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
305e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka                    + " enabled="+ key.isEnabled());
306e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
3079bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
3089bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
3099bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
3109bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
311e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
312996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
313e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
31493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
3152a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
31693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
317dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
318dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
3198aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
320dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
3218aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            Log.d(TAG, "onCancelInput");
3229bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
3239bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
3249bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
3258aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
326dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
327dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
32846286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka    private void setKeyDetectorInner(KeyDetector keyDetector) {
329a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
3305a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
3318a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
3328a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
3338a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
3348a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
3358a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
3368a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            mCurrentKey = newKey;
3378a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
3388da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
339faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka        mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
3405a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
3415a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
342cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
343cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
344cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
345cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
346e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
347e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
348dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
349dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
3502aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
351e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
3522aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
3532aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
354e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKeyOn(int x, int y) {
355723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
3562aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
3572aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
358e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void setReleasedKeyGraphics(Key key) {
359d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
3606bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
361faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
362faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
363faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
3646bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
365faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
366faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
367faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
368faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
369faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
370faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
3712013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
3722013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
373faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
3742013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
375faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
376faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final int altCode = key.mAltCode;
377faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
378faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
379faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
380faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
381faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
382faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (k != key && k.mAltCode == altCode) {
383faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
3842013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
3852013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
3866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
3876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
389e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void setPressedKeyGraphics(Key key) {
3906bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
3916bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
3926bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
3936bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
3946bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
3956bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
3966bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
3976bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
398faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
399faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
400faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
401eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (!key.noKeyPreview() && !sGestureTracker.isInGesture()) {
402faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
403faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
404faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
405faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
406faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
407faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
408faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
409faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
4102013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4112013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
412faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
4132013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
41473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
415faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final int altCode = key.mAltCode;
416faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
417faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
418faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
419faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
420faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
421faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (k != key && k.mAltCode == altCode) {
422faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
4232013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4242013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
425d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
426c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
427c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
428faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    private void updateReleaseKeyGraphics(Key key) {
429faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
430faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
431faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
432faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
433faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    private void updatePressKeyGraphics(Key key) {
434faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
435faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
436faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
437faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
4388a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastX() {
4398a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastX;
4408a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4418a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
4428a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastY() {
4438a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastY;
4448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4458a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
4468a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
4478a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
4488a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4498a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
450e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key onDownKey(int x, int y, long eventTime) {
4518a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
4528a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
4538a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
455e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key onMoveKeyInternal(int x, int y) {
4568a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
4578a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
458723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
4598a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4608a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
461e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key onMoveKey(int x, int y) {
4628a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
4638a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4648a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
465e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key onMoveToNewKey(Key newKey, int x, int y) {
466e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
4678a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
4688a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
469e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
4708a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4718a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
4728ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    public void processMotionEvent(int action, int x, int y, long eventTime,
4738ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            KeyEventHandler handler) {
4748ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
4758ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
4768ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
4778ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
4788ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
4798ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
4808ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
4818ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
4828ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
4838ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
4843314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
4858ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
4868ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
4878ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
4888ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
4898ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
4908ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
4918ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
492f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) {
493dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
494dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
4951d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
496f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
49763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
498f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
499f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
500baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
5018a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
502160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
5038a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dx = x - mLastX;
5048a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dy = y - mLastY;
505baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            final int distanceSquared = (dx * dx + dy * dy);
5065c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            if (distanceSquared < sTouchNoiseThresholdDistanceSquared) {
507faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
508faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
509faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                            + " distance=" + distanceSquared);
5109bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
5119bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distanceSquared);
5129bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
513d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
514baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
515baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
516baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
517baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
5185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
519eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
5201d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
521e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            if (key != null && key.isModifier()) {
5221d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // Before processing a down event of modifier key, all pointers already being
5231d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // tracked should be released.
5241d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                queue.releaseAllPointers(eventTime);
5251d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
5261d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.add(this);
5271d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
5281d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
529eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (queue != null && queue.size() == 1) {
530eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            sGestureTracker.onDownEvent(this, x, y, eventTime, key);
531eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
5321d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
5331d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
534baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onDownEventInternal(int x, int y, long eventTime) {
535e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
53667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
53732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
538160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
539c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
54032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
5411a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
542c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka        mKeyAlreadyProcessed = false;
543cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
544996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIgnoreModifierKey = false;
545e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
5461a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
547e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
5481a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
5492a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
550e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
551e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
552996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
553e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
554e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
555e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            setPressedKeyGraphics(key);
5566a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
5576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
5586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
559996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private void startSlidingKeyInput(Key key) {
560e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
561e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            mIgnoreModifierKey = key.isModifier();
562e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
563996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
564996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
565996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
5663314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka    public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) {
567dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_MOVE_EVENT)
568dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
569e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka        if (mKeyAlreadyProcessed)
570e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
571baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
572eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (me != null) {
573eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
574eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
575eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
576eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
577eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
578eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
579eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
580eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                sGestureTracker.onMoveEvent(this, historicalX, historicalY, historicalTime,
581eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                        true /* isHistorical */, null);
582eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
583eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
584eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
5858a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
5868a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
587e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
588e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onMoveKey(x, y);
589eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
590eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        // Register move event on gesture tracker.
591eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        sGestureTracker.onMoveEvent(this, x, y, eventTime, false, key);
592eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (sGestureTracker.isInGesture()) {
593eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mIgnoreModifierKey = true;
594eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mTimerProxy.cancelLongPressTimer();
595eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mIsInSlidingKeyInput = true;
596eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mCurrentKey = null;
597eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            setReleasedKeyGraphics(oldKey);
598eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
599eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
600e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
601c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka            if (oldKey == null) {
6029e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
6039e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
6041a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // This onPress call may have changed keyboard layout. Those cases are detected at
605e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                // {@link #setKeyboard}. In those cases, we should update key according to the
6061a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // new keyboard layout.
6072a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
608e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    key = onMoveKey(x, y);
609e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                }
610e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                onMoveToNewKey(key, x, y);
611e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                startLongPressTimer(key);
612e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setPressedKeyGraphics(key);
613e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            } else if (isMajorEnoughMoveToBeOnNewKey(x, y, key)) {
6149e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key from the previous key, we must call
6159e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onRelease() first to notify that the previous key has been released, then call
6169e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onPress() to notify that the new key is being pressed.
617e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
618e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
619996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
6202321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka                mTimerProxy.cancelKeyTimers();
621e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                startRepeatKey(key);
62267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
6231a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // This onPress call may have changed keyboard layout. Those cases are detected
624e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    // at {@link #setKeyboard}. In those cases, we should update key according
6251a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // to the new keyboard layout.
6262a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                    if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
627e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                        key = onMoveKey(x, y);
628e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    }
629e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    onMoveToNewKey(key, x, y);
630e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    startLongPressTimer(key);
631e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    setPressedKeyGraphics(key);
63267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
633faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // HACK: On some devices, quick successive touches may be translated to sudden
634faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move by touch panel firmware. This hack detects the case and translates the
635faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move event to successive up and down events.
636faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dx = x - lastX;
637faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dy = y - lastY;
638faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int lastMoveSquared = dx * dx + dy * dy;
639d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                    if (sNeedsPhantomSuddenMoveEventHack
640d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                            && lastMoveSquared >= mKeyQuarterWidthSquared) {
641d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        if (DEBUG_MODE) {
642d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                            Log.w(TAG, String.format("onMoveEvent:"
643d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                                    + " phantom sudden move event is translated to "
644faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                                    + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
645d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        }
6469bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                        if (ProductionFlag.IS_EXPERIMENTAL) {
6479bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
6489bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                        }
649eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                        onUpEventInternal(x, y, eventTime);
650faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                        onDownEventInternal(x, y, eventTime);
651faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    } else {
6525e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // HACK: If there are currently multiple touches, register the key even if
6535e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // the finger slides off the key. This defends against noise from some
6545e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // touch panels when there are close multiple touches.
6555e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // Caveat: When in chording input mode with a modifier key, we don't use
6565e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // this hack.
6575e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        if (me != null && me.getPointerCount() > 1
6585e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
659eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                            onUpEventInternal(x, y, eventTime);
6605e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        }
661d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                        mKeyAlreadyProcessed = true;
662e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                        setReleasedKeyGraphics(oldKey);
663faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    }
66467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
665c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
6666a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
667e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, key)) {
6689e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid out from the previous key, we must call onRelease() to
6699e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // notify that the previous key has been released.
670e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
671e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
672996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
67398b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka                mTimerProxy.cancelLongPressTimer();
67467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
675e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    onMoveToNewKey(key, x, y);
67667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
677d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                    mKeyAlreadyProcessed = true;
67867a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
67907221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
6806a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6826a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
683906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onUpEvent(int x, int y, long eventTime) {
684dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
685dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
6861d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
6875c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
6881d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
689eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            if (!sGestureTracker.isInGesture()) {
690eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                if (mCurrentKey != null && mCurrentKey.isModifier()) {
691eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    // Before processing an up event of modifier key, all pointers already being
692eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    // tracked should be released.
693eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    queue.releaseAllPointersExcept(this, eventTime);
694eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                } else {
695eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    queue.releaseAllPointersOlderThan(this, eventTime);
696eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                }
6971d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
6981d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
6991d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
700eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        onUpEventInternal(x, y, eventTime);
7011d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
7021d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
703d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
704d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
705d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
706d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka    public void onPhantomUpEvent(int x, int y, long eventTime) {
707c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (DEBUG_EVENT)
708c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            printTouchEvent("onPhntEvent:", x, y, eventTime);
709eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        onUpEventInternal(x, y, eventTime);
710d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
7111d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
7121d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
713eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    private void onUpEventInternal(int x, int y, long eventTime) {
7142321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
715cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
7165a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
7175a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
7189d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
7199d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
7209d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
7219ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
722eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
723eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (sGestureTracker.isInGesture()) {
724eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Register up event on gesture tracker.
725eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            sGestureTracker.onUpEvent(this, x, y, eventTime);
726eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            if (!sPointerTrackerQueue.isAnyInSlidingKeyInput()) {
727eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
728eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                sGestureTracker.endBatchInput();
729eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
730eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            if (mCurrentKey != null) {
731eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
732eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
733eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            mCurrentKey = null;
734eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
735eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        } else {
736eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
737eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            sGestureTracker.endBatchInput();
738eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
739eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
740d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        if (mKeyAlreadyProcessed)
741d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
7420d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka        if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
7435a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka            detectAndSendKey(mCurrentKey, mKeyX, mKeyY);
7446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
7456a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
7466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
747e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka    public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) {
7489ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        onLongPressed();
749e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka        onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
7509d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mIsShowingMoreKeysPanel = true;
751eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
752eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        sGestureTracker.abortBatchInput();
7539ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
7549ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
755906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
756d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
757e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
7585c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
7591ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        if (queue != null) {
760d00d963b9d47c1bba6f65534033a33fe7c30dde5Tadashi G. Takaoka            queue.remove(this);
7611ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        }
762d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
763d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
764906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onCancelEvent(int x, int y, long eventTime) {
765dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
766dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
7671d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
7685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
769c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (queue != null) {
770d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka            queue.releaseAllPointersExcept(this, eventTime);
7711d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
772c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        }
773baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
7741d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
7751d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
776baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
7772321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
778e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
779cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
7809d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
7819d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
7829d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
7839ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
7846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
7856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
786e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void startRepeatKey(Key key) {
787eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (key != null && key.isRepeatable() && !sGestureTracker.isInGesture()) {
7886662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv            onRegisterKey(key);
789a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startKeyRepeatTimer(this);
790a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        }
791a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
792a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
7936662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv    public void onRegisterKey(Key key) {
794c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
795e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            detectAndSendKey(key, key.mX, key.mY);
7960f87ca72f7c569f7db075701767e504c75952182Tadashi G. Takaoka            if (!key.altCodeWhileTyping() && !key.isModifier()) {
7970f87ca72f7c569f7db075701767e504c75952182Tadashi G. Takaoka                mTimerProxy.startTypingStateTimer();
7980f87ca72f7c569f7db075701767e504c75952182Tadashi G. Takaoka            }
799c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
8006a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
8016a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
802e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, Key newKey) {
803b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        if (mKeyDetector == null)
804a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
805e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key curKey = mCurrentKey;
8066a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
807e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
808e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        } else if (curKey != null) {
809e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            return curKey.squaredDistanceToEdge(x, y)
810a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka                    >= mKeyDetector.getKeyHysteresisDistanceSquared();
8116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
812e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
8136a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
8146a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
8156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
816e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void startLongPressTimer(Key key) {
817eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (key != null && key.isLongPressEnabled() && !sGestureTracker.isInGesture()) {
818a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startLongPressTimer(this);
8194189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        }
82066e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
82166e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
822e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private void detectAndSendKey(Key key, int x, int y) {
82383e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
8248aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
825dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
826dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
82766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
8282013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        int code = key.mCode;
829ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        callListenerOnCodeInput(key, code, x, y);
8302013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        callListenerOnRelease(key, code, false);
8316a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
8326a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
833dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private void printTouchEvent(String title, int x, int y, long eventTime) {
834723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
835e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
836e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
837d7edd1cbb5dfacacdd39766e379fb60d2b7c6b73Tadashi G. Takaoka                (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, eventTime, code));
838dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
8396e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
840