PointerTracker.java revision 93b5c2ce63705e7ebffd9bdb7358100e8d5b5235
16a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka/*
28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2010 The Android Open Source Project
36a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
46a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); you may not
56a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * use this file except in compliance with the License. You may obtain a copy of
66a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * the License at
76a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
86a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0
96a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
106a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
136a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * License for the specific language governing permissions and limitations under
146a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * the License.
156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka */
166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
195509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport android.content.res.TypedArray;
20e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaokaimport android.os.SystemClock;
21c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport android.util.Log;
228ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaokaimport android.view.MotionEvent;
23c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
24918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
25f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStroke;
2680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokeParams;
27c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
2872934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
295f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaokaimport com.android.inputmethod.latin.CollectionUtils;
30240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaokaimport com.android.inputmethod.latin.Constants;
31f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.latin.InputPointers;
32faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
335509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport com.android.inputmethod.latin.R;
349bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
356b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
366a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
375c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
38dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
39a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class PointerTracker implements PointerTrackerQueue.Element {
40dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
41dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
42dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
43dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
44b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG || DEBUG_EVENT;
4540a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
460657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    /** True if {@link PointerTracker}s should handle gesture events. */
470657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    private static boolean sShouldHandleGesture = false;
488335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sMainDictionaryAvailable = false;
498335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByInputField = false;
508335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByUser = false;
51918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
52f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
53f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
54f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
55f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
67f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
68f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
69f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
70f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
71f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
72f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
73f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
74f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
75f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
76f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
77f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
78f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
799d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public interface DrawingProxy extends MoreKeysPanel.Controller {
806a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
81e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
82d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
83f0e83046f022b5594f98aef07fbda7f2f837f262Tadashi G. Takaoka        public void showGesturePreviewTrail(PointerTracker tracker, boolean isOldestTracker);
846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
862321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
87d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        public void startTypingStateTimer(Key typedKey);
8873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
89a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
90a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker);
91a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code);
9298b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
930ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer();
94beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer();
950ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout();
962321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
9729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
9829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
9929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
100d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            public void startTypingStateTimer(Key typedKey) {}
10193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
10273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
10393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
104a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker) {}
10529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
106a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(PointerTracker tracker) {}
107a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            @Override
108a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(int code) {}
10929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
11029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelLongPressTimer() {}
11129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
1120ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public void startDoubleTapTimer() {}
1130ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
114beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            public void cancelDoubleTapTimer() {}
115beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            @Override
1160ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public boolean isInDoubleTapTimeout() { return false; }
1170ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
11829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelKeyTimers() {}
11929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
1202321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    }
1212321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka
122a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    static final class PointerTrackerParams {
1235509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final boolean mSlidingKeyInputEnabled;
1245509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
125b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public final int mTouchNoiseThresholdDistance;
1263623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public final int mSuppressKeyPreviewAfterBatchInputDuration;
1275509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1285509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
1295509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1305509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private PointerTrackerParams() {
1315509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
1325509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = 0;
133b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = 0;
1343623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = 0;
1355509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1365509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
13780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
1385509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
1395509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
1405509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
1415509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
142b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
1435509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
1443623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
1453623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
1465509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1475509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    }
1485509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
149160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
1505509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    private static PointerTrackerParams sParams;
15180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    private static GestureStrokeParams sGestureStrokeParams;
152d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
153b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // Move this threshold to resource.
154b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // TODO: Device specific parameter would be better for device specific hack?
155b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
156b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // This hack might be device specific.
157b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final boolean sNeedsProximateBogusDownMoveUpEventHack = true;
1585c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1595f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
16093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
1615c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1625c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1636a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1640efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1650efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
166a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
167dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private KeyboardActionListener mListener = EMPTY_LISTENER;
168baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1695a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
170b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private int mPhantonSuddenMoveThreshold;
171b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
1726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1736c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
1746c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
1756c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sGestureFirstDownTime;
1763623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private static TimeRecorder sTimeRecorder;
1776c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static final InputPointers sAggregratedPointers = new InputPointers(
1786c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            GestureStroke.DEFAULT_CAPACITY);
17958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
18058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
1819580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
182b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static final class BogusMoveEventDetector {
183b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Move these thresholds to resource.
184d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        // These thresholds' unit is a diagonal length of a key.
185d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
186d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
187b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
188b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mAccumulatedDistanceThreshold;
189b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mRadiusThreshold;
190b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
191b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Accumulated distance from actual and artificial down keys.
192b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int mAccumulatedDistanceFromDownKey;
193b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownX;
194b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownY;
195b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
196d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
197d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
198b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceThreshold = (int)(
199d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
200d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
201b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
202b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
203b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onActualDownEvent(final int x, final int y) {
204b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownX = x;
205b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownY = y;
206b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
207b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
208b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onDownKey() {
209b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey = 0;
210b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
211b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
212b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onMoveKey(final int distance) {
213b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey += distance;
214b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
215b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
216d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public boolean hasTraveledLongDistance(final int x, final int y) {
217d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dx = Math.abs(x - mActualDownX);
218d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dy = Math.abs(y - mActualDownY);
219d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // A bogus move event should be a horizontal movement. A vertical movement might be
220d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // a sloppy typing and should be ignored.
221d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
222b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
223b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
224b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int getDistanceFromDownEvent(final int x, final int y) {
225b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistance(x, y, mActualDownX, mActualDownY);
226b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
227b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
228b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isCloseToActualDownEvent(final int x, final int y) {
229b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
230b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
231b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
232b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2333623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    static final class TimeRecorder {
2343623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mSuppressKeyPreviewAfterBatchInputDuration;
2353623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mStaticTimeThresholdAfterFastTyping; // msec
2363623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastTypingTime;
2373623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastLetterTypingTime;
2383623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastBatchInputTime;
2393623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2403623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
2413623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                final GestureStrokeParams gestureStrokeParams) {
2423623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration =
2433623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
2443623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping =
2453623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
2463623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2473623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
248b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isInFastTyping(final long eventTime) {
249b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
250b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
251b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
252b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2537a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        private boolean wasLastInputTyping() {
2547a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return mLastTypingTime >= mLastBatchInputTime;
2553623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2563623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2573623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onCodeInput(final int code, final long eventTime) {
2587a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // Record the letter typing time when
2597a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 1. Letter keys are typed successively without any batch input in between.
2607a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 2. A letter key is typed within the threshold time since the last any key typing.
2617a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 3. A non-letter key is typed within the threshold time since the last letter key
2627a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // typing.
2637a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (Character.isLetter(code)) {
2647a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                if (wasLastInputTyping()
2657a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                        || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
2667a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2673623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2683623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            } else {
2693623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
2703623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    // This non-letter typing should be treated as a part of fast typing.
2717a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2723623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2733623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            }
2747a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastTypingTime = eventTime;
2753623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2763623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2773623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onEndBatchInput(final long eventTime) {
2787a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastBatchInputTime = eventTime;
2793623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2803623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2813623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public long getLastLetterTypingTime() {
2823623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            return mLastLetterTypingTime;
2833623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2843623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2853623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
2867a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return !wasLastInputTyping()
2873623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
2883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    }
2903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2918a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
2928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
2938a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
2948a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
295e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
296e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
297e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
2988a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
2998a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
3008a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3018a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
3028a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
3038a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
3046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3051a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
3061a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
3071a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
3089ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    // true if event is already translated to a key action.
309c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    private boolean mKeyAlreadyProcessed;
310c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
3119d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // true if this pointer has been long-pressed and is showing a more keys panel.
3129d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private boolean mIsShowingMoreKeysPanel;
3139ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
314f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input.
3155c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
316f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input from a modifier key,
317f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // so that further modifier keys should be ignored.
318f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    boolean mIsInSlidingKeyInputFromModifier;
319cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
320f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if a sliding key input is allowed.
32167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
32267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
323dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    // Empty {@link KeyboardActionListener}
324e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka    private static final KeyboardActionListener EMPTY_LISTENER =
325e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka            new KeyboardActionListener.Adapter();
3266e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka
327c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
328f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
32993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    public static void init(final boolean needsPhantomSuddenMoveEventHack) {
330d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
3315509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = PointerTrackerParams.DEFAULT;
33280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = GestureStrokeParams.DEFAULT;
3333623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
334160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
33593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3365509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    public static void setParameters(final TypedArray mainKeyboardViewAttr) {
3375509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
33880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
3393623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
3406a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3416a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3428335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
3438335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
3448335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
3458335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
3468335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
3478335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3488335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
3498335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
350694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
3518335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
3528335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
3538335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3548335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
355694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
3568335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
3578335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
358918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
359918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
360694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
361b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
3625c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3635c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
3645c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
3655c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
3665c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
3675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3695c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
3705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3725c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
37393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.isAnyInSlidingKeyInput();
3745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
376694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
377afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
378afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
379afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
3805c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
3815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3835c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
384694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
385afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
386afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
387afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
3885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
3895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
3905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
3915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
392918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
3938335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
3948335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
3955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3965c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
397dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
398afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
399afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
400afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
401e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
4025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
405694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private PointerTracker(final int id, final KeyEventHandler handler) {
406694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (handler == null) {
4075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
408694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
4095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
41080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
41180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                id, sGestureStrokeParams);
4125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
4135c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
4145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
4155c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
4166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
4181a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
419694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
4206c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
421eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
422eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
423f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
424e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
42558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onPress    : %s%s%s", mPointerId,
42658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    KeyDetector.printableCode(key),
42758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ignoreModifierKey ? " ignoreModifier" : "",
42858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ? "" : " disabled"));
429e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
43093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
431996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
43293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
433e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
4342a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onPressKey(key.mCode);
435690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
436690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
437d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
438690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
439690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
440690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
441dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
442dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
443690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
444690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
445694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
4463623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            final int y, final long eventTime) {
447f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
4486bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
44929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
4502013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
451240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            final String output = code == Constants.CODE_OUTPUT_TEXT
452240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    ? key.getOutputText() : Constants.printableCode(code);
45358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
45458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    output, ignoreModifierKey ? " ignoreModifier" : "",
45558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
4562013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
4579bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
4589bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
4599bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
4609bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
461e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
462996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
463e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
4646bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
4656bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
4663623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            sTimeRecorder.onCodeInput(code, eventTime);
467240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
46829d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                mListener.onTextInput(key.getOutputText());
469240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            } else if (code != Constants.CODE_UNSPECIFIED) {
470ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
4712013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
47293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
473dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
474dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
475f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may be in shifted state and the
476690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
477694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
478694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
4796c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
480eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
481eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
482f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
483e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
48458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
485240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    Constants.printableCode(primaryCode),
48658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
48758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ?  "": " disabled"));
488e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
4899bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
4909bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
4919bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
4929bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
493e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
494996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
495e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
49693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
4972a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
49893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
499dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
500dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
5018aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
502694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
50358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
504694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
5059bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
5069bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
5079bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5088aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
509dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
510dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
511694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
51258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
51358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
51458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return;
51558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
516a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
5175a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
518d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyWidth = mKeyboard.mMostCommonKeyWidth;
519d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyHeight = mKeyboard.mMostCommonKeyHeight;
520d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth);
5218a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
5228a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
5238a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
5248a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
5258a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
52644972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
5278a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
528b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
529d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
5305a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
5315a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
5320cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
533cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
534cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
535cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
536cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
537e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
538e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
539dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
540dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
5410cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
5422aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
543e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
5442aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
5452aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
546694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
547723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
5482aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
5492aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
550694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setReleasedKeyGraphics(final Key key) {
551d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
5526bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
553faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
554faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
555faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
5566bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
557faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
558faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
559faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
560faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
561faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
562faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
5632013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
5642013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
565faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
5662013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
567faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
56829d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
569faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
570faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
571faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
572faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
573faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
57429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
575faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
5762013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
5772013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
5786a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
5796a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
5806a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
58129d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
58229d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!sShouldHandleGesture) return false;
5833623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
58429d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    }
58529d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka
58629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void setPressedKeyGraphics(final Key key, final long eventTime) {
5876bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
5886bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
5896bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
5906bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
5916bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
5926bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
5936bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
5946bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
595faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
596faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
597faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
59829d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
599faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
600faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
601faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
602faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
603faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
604faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
605faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
606faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
6072013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6082013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
609faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6102013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
61173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
61229d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
613faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
614faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
615faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
616faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
617faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
61829d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
619faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
6202013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6212013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
622d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
623c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
624c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
625694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updateReleaseKeyGraphics(final Key key) {
626faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
627faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
628faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
629faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
630694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updatePressKeyGraphics(final Key key) {
631faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
632faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
633faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
634faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
635c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
636c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return mGestureStrokeWithPreviewPoints;
6372f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
6382f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
6398a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastX() {
6408a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastX;
6418a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6428a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
6438a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastY() {
6448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastY;
6458a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6468a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
6478a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
6488a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
6498a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6508a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
651694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
6528a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
653b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onDownKey();
6548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
6558a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6568a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
657b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static int getDistance(final int x1, final int y1, final int x2, final int y2) {
658b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        return (int)Math.hypot(x1 - x2, y1 - y2);
659b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
660b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
661694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
662b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
6638a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
6648a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
665723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
6668a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6678a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
668694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
6698a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
6708a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6718a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
672694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
673e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
6748a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
6758a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
676e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
6778a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6788a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
6796c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int getActivePointerTrackerCount() {
68093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.size();
6816c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
6826c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
68313ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    private static boolean isOldestTrackerInQueue(final PointerTracker tracker) {
68493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.getOldestElement() == tracker;
68513ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    }
68613ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka
6871645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka    private void mayStartBatchInput(final Key key) {
688c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
6899c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            return;
6909c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        }
6911645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        if (key == null || !Character.isLetter(key.mCode)) {
6921645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            return;
6931645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        }
6949c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
69558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
6969580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
6979c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        sInGesture = true;
69858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
69958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sAggregratedPointers.reset();
70058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionPointSize = 0;
70158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionTime = 0;
70258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mListener.onStartBatchInput();
70358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
704ad181915f78235bc09e88c85ed9df669801b8442Tadashi G. Takaoka        mTimerProxy.cancelLongPressTimer();
70513ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
7066c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7076c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
708157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka    private void mayUpdateBatchInput(final long eventTime, final Key key) {
709157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka        if (key != null) {
710157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            synchronized (sAggregratedPointers) {
71180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
71280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                stroke.appendIncrementalBatchPoints(sAggregratedPointers);
713157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                final int size = sAggregratedPointers.getPointerSize();
714157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                if (size > sLastRecognitionPointSize
71580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                        && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
716157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    sLastRecognitionPointSize = size;
717157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    sLastRecognitionTime = eventTime;
718157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    if (DEBUG_LISTENER) {
71958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                        Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d",
72058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                                mPointerId, size));
721157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    }
722157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    mListener.onUpdateBatchInput(sAggregratedPointers);
7236c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
7246c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
7259580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
72613ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
7279580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7289580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
72929d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void mayEndBatchInput(final long eventTime) {
7306c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        synchronized (sAggregratedPointers) {
731c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
7326c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
7336c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                if (DEBUG_LISTENER) {
73458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
73558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                            mPointerId, sAggregratedPointers.getPointerSize()));
7366c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
7376c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sInGesture = false;
7383623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                sTimeRecorder.onEndBatchInput(eventTime);
7396c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mListener.onEndBatchInput(sAggregratedPointers);
7406c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
7419580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
74213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
7439580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7449580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
745694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
746694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
7478ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
7488ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
7498ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
7508ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
7518ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
7528ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
7538ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
7548ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
7558ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
7568ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
7573314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
7588ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
7598ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
7608ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
7618ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
7628ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
7638ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
7648ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
765694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long eventTime,
766694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
767694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
768dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
769694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
7701d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
771f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
77263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
773f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
774f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
775baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
7768a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
777160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
778b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distance = getDistance(x, y, mLastX, mLastY);
779b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distance < sParams.mTouchNoiseThresholdDistance) {
780faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
781b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.w(TAG, String.format("[%d] onDownEvent:"
782b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            + " ignore potential noise: time=%d distance=%d",
783b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, deltaT, distance));
7849bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
785b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
7869bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
787d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
788baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
789baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
790baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
791baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
792eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
793b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onActualDownEvent(x, y);
79493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (key != null && key.isModifier()) {
79593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // Before processing a down event of modifier key, all pointers already being
79693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // tracked should be released.
79793b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            sPointerTrackerQueue.releaseAllPointers(eventTime);
7981d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
79993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.add(this);
8001d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
8016c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!sShouldHandleGesture) {
8026c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
8036c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
8044df6549c1a5ae2cdc2cdfafdad1ec2a75881134cTadashi G. Takaoka        // A gesture should start only from a non-modifier key.
805b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
8064df6549c1a5ae2cdc2cdfafdad1ec2a75881134cTadashi G. Takaoka                && !mIsShowingMoreKeysPanel && key != null && !key.isModifier();
807b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
808b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
8096c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sGestureFirstDownTime = eventTime;
8109580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
81158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
8123623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    sTimeRecorder.getLastLetterTypingTime());
813eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
8141d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
8151d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
816694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
817e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
81867a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
81932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
820160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
821c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
82232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
8231a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
824c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka        mKeyAlreadyProcessed = false;
825f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
826e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
8271a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
828e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
8291a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
8302a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
831e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
832e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
833996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
834e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
835e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
83629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            setPressedKeyGraphics(key, eventTime);
8376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
8386a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
8396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
840694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startSlidingKeyInput(final Key key) {
841e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
842f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka            mIsInSlidingKeyInputFromModifier = key.isModifier();
843e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
844996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
845996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
846996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
847f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    private void resetSlidingKeyInput() {
848f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
849f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInputFromModifier = false;
850f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    }
851f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka
8526c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
85302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
8546c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
8556c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (mIsDetectingGesture) {
85602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent);
8571645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            mayStartBatchInput(key);
858157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            if (sInGesture) {
859157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mayUpdateBatchInput(eventTime, key);
8609580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
8619580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
8629580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8639580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
864694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
865694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
866dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
867694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
868694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyAlreadyProcessed) {
869e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
870694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
871baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
8726c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture && me != null) {
873eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
874eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
875eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
876eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
877eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
878eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
879eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
8806c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
88102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
882eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
883eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
884eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
8856c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
8866c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
8876c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
8883c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processSlidingKeyInput(final Key newKey, final int x, final int y,
8893c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime) {
8903c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // This onPress call may have changed keyboard layout. Those cases are detected
8913c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // at {@link #setKeyboard}. In those cases, we should update key according
8923c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // to the new keyboard layout.
8933c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        Key key = newKey;
8943c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
8953c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            key = onMoveKey(x, y);
8963c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
8973c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onMoveToNewKey(key, x, y);
8983c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        startLongPressTimer(key);
8993c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        setPressedKeyGraphics(key, eventTime);
9003c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
9013c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
9023c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
9033c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
9043c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
9053c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
9063c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " phantom sudden move event (distance=%d) is translated to "
9073c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
9083c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    getDistance(x, y, lastX, lastY),
9093c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
9103c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
9113c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
9123c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // TODO: This should be moved to outside of this nested if-clause?
9133c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (ProductionFlag.IS_EXPERIMENTAL) {
9143c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
9153c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
9163c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onUpEventInternal(eventTime);
9173c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
9183c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
9193c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
9203c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
9213c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
9223c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
9233c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(
9243c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
9253c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float radiusRatio =
9263c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
9273c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    / keyDiagonal;
9283c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
9293c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " bogus down-move-up event (raidus=%.2f key diagonal) is "
9303c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
9313c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mPointerId, radiusRatio,
9323c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.mCode),
9333c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    x, y, Constants.printableCode(key.mCode)));
9343c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
9353c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onUpEventInternal(eventTime);
9363c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
9373c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
9383c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
939831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    private void processSildeOutFromOldKey(final Key oldKey) {
940831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        setReleasedKeyGraphics(oldKey);
941831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        callListenerOnRelease(oldKey, oldKey.mCode, true);
942831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        startSlidingKeyInput(oldKey);
943831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
944831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    }
945831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka
9463c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void slideFromOldKeyToNewKey(final Key key, final int x, final int y,
9478b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
9488b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid in to the new key from the previous key, we must call
9498b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onRelease() first to notify that the previous key has been released, then call
9508b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onPress() to notify that the new key is being pressed.
951831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
9528b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        startRepeatKey(key);
9538b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
9543c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            processSlidingKeyInput(key, x, y, eventTime);
9552a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
9562a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive touches may be reported as a sudden move by
9572a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // touch panel firmware. This hack detects such cases and translates the move event to
9582a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // successive up and down events.
9592a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // TODO: Should find a way to balance gesture detection and this hack.
9602a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsPhantomSuddenMoveEventHack
9612a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
9622a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
9632a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
9642a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive proximate touches may be reported as a bogus
9652a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // down-move-up event by touch panel firmware. This hack detects such cases and breaks
9662a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // these events into separate up and down events.
9672a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime)
9682a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
9692a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
9702a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
9712a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: If there are currently multiple touches, register the key even if the finger
9722a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // slides off the key. This defends against noise from some touch panels when there are
9732a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // close multiple touches.
9742a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // Caveat: When in chording input mode with a modifier key, we don't use this hack.
97593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        else if (getActivePointerTrackerCount() > 1
9762a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
9772a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (DEBUG_MODE) {
9782a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                Log.w(TAG, String.format("[%d] onMoveEvent:"
9792a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                        + " detected sliding finger while multi touching", mPointerId));
9803c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            }
9812a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
9822a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            mKeyAlreadyProcessed = true;
9832a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
9842a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        } else {
9852a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (!mIsDetectingGesture) {
9862a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
9878b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
9882a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey);
9898b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
9908b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
9918b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
9928b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    private void slideOutFromOldKey(final Key oldKey, final int x, final int y) {
9938b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid out from the previous key, we must call onRelease() to
9948b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // notify that the previous key has been released.
995831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        processSildeOutFromOldKey(oldKey);
9968b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (mIsAllowedSlidingKeyInput) {
9978b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onMoveToNewKey(null, x, y);
9988b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        } else {
9998b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (!mIsDetectingGesture) {
10008b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
10018b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
10028b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
10038b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
10048b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
10056c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
10068a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
10078a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
1008e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
10098b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        final Key newKey = onMoveKey(x, y);
1010eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
10116c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture) {
10126c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
10138b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
10146c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
10156c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
10166c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
10176c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
10186c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
1019eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
1020eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
10218b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (newKey != null) {
10223c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
10238b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
10243c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            } else if (oldKey == null) {
1025831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
1026831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
1027831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                processSlidingKeyInput(newKey, x, y, eventTime);
1028c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
1029831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        } else { // newKey == null
10308b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
10318b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka                slideOutFromOldKey(oldKey, x, y);
103207221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
10336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
10346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1036694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onUpEvent(final int x, final int y, final long eventTime) {
1037694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1038dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
1039694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
10401d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
104193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (!sInGesture) {
104293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            if (mCurrentKey != null && mCurrentKey.isModifier()) {
104393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
104493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // tracked should be released.
104593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
104693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            } else {
104793b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
10481d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
10491d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
1050630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        onUpEventInternal(eventTime);
105193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
10521d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
10531d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1054d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
1055d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
1056d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
10570cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
1058694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
1059694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
10600cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka            printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
1061694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1062630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        onUpEventInternal(eventTime);
1063d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
10641d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
10651d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1066630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka    private void onUpEventInternal(final long eventTime) {
10672321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1068f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
10696c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
10706c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
10716c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
10725a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
10736c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        setReleasedKeyGraphics(currentKey);
10749d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
10759d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
10769d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
10779ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
1078eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
10796c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
10806c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
10816c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.mCode, true);
1082eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
108329d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            mayEndBatchInput(eventTime);
1084eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
1085eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
108658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
1087694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyAlreadyProcessed) {
1088d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
1089694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
10906c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (currentKey != null && !currentKey.isRepeatable()) {
10913623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
10926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
10936a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10946a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1095694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) {
10969ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        onLongPressed();
10979d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mIsShowingMoreKeysPanel = true;
1098cc3500b0c8f11e8de1ad7376dda466d30637e462Tadashi G. Takaoka        onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
10999ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
11009ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
1101906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
1102d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
1103e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
110493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
1105d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
1106d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
1107694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onCancelEvent(final int x, final int y, final long eventTime) {
1108694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1109dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
1110694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
11111d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
111293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
111393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
1114baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
11151d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
11161d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1117baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
11182321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1119e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
1120f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
11219d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
11229d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
11239d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
11249ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
11256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1127694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
11286c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (key != null && key.isRepeatable() && !sInGesture) {
11296662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv            onRegisterKey(key);
1130a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startKeyRepeatTimer(this);
1131a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        }
1132a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
1133a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
1134694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onRegisterKey(final Key key) {
1135c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
11363623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
1137d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
1138c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
11396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11406a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1141b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
1142b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final Key newKey) {
1143694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyDetector == null) {
1144a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
1145694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1146694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
11476a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1148e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1149a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1150a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (curKey == null /* && newKey != null */) {
1151a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1152a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1153a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        // Here curKey points to the different key from newKey.
1154a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
1155a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                mIsInSlidingKeyInputFromModifier);
1156a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
1157a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
1158a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1159a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
1160a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        / mKeyboard.mMostCommonKeyWidth;
1161a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1162a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
1163b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1164a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1165a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1166a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
1167a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && sTimeRecorder.isInFastTyping(eventTime)
1168a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
1169a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1170a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float keyDiagonal = (float)Math.hypot(
1171a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
1172a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float lengthFromDownRatio =
1173a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
1174a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1175a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        + " %.2f key diagonal from virtual down point",
1176a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mPointerId, lengthFromDownRatio));
1177b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1178e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
11796a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1180a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        return false;
11816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11826a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1183694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
11846c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (key != null && key.isLongPressEnabled() && !sInGesture) {
1185a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startLongPressTimer(this);
11864189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        }
118766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
118866e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
11893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
119083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
11918aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1192dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1193dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
119466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
1195694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final int code = key.mCode;
11963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        callListenerOnCodeInput(key, code, x, y, eventTime);
11972013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        callListenerOnRelease(key, code, false);
11986a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11996a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1200694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1201694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1202723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1203e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
120458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
120558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                (mKeyAlreadyProcessed ? "-" : " "), title, x, y, eventTime, code));
1206dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
12076e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1208