PointerTracker.java revision 9d5601e9013c5ec9a7ac75db16f4a0a8218b02bf
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
19f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaokaimport android.content.Context;
20c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport android.content.res.Resources;
21c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport android.util.Log;
224692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaokaimport android.widget.TextView;
23c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
2472934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
25faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
265a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
276a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
29dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaokaimport java.util.Arrays;
30dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaokaimport java.util.List;
31dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
326a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaokapublic class PointerTracker {
33dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
34dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
35dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
36dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
37faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
3840a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
39f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public interface KeyEventHandler {
40f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
41f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyDetector object that is used for this PointerTracker.
42f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyDetector object that is used for this PointerTracker
43f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
44f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyDetector getKeyDetector();
45f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
46f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
47f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get KeyboardActionListener object that is used to register key code and so on.
48f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the KeyboardActionListner for this PointerTracker
49f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
50f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public KeyboardActionListener getKeyboardActionListener();
51f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
52f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
53f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get DrawingProxy object that is used for this PointerTracker.
54f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the DrawingProxy object that is used for this PointerTracker
55f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public DrawingProxy getDrawingProxy();
57f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
58f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        /**
59f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * Get TimerProxy object that handles key repeat and long press timer event for this
60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * PointerTracker.
61f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         * @return the TimerProxy object that handles key repeat and long press timer event.
62f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka         */
63f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        public TimerProxy getTimerProxy();
64f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
65f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
669d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public interface DrawingProxy extends MoreKeysPanel.Controller {
676a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
684692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        public TextView inflateKeyPreviewText();
69d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void showKeyPreview(int keyIndex, PointerTracker tracker);
705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelShowKeyPreview(PointerTracker tracker);
71d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
742321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
752321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker);
762321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker);
7798b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer();
782321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelKeyTimers();
792321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    }
802321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka
815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static KeyboardSwitcher sKeyboardSwitcher;
825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static boolean sConfigSlidingKeyInputEnabled;
836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    // Timing constants
845c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sDelayBeforeKeyRepeatStart;
855c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sLongPressKeyTimeout;
865c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sLongPressShiftKeyTimeout;
8798b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka    private static int sLongPressSpaceKeyTimeout;
885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sTouchNoiseThresholdMillis;
895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sTouchNoiseThresholdDistanceSquared;
905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static final List<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
925c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static PointerTrackerQueue sPointerTrackerQueue;
935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
945c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
956a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
960efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
970efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
98a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
99dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private KeyboardActionListener mListener = EMPTY_LISTENER;
100baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1015a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
102dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka    private List<Key> mKeys;
103faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private int mKeyQuarterWidthSquared;
1044692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    private final TextView mKeyPreviewText;
1056a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1068a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
1078a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
1088a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
1098a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
1108a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The current key index where this pointer is.
1118a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyIndex = KeyDetector.NOT_A_KEY;
1128a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position where mKeyIndex was recognized for the first time.
1138a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
1148a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
1158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
1168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
1178a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
1188a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
1196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1201a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
1211a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
1221a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
1239ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    // true if event is already translated to a key action.
124c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    private boolean mKeyAlreadyProcessed;
125c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
1269d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // true if this pointer has been long-pressed and is showing a more keys panel.
1279d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private boolean mIsShowingMoreKeysPanel;
1289ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
1296252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka    // true if this pointer is repeatable key
1306252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka    private boolean mIsRepeatableKey;
1316252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka
132cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    // true if this pointer is in sliding key input
1335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
134cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
13567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    // true if sliding key is allowed.
13667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
13767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
138996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    // ignore modifier key if true
139996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private boolean mIgnoreModifierKey;
140996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
141dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    // Empty {@link KeyboardActionListener}
142e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka    private static final KeyboardActionListener EMPTY_LISTENER =
143e3be3bcebc11892b536fdf650f74bba21af13383Tadashi G. Takaoka            new KeyboardActionListener.Adapter();
1446e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka
1455c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void init(boolean hasDistinctMultitouch, Context context) {
1465c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (hasDistinctMultitouch) {
1475c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = new PointerTrackerQueue();
1485c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        } else {
1495c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = null;
1505c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1515c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
152f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final Resources res = context.getResources();
1535c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
1545c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
1555c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout);
1565c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout);
15798b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        sLongPressSpaceKeyTimeout = res.getInteger(R.integer.config_long_press_space_key_timeout);
1585c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sTouchNoiseThresholdMillis = res.getInteger(R.integer.config_touch_noise_threshold_millis);
159baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        final float touchNoiseThresholdDistance = res.getDimension(
160baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                R.dimen.config_touch_noise_threshold_distance);
1615c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sTouchNoiseThresholdDistanceSquared = (int)(
162baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                touchNoiseThresholdDistance * touchNoiseThresholdDistance);
1635c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sKeyboardSwitcher = KeyboardSwitcher.getInstance();
1646a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
1656a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1665c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
1675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final List<PointerTracker> trackers = sTrackers;
1685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1695c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
1705c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
1715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
1725c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
1735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
1765c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1775c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1785c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
1795c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
1805c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void setKeyboardActionListener(KeyboardActionListener listener) {
1835c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
1845c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
1855c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1865c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1875c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void setKeyDetector(KeyDetector keyDetector) {
1895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
1905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
1915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
1925c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
1935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1945c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1965c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void dismissAllKeyPreviews() {
1975c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
198d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.mKeyIndex);
1995c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2005c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2015c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public PointerTracker(int id, KeyEventHandler handler) {
2035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (handler == null)
2045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
2055c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
2065c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
2075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
2085c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
2095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
2104692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText();
2114692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    }
2124692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka
2134692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka    public TextView getKeyPreviewText() {
2144692af50daefea9498faebeaa8d7e7a444afda4cTadashi G. Takaoka        return mKeyPreviewText;
2156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
2166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
2171a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
218e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key, boolean withSliding) {
219996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
220dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
221996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            Log.d(TAG, "onPress    : " + keyCodePrintable(key.mCode) + " sliding=" + withSliding
222996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey);
223996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        if (ignoreModifierKey)
224996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
225e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
226e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            mListener.onPress(key.mCode, withSliding);
227690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
228690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
229690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
230690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
231690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
232dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
233dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
234690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
235690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
236690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    private void callListenerOnCodeInput(Key key, int primaryCode, int[] keyCodes, int x, int y) {
237996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
238dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
2398aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            Log.d(TAG, "onCodeInput: " + keyCodePrintable(primaryCode)
240996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                    + " codes="+ Arrays.toString(keyCodes) + " x=" + x + " y=" + y
241996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey);
242996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        if (ignoreModifierKey)
243996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
244e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled())
245690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mListener.onCodeInput(primaryCode, keyCodes, x, y);
246dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
247dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
248690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    private void callListenerOnTextInput(Key key) {
249dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
250690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            Log.d(TAG, "onTextInput: text=" + key.mOutputText);
251e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled())
252690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mListener.onTextInput(key.mOutputText);
253dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
254dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
255690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
256690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
257e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
258996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
259dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
260996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            Log.d(TAG, "onRelease  : " + keyCodePrintable(primaryCode) + " sliding="
261996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                    + withSliding + " ignoreModifier=" + ignoreModifierKey);
262996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        if (ignoreModifierKey)
263996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
264e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled())
265e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            mListener.onRelease(primaryCode, withSliding);
266dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
267dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
2688aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
269dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
2708aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            Log.d(TAG, "onCancelInput");
2718aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
272dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
273dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
27446286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka    private void setKeyDetectorInner(KeyDetector keyDetector) {
275a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
2765a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
2771be29abab2e112f0253a8a5da3478740bb866d27Tadashi G. Takaoka        mKeys = mKeyboard.mKeys;
2788da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
279faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka        mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
2805a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
2815a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
282cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
283cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
284cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
285cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
286c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka    private boolean isValidKeyIndex(int keyIndex) {
287dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka        return keyIndex >= 0 && keyIndex < mKeys.size();
288c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka    }
289c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka
2906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    public Key getKey(int keyIndex) {
291dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka        return isValidKeyIndex(keyIndex) ? mKeys.get(keyIndex) : null;
2926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
2936a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
294dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static boolean isModifierCode(int primaryCode) {
295571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        return primaryCode == Keyboard.CODE_SHIFT
296e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka                || primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL;
29740a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka    }
29840a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
299dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private boolean isModifierInternal(int keyIndex) {
300dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final Key key = getKey(keyIndex);
301c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        return key == null ? false : isModifierCode(key.mCode);
302dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
303dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
3042aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
3058a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return isModifierInternal(mKeyIndex);
3062aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
3072aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
3081d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    private boolean isOnModifierKey(int x, int y) {
3092aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka        return isModifierInternal(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null));
3102aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
3112aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
312418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka    public boolean isOnShiftKey(int x, int y) {
313418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka        final Key key = getKey(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null));
314c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        return key != null && key.mCode == Keyboard.CODE_SHIFT;
315418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka    }
316418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka
317a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    public int getKeyIndexOn(int x, int y) {
318a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
319a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
320a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
321d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    private void setReleasedKeyGraphics(int keyIndex) {
322d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
323d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        final Key key = getKey(keyIndex);
324d4b533774df4db403ecf2a7126f2637c451aefeeTadashi G. Takaoka        if (key != null && key.isEnabled()) {
325d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            key.onReleased();
326f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka            mDrawingProxy.invalidateKey(key);
3276a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
3286a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3296a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
330d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    private void setPressedKeyGraphics(int keyIndex) {
331d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        final Key key = getKey(keyIndex);
332e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key != null && key.isEnabled()) {
333d4b533774df4db403ecf2a7126f2637c451aefeeTadashi G. Takaoka            if (isKeyPreviewRequired(key)) {
334d4b533774df4db403ecf2a7126f2637c451aefeeTadashi G. Takaoka                mDrawingProxy.showKeyPreview(keyIndex, this);
335d4b533774df4db403ecf2a7126f2637c451aefeeTadashi G. Takaoka            }
336d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            key.onPressed();
337f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka            mDrawingProxy.invalidateKey(key);
338d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
339c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
340c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
341d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka    // The modifier key, such as shift key, should not show its key preview.
342d4b533774df4db403ecf2a7126f2637c451aefeeTadashi G. Takaoka    private static boolean isKeyPreviewRequired(Key key) {
343d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        final int code = key.mCode;
344d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        if (isModifierCode(code) || code == Keyboard.CODE_DELETE
345d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka                || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE)
346d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka            return false;
347d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        return true;
348d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka    }
349d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka
3508a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastX() {
3518a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastX;
3528a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3538a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastY() {
3558a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastY;
3568a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3578a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3588a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
3598a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
3608a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3618a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3628a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onDownKey(int x, int y, long eventTime) {
3638a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
3648a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
3658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3668a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3678a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onMoveKeyInternal(int x, int y) {
3688a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
3698a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
3708a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
3718a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3728a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3738a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onMoveKey(int x, int y) {
3748a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
3758a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3768a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3778a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onMoveToNewKey(int keyIndex, int x, int y) {
3788a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyIndex = keyIndex;
3798a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
3808a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
3818a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return keyIndex;
3828a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3838a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3848a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onUpKey(int x, int y, long eventTime) {
3858a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mUpTime = eventTime;
3868a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyIndex = KeyDetector.NOT_A_KEY;
3878a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
3888a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3898a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
390f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) {
391dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
392dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
3931d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
394f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
39563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
396f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
397f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
398baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
3998a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
4005c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (deltaT < sTouchNoiseThresholdMillis) {
4018a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dx = x - mLastX;
4028a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dy = y - mLastY;
403baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            final int distanceSquared = (dx * dx + dy * dy);
4045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            if (distanceSquared < sTouchNoiseThresholdDistanceSquared) {
405faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
406faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
407faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                            + " distance=" + distanceSquared);
408d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
409baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
410baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
411baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
412baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
4135c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
4141d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
4151d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            if (isOnModifierKey(x, y)) {
4161d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // Before processing a down event of modifier key, all pointers already being
4171d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // tracked should be released.
4181d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                queue.releaseAllPointers(eventTime);
4191d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
4201d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.add(this);
4211d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
4221d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
4231d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
4241d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
425baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onDownEventInternal(int x, int y, long eventTime) {
4268a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        int keyIndex = onDownKey(x, y, eventTime);
42767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
42832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
4295c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mIsAllowedSlidingKeyInput = sConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
43032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                || mKeyDetector.alwaysAllowsSlidingInput();
4311a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
432c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka        mKeyAlreadyProcessed = false;
4336252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka        mIsRepeatableKey = false;
434cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
435996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIgnoreModifierKey = false;
436d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        if (isValidKeyIndex(keyIndex)) {
4371a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
4381a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update keyIndex according to the new
4391a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
440d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), false))
4418a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                keyIndex = onDownKey(x, y, eventTime);
442996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
443a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            startRepeatKey(keyIndex);
444adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka            startLongPressTimer(keyIndex);
445d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            setPressedKeyGraphics(keyIndex);
4466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
4476a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
449996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private void startSlidingKeyInput(Key key) {
450996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        if (!mIsInSlidingKeyInput)
451996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            mIgnoreModifierKey = isModifierCode(key.mCode);
452996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
453996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
454996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
455906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onMoveEvent(int x, int y, long eventTime) {
456dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_MOVE_EVENT)
457dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
458e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka        if (mKeyAlreadyProcessed)
459e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
460baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
4618a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
4628a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
4638a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int oldKeyIndex = mKeyIndex;
464d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        final Key oldKey = getKey(oldKeyIndex);
4658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        int keyIndex = onMoveKey(x, y);
466c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka        if (isValidKeyIndex(keyIndex)) {
467c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka            if (oldKey == null) {
4689e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
4699e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
4701a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // This onPress call may have changed keyboard layout. Those cases are detected at
4711a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // {@link #setKeyboard}. In those cases, we should update keyIndex according to the
4721a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // new keyboard layout.
473e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), true))
4748a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                    keyIndex = onMoveKey(x, y);
4758a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                onMoveToNewKey(keyIndex, x, y);
476adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka                startLongPressTimer(keyIndex);
477d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                setPressedKeyGraphics(keyIndex);
478e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            } else if (isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
4799e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key from the previous key, we must call
4809e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onRelease() first to notify that the previous key has been released, then call
4819e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onPress() to notify that the new key is being pressed.
482d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                setReleasedKeyGraphics(oldKeyIndex);
483e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
484996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
4852321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka                mTimerProxy.cancelKeyTimers();
486a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka                startRepeatKey(keyIndex);
48767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
4881a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // This onPress call may have changed keyboard layout. Those cases are detected
4891a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // at {@link #setKeyboard}. In those cases, we should update keyIndex according
4901a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // to the new keyboard layout.
491e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), true))
4928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                        keyIndex = onMoveKey(x, y);
4938a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                    onMoveToNewKey(keyIndex, x, y);
49467a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                    startLongPressTimer(keyIndex);
495d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                    setPressedKeyGraphics(keyIndex);
49667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
497faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // HACK: On some devices, quick successive touches may be translated to sudden
498faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move by touch panel firmware. This hack detects the case and translates the
499faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move event to successive up and down events.
500faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dx = x - lastX;
501faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dy = y - lastY;
502faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int lastMoveSquared = dx * dx + dy * dy;
503faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    if (lastMoveSquared >= mKeyQuarterWidthSquared) {
504faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                        if (DEBUG_MODE)
505faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                            Log.w(TAG, String.format("onMoveEvent: sudden move is translated to "
506faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                                    + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
507d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka                        onUpEventInternal(lastX, lastY, eventTime);
508faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                        onDownEventInternal(x, y, eventTime);
509faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    } else {
510d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                        mKeyAlreadyProcessed = true;
511d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                        setReleasedKeyGraphics(oldKeyIndex);
512faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    }
51367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
514c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
5156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
516e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
5179e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid out from the previous key, we must call onRelease() to
5189e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // notify that the previous key has been released.
519d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                setReleasedKeyGraphics(oldKeyIndex);
520e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
521996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
52298b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka                mTimerProxy.cancelLongPressTimer();
52367a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
5248a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                    onMoveToNewKey(keyIndex, x, y);
52567a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
526d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                    mKeyAlreadyProcessed = true;
52767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
52807221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
5296a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
5306a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
5316a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
532906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onUpEvent(int x, int y, long eventTime) {
533dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
534dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
5351d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
5365c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
5371d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
5381d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            if (isModifier()) {
5391d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
5401d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // tracked should be released.
541d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka                queue.releaseAllPointersExcept(this, eventTime);
5421d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            } else {
5431d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                queue.releaseAllPointersOlderThan(this, eventTime);
5441d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
5451d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
5461d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
547d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        onUpEventInternal(x, y, eventTime);
5481d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
5491d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
550d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
551d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
552d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
553d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka    public void onPhantomUpEvent(int x, int y, long eventTime) {
554c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (DEBUG_EVENT)
555c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            printTouchEvent("onPhntEvent:", x, y, eventTime);
556d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        onUpEventInternal(x, y, eventTime);
557d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
5581d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
5591d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
560d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka    private void onUpEventInternal(int x, int y, long eventTime) {
5612321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
562f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mDrawingProxy.cancelShowKeyPreview(this);
563cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
564d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        final int keyX, keyY;
5658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        if (isMajorEnoughMoveToBeOnNewKey(x, y, onMoveKey(x, y))) {
566d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            keyX = x;
567d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            keyY = y;
568d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        } else {
569d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            // Use previous fixed key coordinates.
5708a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            keyX = mKeyX;
5718a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            keyY = mKeyY;
5726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
5738a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int keyIndex = onUpKey(keyX, keyY, eventTime);
574d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        setReleasedKeyGraphics(keyIndex);
5759d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
5769d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
5779d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
5789ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
579d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        if (mKeyAlreadyProcessed)
580d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
5816252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka        if (!mIsRepeatableKey) {
582d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            detectAndSendKey(keyIndex, keyX, keyY);
5836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
5846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
5856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
5869d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public void onShowMoreKeysPanel(int x, int y, long eventTime, KeyEventHandler handler) {
5879ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        onLongPressed();
5889ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        onDownEvent(x, y, eventTime, handler);
5899d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mIsShowingMoreKeysPanel = true;
5909ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
5919ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
592906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
593d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
594d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka        setReleasedKeyGraphics(mKeyIndex);
5955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
5961ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        if (queue != null) {
597d00d963b9d47c1bba6f65534033a33fe7c30dde5Tadashi G. Takaoka            queue.remove(this);
5981ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        }
599d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
600d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
601906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onCancelEvent(int x, int y, long eventTime) {
602dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
603dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
6041d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
6055c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
606c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (queue != null) {
607d3002aa8cd5339d59123e0c96174f6701e2c72ccTadashi G. Takaoka            queue.releaseAllPointersExcept(this, eventTime);
6081d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
609c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        }
610baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
6111d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
6121d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
613baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
6142321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
615f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mDrawingProxy.cancelShowKeyPreview(this);
6168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        setReleasedKeyGraphics(mKeyIndex);
617cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
6189d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mIsShowingMoreKeysPanel) {
6199d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mDrawingProxy.dismissMoreKeysPanel();
6209d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mIsShowingMoreKeysPanel = false;
6219ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
6226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
624a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    private void startRepeatKey(int keyIndex) {
625a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        final Key key = getKey(keyIndex);
626101a00e3d4b1c29ef2ecdecd1b72b43efde7791aTadashi G. Takaoka        if (key != null && key.mRepeatable) {
627a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            onRepeatKey(keyIndex);
6285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            mTimerProxy.startKeyRepeatTimer(sDelayBeforeKeyRepeatStart, keyIndex, this);
629a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            mIsRepeatableKey = true;
630a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        } else {
631a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            mIsRepeatableKey = false;
632a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        }
633a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
634a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
635a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    public void onRepeatKey(int keyIndex) {
636c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        Key key = getKey(keyIndex);
637c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
638c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka            detectAndSendKey(keyIndex, key.mX, key.mY);
639c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
6406a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6416a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
642e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, int newKey) {
643a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        if (mKeys == null || mKeyDetector == null)
644a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
6458a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        int curKey = mKeyIndex;
6466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
647e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
648c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        } else if (isValidKeyIndex(curKey)) {
649a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            return mKeys.get(curKey).squaredDistanceToEdge(x, y)
650a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka                    >= mKeyDetector.getKeyHysteresisDistanceSquared();
6516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
652e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
6536a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6546a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6556a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
65666e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    private void startLongPressTimer(int keyIndex) {
65766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka        Key key = getKey(keyIndex);
6580a5345c7b6e9282ea401a4017c2c2f9835e623b1Tadashi G. Takaoka        if (key == null) return;
659c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        if (key.mCode == Keyboard.CODE_SHIFT) {
66098b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            if (sLongPressShiftKeyTimeout > 0) {
66198b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka                mTimerProxy.startLongPressTimer(sLongPressShiftKeyTimeout, keyIndex, this);
66298b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            }
66398b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        } else if (key.mCode == Keyboard.CODE_SPACE) {
66498b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            if (sLongPressSpaceKeyTimeout > 0) {
66598b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka                mTimerProxy.startLongPressTimer(sLongPressSpaceKeyTimeout, keyIndex, this);
66698b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            }
6679d9522abdcee70408c9e99ac20c8e1c224eef19dTadashi G. Takaoka        } else if (key.hasUppercaseLetter() && mKeyboard.isManualTemporaryUpperCase()) {
6682b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka            // We need not start long press timer on the key which has manual temporary upper case
6692b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka            // code defined and the keyboard is in manual temporary upper case mode.
6702b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka            return;
6715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        } else if (sKeyboardSwitcher.isInMomentarySwitchState()) {
6729e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // We use longer timeout for sliding finger input started from the symbols mode key.
6735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            mTimerProxy.startLongPressTimer(sLongPressKeyTimeout * 3, keyIndex, this);
6744189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        } else {
6755c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            mTimerProxy.startLongPressTimer(sLongPressKeyTimeout, keyIndex, this);
6764189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        }
67766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
67866e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
679c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka    private void detectAndSendKey(int index, int x, int y) {
68083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        final Key key = getKey(index);
68183e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
6828aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
683dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
684dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
685dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (key.mOutputText != null) {
686690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            callListenerOnTextInput(key);
687e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            callListenerOnRelease(key, key.mCode, false);
68883e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        } else {
689c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka            int code = key.mCode;
690dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            final int[] codes = mKeyDetector.newCodeArray();
691dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
69266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
693dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            // If keyboard is in manual temporary upper case state and key has manual temporary
6949d9522abdcee70408c9e99ac20c8e1c224eef19dTadashi G. Takaoka            // uppercase letter as key hint letter, alternate character code should be sent.
6959d9522abdcee70408c9e99ac20c8e1c224eef19dTadashi G. Takaoka            if (mKeyboard.isManualTemporaryUpperCase() && key.hasUppercaseLetter()) {
696520a297ad1d148a57bcf6559a9802d5d49182d70Tadashi G. Takaoka                code = key.mHintLabel.charAt(0);
697dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka                codes[0] = code;
698dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            }
69966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
700dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            // Swap the first and second values in the codes array if the primary code is not the
701dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            // first value but the second value in the array. This happens when key debouncing is
702dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            // in effect.
703dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            if (codes.length >= 2 && codes[0] != code && codes[1] == code) {
704dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka                codes[1] = codes[0];
705dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka                codes[0] = code;
7066a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka            }
707690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            callListenerOnCodeInput(key, code, codes, x, y);
708e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            callListenerOnRelease(key, code, false);
7096a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
7106a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
7116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
712dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private long mPreviousEventTime;
713dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
714dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private void printTouchEvent(String title, int x, int y, long eventTime) {
715dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
716dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final Key key = getKey(keyIndex);
717c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        final String code = (key == null) ? "----" : keyCodePrintable(key.mCode);
718dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final long delta = eventTime - mPreviousEventTime;
719dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %3d(%s)", title,
720dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka                (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, keyIndex, code));
721dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        mPreviousEventTime = eventTime;
722dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
723dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
724dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static String keyCodePrintable(int primaryCode) {
725dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final String modifier = isModifierCode(primaryCode) ? " modifier" : "";
726dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        return  String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode) + modifier;
7273d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka    }
7286e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
729