PointerTracker.java revision d631778e1c1ffcdf28129894239e7ee7d6f399fc
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;
30f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.latin.InputPointers;
31faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
325509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport com.android.inputmethod.latin.R;
339bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
346b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
365c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
37dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
38a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class PointerTracker implements PointerTrackerQueue.Element {
39dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
40dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
41dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
42dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
43b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG || DEBUG_EVENT;
4440a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
450657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    /** True if {@link PointerTracker}s should handle gesture events. */
460657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    private static boolean sShouldHandleGesture = false;
478335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sMainDictionaryAvailable = false;
488335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByInputField = false;
498335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static boolean sGestureHandlingEnabledByUser = false;
50918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
51f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
52f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
53f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
54f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
55f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
66f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
67f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
68f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
69f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
70f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
71f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
72f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
73f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
74f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
75f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
76f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
77f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
789d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public interface DrawingProxy extends MoreKeysPanel.Controller {
796a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
80e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        public void showKeyPreview(PointerTracker tracker);
81d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
82f0e83046f022b5594f98aef07fbda7f2f837f262Tadashi G. Takaoka        public void showGesturePreviewTrail(PointerTracker tracker, boolean isOldestTracker);
836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
852321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
86d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        public void startTypingStateTimer(Key typedKey);
8773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState();
88a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker);
89a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker);
90a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code);
9198b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
920ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer();
93beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer();
940ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout();
952321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
9629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
9729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public static class Adapter implements TimerProxy {
9829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
99d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            public void startTypingStateTimer(Key typedKey) {}
10093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
10173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            public boolean isTypingState() { return false; }
10293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            @Override
103a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startKeyRepeatTimer(PointerTracker tracker) {}
10429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
105a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(PointerTracker tracker) {}
106a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            @Override
107a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            public void startLongPressTimer(int code) {}
10829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
10929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelLongPressTimer() {}
11029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            @Override
1110ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public void startDoubleTapTimer() {}
1120ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
113beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            public void cancelDoubleTapTimer() {}
114beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            @Override
1150ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            public boolean isInDoubleTapTimeout() { return false; }
1160ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            @Override
11729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            public void cancelKeyTimers() {}
11829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
1192321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    }
1202321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka
121a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    static final class PointerTrackerParams {
1225509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final boolean mSlidingKeyInputEnabled;
1235509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
124b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public final int mTouchNoiseThresholdDistance;
1253623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public final int mSuppressKeyPreviewAfterBatchInputDuration;
1265509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1275509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
1285509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1295509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private PointerTrackerParams() {
1305509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
1315509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = 0;
132b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = 0;
1333623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = 0;
1345509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1355509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
13680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
1375509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
1385509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
1395509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
1405509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
141b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
1425509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
1433623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
1443623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
1455509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
1465509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    }
1475509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
148160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
1495509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    private static PointerTrackerParams sParams;
15080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    private static GestureStrokeParams sGestureStrokeParams;
151d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
152b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // Move this threshold to resource.
153b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // TODO: Device specific parameter would be better for device specific hack?
154b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
155b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // This hack might be device specific.
156b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final boolean sNeedsProximateBogusDownMoveUpEventHack = true;
1575c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1585f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
1595c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static PointerTrackerQueue sPointerTrackerQueue;
1605c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1615c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
1626a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1630efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
1640efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
165a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
166dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private KeyboardActionListener mListener = EMPTY_LISTENER;
167baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1685a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
169b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private int mPhantonSuddenMoveThreshold;
170b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
1716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1726c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
1736c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
1746c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static long sGestureFirstDownTime;
1753623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private static TimeRecorder sTimeRecorder;
1766c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static final InputPointers sAggregratedPointers = new InputPointers(
1776c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            GestureStroke.DEFAULT_CAPACITY);
17858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
17958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
1809580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
181b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static final class BogusMoveEventDetector {
182b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Move these thresholds to resource.
183d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        // These thresholds' unit is a diagonal length of a key.
184d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
185d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
186b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
187b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mAccumulatedDistanceThreshold;
188b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mRadiusThreshold;
189b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
190b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        // Accumulated distance from actual and artificial down keys.
191b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int mAccumulatedDistanceFromDownKey;
192b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownX;
193b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        private int mActualDownY;
194b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
195d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
196d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
197b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceThreshold = (int)(
198d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
199d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
200b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
201b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
202b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onActualDownEvent(final int x, final int y) {
203b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownX = x;
204b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mActualDownY = y;
205b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
206b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
207b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onDownKey() {
208b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey = 0;
209b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
210b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
211b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public void onMoveKey(final int distance) {
212b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mAccumulatedDistanceFromDownKey += distance;
213b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
214b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
215d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        public boolean hasTraveledLongDistance(final int x, final int y) {
216d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dx = Math.abs(x - mActualDownX);
217d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            final int dy = Math.abs(y - mActualDownY);
218d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // A bogus move event should be a horizontal movement. A vertical movement might be
219d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            // a sloppy typing and should be ignored.
220d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka            return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
221b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
222b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
223b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        /* package */ int getDistanceFromDownEvent(final int x, final int y) {
224b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistance(x, y, mActualDownX, mActualDownY);
225b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
226b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
227b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isCloseToActualDownEvent(final int x, final int y) {
228b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
229b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
230b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
231b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2323623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    static final class TimeRecorder {
2333623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mSuppressKeyPreviewAfterBatchInputDuration;
2343623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private final int mStaticTimeThresholdAfterFastTyping; // msec
2353623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastTypingTime;
2363623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastLetterTypingTime;
2373623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        private long mLastBatchInputTime;
2383623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2393623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
2403623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                final GestureStrokeParams gestureStrokeParams) {
2413623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration =
2423623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
2433623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping =
2443623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
2453623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2463623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
247b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public boolean isInFastTyping(final long eventTime) {
248b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
249b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
250b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        }
251b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
2527a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        private boolean wasLastInputTyping() {
2537a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return mLastTypingTime >= mLastBatchInputTime;
2543623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2553623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2563623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onCodeInput(final int code, final long eventTime) {
2577a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // Record the letter typing time when
2587a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 1. Letter keys are typed successively without any batch input in between.
2597a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 2. A letter key is typed within the threshold time since the last any key typing.
2607a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // 3. A non-letter key is typed within the threshold time since the last letter key
2617a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            // typing.
2627a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (Character.isLetter(code)) {
2637a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                if (wasLastInputTyping()
2647a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                        || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
2657a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2663623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2673623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            } else {
2683623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
2693623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    // This non-letter typing should be treated as a part of fast typing.
2707a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka                    mLastLetterTypingTime = eventTime;
2713623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                }
2723623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            }
2737a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastTypingTime = eventTime;
2743623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2753623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2763623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public void onEndBatchInput(final long eventTime) {
2777a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            mLastBatchInputTime = eventTime;
2783623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2793623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2803623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public long getLastLetterTypingTime() {
2813623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            return mLastLetterTypingTime;
2823623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2833623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2843623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
2857a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return !wasLastInputTyping()
2863623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
2873623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        }
2883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    }
2893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka
2908a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
2918a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
2928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
2938a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
294e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
295e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
296e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
2978a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
2988a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
2998a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3008a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
3018a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
3028a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
3036a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3041a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
3051a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
3061a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
3079ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    // true if event is already translated to a key action.
308c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    private boolean mKeyAlreadyProcessed;
309c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
3109d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // true if this pointer has been long-pressed and is showing a more keys panel.
3119d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private boolean mIsShowingMoreKeysPanel;
3129ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
313f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input.
3145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
315f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if this pointer is in a sliding key input from a modifier key,
316f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // so that further modifier keys should be ignored.
317f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    boolean mIsInSlidingKeyInputFromModifier;
318cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
319f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // true if a sliding key input is allowed.
32067a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
32167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
322dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    // Empty {@link KeyboardActionListener}
323e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka    private static final KeyboardActionListener EMPTY_LISTENER =
324e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka            new KeyboardActionListener.Adapter();
3256e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka
326c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
327f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
328d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    public static void init(boolean hasDistinctMultitouch,
32962b8dddb6ddb057555a1665759f9cf197e480c9fTadashi G. Takaoka            boolean needsPhantomSuddenMoveEventHack) {
3305c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (hasDistinctMultitouch) {
3315c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = new PointerTrackerQueue();
3325c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        } else {
3335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = null;
3345c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
335d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
3365509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = PointerTrackerParams.DEFAULT;
33780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = GestureStrokeParams.DEFAULT;
3383623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
339160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
34093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3415509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    public static void setParameters(final TypedArray mainKeyboardViewAttr) {
3425509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
34380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
3443623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
3456a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
3478335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    private static void updateGestureHandlingMode() {
3488335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sShouldHandleGesture = sMainDictionaryAvailable
3498335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByInputField
3508335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && sGestureHandlingEnabledByUser
3518335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
3528335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3538335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
3548335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
355694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
3568335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sMainDictionaryAvailable = mainDictionaryAvailable;
3578335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
3588335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
3598335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
360694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
3618335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
3628335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
363918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
364918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
365694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
366b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
3675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
3695c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
3705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
3715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
3725c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
3755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
3775c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
3785c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
3795c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3805c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
381694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
382afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
383afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
384afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
3855c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
3865c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
3875c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
3885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
389694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
390afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
391afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
392afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
3935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
3945c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
3955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
3965c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
397918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
3988335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
3998335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        updateGestureHandlingMode();
4005c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4015c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
402dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
403afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
404afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
405afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
406e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
4075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
4085c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
4095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
410694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private PointerTracker(final int id, final KeyEventHandler handler) {
411694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (handler == null) {
4125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
413694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
4145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
41580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
41680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                id, sGestureStrokeParams);
4175c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
4185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
4195c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
4205c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
4216a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
4231a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
424694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
4256c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
426eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
427eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
428f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
429e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
43058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onPress    : %s%s%s", mPointerId,
43158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    KeyDetector.printableCode(key),
43258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ignoreModifierKey ? " ignoreModifier" : "",
43358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ? "" : " disabled"));
434e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
43593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
436996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
43793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
438e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
4392a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onPressKey(key.mCode);
440690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
441690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
442d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
443690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
444690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
445690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
446dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
447dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
448690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
449690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
450694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
4513623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            final int y, final long eventTime) {
452f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
4536bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
45429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
4552013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
45658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final String output = code == Keyboard.CODE_OUTPUT_TEXT
45758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ? key.getOutputText() : Keyboard.printableCode(code);
45858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
45958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    output, ignoreModifierKey ? " ignoreModifier" : "",
46058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
4612013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
4629bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
4639bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
4649bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    altersCode, code);
4659bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
466e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
467996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
468e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
4696bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
4706bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
4713623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            sTimeRecorder.onCodeInput(code, eventTime);
4728cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            if (code == Keyboard.CODE_OUTPUT_TEXT) {
47329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                mListener.onTextInput(key.getOutputText());
4748cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka            } else if (code != Keyboard.CODE_UNSPECIFIED) {
475ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                mListener.onCodeInput(code, x, y);
4762013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
47793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
478dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
479dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
480f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may be in shifted state and the
481690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
482694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
483694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
4846c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
485eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
486eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
487f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
488e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
48958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
49058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    Keyboard.printableCode(primaryCode),
49158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
49258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ?  "": " disabled"));
493e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
4949bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
4959bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
4969bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ignoreModifierKey);
4979bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
498e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
499996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
500e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
50193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
5022a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            mListener.onReleaseKey(primaryCode, withSliding);
50393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
504dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
505dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
5068aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
507694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
50858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
509694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
5109bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
5119bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.pointerTracker_callListenerOnCancelInput();
5129bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5138aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
514dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
515dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
516694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
51758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
51858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
51958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return;
52058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
521a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
5225a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
523d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyWidth = mKeyboard.mMostCommonKeyWidth;
524d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyHeight = mKeyboard.mMostCommonKeyHeight;
525d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth);
5268a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
5278a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        if (newKey != mCurrentKey) {
5288a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (mDrawingProxy != null) {
5298a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                setReleasedKeyGraphics(mCurrentKey);
5308a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            }
53144972bcdb6f6dd0e4f02a26f31c7effaf0525403Tadashi G. Takaoka            // Keep {@link #mCurrentKey} that comes from previous keyboard.
5328a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        }
533b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
534d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
5355a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
5365a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
5370cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
538cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
539cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
540cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
541cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
542e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
543e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
544dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
545dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
5460cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
5472aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
548e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
5492aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
5502aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
551694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
552723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
5532aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
5542aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
555694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setReleasedKeyGraphics(final Key key) {
556d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
5576bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
558faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
559faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
560faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
5616bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, update the key release graphics just in case.
562faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updateReleaseKeyGraphics(key);
563faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
564faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
565faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
566faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
567faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(shiftKey);
5682013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
5692013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
570faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
5712013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
572faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
57329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
574faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
575faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
576faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updateReleaseKeyGraphics(altKey);
577faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
578faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
57929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
580faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updateReleaseKeyGraphics(k);
5812013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
5822013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
5836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
5846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
5856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
58629d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
58729d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!sShouldHandleGesture) return false;
5883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
58929d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    }
59029d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka
59129d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void setPressedKeyGraphics(final Key key, final long eventTime) {
5926bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
5936bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
5946bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
5956bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
5966bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
5976bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
5986bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
5996bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
600faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
601faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
602faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
60329d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka        if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
604faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            mDrawingProxy.showKeyPreview(this);
605faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
606faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        updatePressKeyGraphics(key);
607faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
608faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
609faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
610faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
611faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(shiftKey);
6122013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6132013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
614faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
6152013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
61673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
61729d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
618faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
619faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
620faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                updatePressKeyGraphics(altKey);
621faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
622faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
62329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
624faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                    updatePressKeyGraphics(k);
6252013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
6262013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
627d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
628c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
629c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
630694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updateReleaseKeyGraphics(final Key key) {
631faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onReleased();
632faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
633faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
634faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
635694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void updatePressKeyGraphics(final Key key) {
636faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        key.onPressed();
637faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        mDrawingProxy.invalidateKey(key);
638faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka    }
639faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
640c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
641c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return mGestureStrokeWithPreviewPoints;
6422f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
6432f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
6448a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastX() {
6458a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastX;
6468a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6478a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
6488a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastY() {
6498a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastY;
6508a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6518a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
6528a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
6538a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
6548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6558a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
656694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
6578a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
658b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onDownKey();
6598a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
6608a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6618a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
662b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    static int getDistance(final int x1, final int y1, final int x2, final int y2) {
663b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        return (int)Math.hypot(x1 - x2, y1 - y2);
664b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
665b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
666694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
667b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
6688a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
6698a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
670723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
6718a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6728a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
673694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
6748a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
6758a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6768a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
677694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
678e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
6798a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
6808a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
681e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
6828a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
6838a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
6846c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static int getActivePointerTrackerCount() {
6856c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size();
6866c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
6876c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
6881645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka    private void mayStartBatchInput(final Key key) {
689c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
6909c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka            return;
6919c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        }
6921645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        if (key == null || !Character.isLetter(key.mCode)) {
6931645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            return;
6941645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka        }
6959c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
69658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
6979580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
6989c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        sInGesture = true;
69958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        synchronized (sAggregratedPointers) {
70058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sAggregratedPointers.reset();
70158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionPointSize = 0;
70258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            sLastRecognitionTime = 0;
70358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mListener.onStartBatchInput();
70458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
705350fab6dc4d4d4aec272056004cbb5c039f5bf37Tadashi G. Takaoka        final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
706350fab6dc4d4d4aec272056004cbb5c039f5bf37Tadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
7076c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7086c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
709157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka    private void mayUpdateBatchInput(final long eventTime, final Key key) {
710157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka        if (key != null) {
711157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            synchronized (sAggregratedPointers) {
71280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
71380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                stroke.appendIncrementalBatchPoints(sAggregratedPointers);
714157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                final int size = sAggregratedPointers.getPointerSize();
715157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                if (size > sLastRecognitionPointSize
71680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                        && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
717157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    sLastRecognitionPointSize = size;
718157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    sLastRecognitionTime = eventTime;
719157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    if (DEBUG_LISTENER) {
72058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                        Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d",
72158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                                mPointerId, size));
722157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    }
723157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                    mListener.onUpdateBatchInput(sAggregratedPointers);
7246c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
7256c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
7269580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
727f0e83046f022b5594f98aef07fbda7f2f837f262Tadashi G. Takaoka        final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
728f0e83046f022b5594f98aef07fbda7f2f837f262Tadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
7299580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7309580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
73129d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private void mayEndBatchInput(final long eventTime) {
7326c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        synchronized (sAggregratedPointers) {
733c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
7346c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
7356c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                if (DEBUG_LISTENER) {
73658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
73758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                            mPointerId, sAggregratedPointers.getPointerSize()));
7386c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                }
7396c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sInGesture = false;
7403623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                sTimeRecorder.onEndBatchInput(eventTime);
7416c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mListener.onEndBatchInput(sAggregratedPointers);
7426c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
7439580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
744350fab6dc4d4d4aec272056004cbb5c039f5bf37Tadashi G. Takaoka        final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
745350fab6dc4d4d4aec272056004cbb5c039f5bf37Tadashi G. Takaoka        mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
7469580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7479580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
748694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
749694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
7508ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
7518ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
7528ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
7538ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onDownEvent(x, y, eventTime, handler);
7548ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
7558ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
7568ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
7578ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
7588ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
7598ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
7603314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka            onMoveEvent(x, y, eventTime, null);
7618ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
7628ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
7638ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
7648ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
7658ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
7668ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
7678ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
768694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long eventTime,
769694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final KeyEventHandler handler) {
770694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
771dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
772694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
7731d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
774f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
77563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
776f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
777f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
778baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
7798a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
780160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
781b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distance = getDistance(x, y, mLastX, mLastY);
782b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distance < sParams.mTouchNoiseThresholdDistance) {
783faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
784b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.w(TAG, String.format("[%d] onDownEvent:"
785b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            + " ignore potential noise: time=%d distance=%d",
786b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, deltaT, distance));
7879bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
788b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
7899bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                }
790d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
791baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
792baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
793baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
794baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
795eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
796b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onActualDownEvent(x, y);
7976c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
7981d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
799e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            if (key != null && key.isModifier()) {
8001d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // Before processing a down event of modifier key, all pointers already being
8011d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // tracked should be released.
8021d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                queue.releaseAllPointers(eventTime);
8031d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
8041d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.add(this);
8051d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
8061d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
8076c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (!sShouldHandleGesture) {
8086c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
8096c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
810b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        // A gesture should start only from the letter key.
811b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
812b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka                && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode);
813b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
814b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka            if (getActivePointerTrackerCount() == 1) {
8156c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                sGestureFirstDownTime = eventTime;
8169580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
81758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
8183623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    sTimeRecorder.getLastLetterTypingTime());
819eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
8201d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
8211d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
822694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
823e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
82467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
82532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
826160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
827c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
82832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
8291a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
830c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka        mKeyAlreadyProcessed = false;
831f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
832e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
8331a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
834e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
8351a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
8362a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
837e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
838e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
839996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
840e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
841e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
84229d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            setPressedKeyGraphics(key, eventTime);
8436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
8446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
8456a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
846694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startSlidingKeyInput(final Key key) {
847e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (!mIsInSlidingKeyInput) {
848f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka            mIsInSlidingKeyInputFromModifier = key.isModifier();
849e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
850996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
851996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
852996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
853f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    private void resetSlidingKeyInput() {
854f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
855f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInputFromModifier = false;
856f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    }
857f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka
8586c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
85902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
8606c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
8616c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (mIsDetectingGesture) {
86202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent);
8631645902cce7eaceff4aba3ea01d723240c6ce189Tadashi G. Takaoka            mayStartBatchInput(key);
864157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka            if (sInGesture) {
865157fe98fd439a7d9cc063a7f5573f688e33c2f29Tadashi G. Takaoka                mayUpdateBatchInput(eventTime, key);
8669580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
8679580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
8689580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
8699580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
870694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
871694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
872dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
873694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
874694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyAlreadyProcessed) {
875e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
876694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
877baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
8786c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture && me != null) {
879eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
880eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
881eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
882eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
883eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
884eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
885eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
8866c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
88702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
888eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
889eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
890eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
8916c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
8926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
8936c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
8946c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
8958a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
8968a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
897e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
898e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onMoveKey(x, y);
899eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
9006c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sShouldHandleGesture) {
9016c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
90202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key);
9036c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
904c9ba26994b946d35c375cd1cd9a6db2b23b3de7eTadashi G. Takaoka                mTimerProxy.cancelLongPressTimer();
9056c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
9066c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
9076c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
9086c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
909eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
910eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
911e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
912c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka            if (oldKey == null) {
9139e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
9149e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
9151a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // This onPress call may have changed keyboard layout. Those cases are detected at
916e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                // {@link #setKeyboard}. In those cases, we should update key according to the
9171a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // new keyboard layout.
9182a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
919e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    key = onMoveKey(x, y);
920e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                }
921e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                onMoveToNewKey(key, x, y);
922e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                startLongPressTimer(key);
92329d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka                setPressedKeyGraphics(key, eventTime);
924b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            } else if (isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) {
9259e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key from the previous key, we must call
9269e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onRelease() first to notify that the previous key has been released, then call
9279e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onPress() to notify that the new key is being pressed.
928e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
929e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
930996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
9312321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka                mTimerProxy.cancelKeyTimers();
932e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                startRepeatKey(key);
93367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
9341a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // This onPress call may have changed keyboard layout. Those cases are detected
935e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    // at {@link #setKeyboard}. In those cases, we should update key according
9361a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // to the new keyboard layout.
9372a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                    if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
938e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                        key = onMoveKey(x, y);
939e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    }
940e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    onMoveToNewKey(key, x, y);
941e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    startLongPressTimer(key);
94229d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka                    setPressedKeyGraphics(key, eventTime);
94367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
944b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    // HACK: On some devices, quick successive touches may be reported as a sudden
945b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    // move by touch panel firmware. This hack detects such cases and translates the
946faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move event to successive up and down events.
947c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                    // TODO: Should find a way to balance gesture detection and this hack.
948d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                    if (sNeedsPhantomSuddenMoveEventHack
949b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
950d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        if (DEBUG_MODE) {
951b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            Log.w(TAG, String.format("[%d] onMoveEvent:"
952b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    + " phantom sudden move event (distance=%d) is translated to "
953b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
954b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    getDistance(x, y, lastX, lastY),
955b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    lastX, lastY, Keyboard.printableCode(oldKey.mCode),
956b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    x, y, Keyboard.printableCode(key.mCode)));
957d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        }
958c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        // TODO: This should be moved to outside of this nested if-clause?
9599bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                        if (ProductionFlag.IS_EXPERIMENTAL) {
9609bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                            ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
9619bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                        }
962630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka                        onUpEventInternal(eventTime);
963faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                        onDownEventInternal(x, y, eventTime);
964b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    }
965b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    // HACK: On some devices, quick successive proximate touches may be reported as
966b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    // a bogus down-move-up event by touch panel firmware. This hack detects such
967b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    // cases and breaks these events into separate up and down events.
968b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    else if (sNeedsProximateBogusDownMoveUpEventHack
969b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            && sTimeRecorder.isInFastTyping(eventTime)
970b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
971b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                        if (DEBUG_MODE) {
972d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                            final float keyDiagonal = (float)Math.hypot(
973d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                                    mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
974b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            final float radiusRatio =
975d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                                    mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
976d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                                    / keyDiagonal;
977b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            Log.w(TAG, String.format("[%d] onMoveEvent:"
978d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                                    + " bogus down-move-up event (raidus=%.2f key diagonal) is "
979b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
980b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    mPointerId, radiusRatio,
981b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    lastX, lastY, Keyboard.printableCode(oldKey.mCode),
982b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                    x, y, Keyboard.printableCode(key.mCode)));
983b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                        }
984b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                        onUpEventInternal(eventTime);
985b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                        onDownEventInternal(x, y, eventTime);
986faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    } else {
9875e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // HACK: If there are currently multiple touches, register the key even if
9885e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // the finger slides off the key. This defends against noise from some
9895e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // touch panels when there are close multiple touches.
9905e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // Caveat: When in chording input mode with a modifier key, we don't use
9915e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        // this hack.
9926c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                        if (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null
9936c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
994b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            if (DEBUG_MODE) {
995b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                Log.w(TAG, String.format("[%d] onMoveEvent:"
996b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                        + " detected sliding finger while multi touching",
997b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                                        mPointerId));
998b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            }
999b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            onUpEvent(x, y, eventTime);
1000b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mKeyAlreadyProcessed = true;
10015e06b8534ffdf5099d2ef4551b113a103cdf7061Tadashi G. Takaoka                        }
10026c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                        if (!mIsDetectingGesture) {
1003c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                            mKeyAlreadyProcessed = true;
1004c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        }
1005e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                        setReleasedKeyGraphics(oldKey);
1006faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    }
100767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
1008c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
10096a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
1010b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) {
10119e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid out from the previous key, we must call onRelease() to
10129e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // notify that the previous key has been released.
1013e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                setReleasedKeyGraphics(oldKey);
1014e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
1015996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
101698b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka                mTimerProxy.cancelLongPressTimer();
101767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
1018e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                    onMoveToNewKey(key, x, y);
101967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
10206c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                    if (!mIsDetectingGesture) {
1021c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                        mKeyAlreadyProcessed = true;
1022c902dfeb8f8b889e885565a000556419e0d8307fTadashi G. Takaoka                    }
102367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
102407221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
10256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
10266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10276a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1028694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onUpEvent(final int x, final int y, final long eventTime) {
1029694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1030dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
1031694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
10321d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
10335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
10341d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
10356c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (!sInGesture) {
1036eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                if (mCurrentKey != null && mCurrentKey.isModifier()) {
1037eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    // Before processing an up event of modifier key, all pointers already being
1038eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    // tracked should be released.
1039eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    queue.releaseAllPointersExcept(this, eventTime);
1040eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                } else {
1041eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    queue.releaseAllPointersOlderThan(this, eventTime);
1042eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                }
10431d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
10441d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
1045630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        onUpEventInternal(eventTime);
10466c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (queue != null) {
10476c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            queue.remove(this);
10486c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
10491d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
10501d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1051d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
1052d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
1053d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
10540cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
1055694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
1056694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
10570cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka            printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
1058694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1059630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        onUpEventInternal(eventTime);
1060d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
10611d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
10621d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1063630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka    private void onUpEventInternal(final long eventTime) {
10642321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1065f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
10666c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
10676c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
10686c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
10695a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
10706c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        setReleasedKeyGraphics(currentKey);
10719d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
10729d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
10739d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
10749ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
1075eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
10766c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
10776c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
10786c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.mCode, true);
1079eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
108029d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            mayEndBatchInput(eventTime);
1081eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
1082eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
108358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
1084694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyAlreadyProcessed) {
1085d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
1086694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
10876c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (currentKey != null && !currentKey.isRepeatable()) {
10883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
10896a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
10906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1092694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) {
10939ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        onLongPressed();
10949d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mIsShowingMoreKeysPanel = true;
1095cc3500b0c8f11e8de1ad7376dda466d30637e462Tadashi G. Takaoka        onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
10969ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
10979ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
1098906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
1099d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
1100e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
11015c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
11021ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        if (queue != null) {
1103d00d963b9d47c1bba6f65534033a33fe7c30dde5Tadashi G. Takaoka            queue.remove(this);
11041ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        }
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
11125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
1113c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (queue != null) {
1114d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka            queue.releaseAllPointersExcept(this, eventTime);
11151d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
1116c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        }
1117baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
11181d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
11191d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1120baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
11212321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
1122e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey);
1123f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        resetSlidingKeyInput();
11249d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
11259d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
11269d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
11279ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
11286a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11296a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1130694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
11316c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (key != null && key.isRepeatable() && !sInGesture) {
11326662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv            onRegisterKey(key);
1133a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startKeyRepeatTimer(this);
1134a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        }
1135a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
1136a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
1137694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onRegisterKey(final Key key) {
1138c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
11393623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
1140d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            mTimerProxy.startTypingStateTimer(key);
1141c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
11426a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1144b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
1145b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final Key newKey) {
1146694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (mKeyDetector == null) {
1147a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
1148694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
1149694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
11506a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1151e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1152e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        } else if (curKey != null) {
1153f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka            final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
1154f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                    mIsInSlidingKeyInputFromModifier);
1155b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
1156b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
1157b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                if (DEBUG_MODE) {
1158d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
1159d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                            / mKeyboard.mMostCommonKeyWidth;
1160b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1161d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                            +" %.2f key width from key edge",
1162b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, distanceToEdgeRatio));
1163b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                }
1164b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                return true;
1165b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1166b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
1167b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    && sTimeRecorder.isInFastTyping(eventTime)
1168d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
1169b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                if (DEBUG_MODE) {
1170d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                    final float keyDiagonal = (float)Math.hypot(
1171d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                            mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
1172b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    final float lengthFromDownRatio =
1173d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                            mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
1174b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1175d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka                            + " %.2f key diagonal from virtual down point",
1176b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, lengthFromDownRatio));
1177b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                }
1178b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                return true;
1179b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1180b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            return false;
1181b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        } else { // curKey == null && newKey != null
1182e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
11836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
11846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1186694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
11876c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (key != null && key.isLongPressEnabled() && !sInGesture) {
1188a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTimerProxy.startLongPressTimer(this);
11894189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        }
119066e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
119166e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
11923623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
119383e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
11948aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1195dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1196dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
119766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
1198694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final int code = key.mCode;
11993623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        callListenerOnCodeInput(key, code, x, y, eventTime);
12002013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        callListenerOnRelease(key, code, false);
12016a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
12026a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1203694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1204694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1205723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
1206e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        final String code = KeyDetector.printableCode(key);
120758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
120858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                (mKeyAlreadyProcessed ? "-" : " "), title, x, y, eventTime, code));
1209dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
12106e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1211