PointerTracker.java revision 5c73ed628b22fdfa59585803ee86e383c579a7d4
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;
22c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
2372934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
24faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
255a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
26c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeSwitcher;
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
66f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka    public interface DrawingProxy {
676a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        public void invalidateKey(Key key);
68d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void showKeyPreview(int keyIndex, PointerTracker tracker);
695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelShowKeyPreview(PointerTracker tracker);
70d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        public void dismissKeyPreview(PointerTracker tracker);
716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
732321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    public interface TimerProxy {
742321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker);
752321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker);
762321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void startLongPressShiftTimer(long delay, int keyIndex, PointerTracker tracker);
772321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        public void cancelLongPressTimers();
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;
875c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sTouchNoiseThresholdMillis;
885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static int sTouchNoiseThresholdDistanceSquared;
895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static final List<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static PointerTrackerQueue sPointerTrackerQueue;
925c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
946a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
950efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private DrawingProxy mDrawingProxy;
960efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    private TimerProxy mTimerProxy;
97a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    private KeyDetector mKeyDetector;
98dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private KeyboardActionListener mListener = EMPTY_LISTENER;
99baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
1005a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
101dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka    private List<Key> mKeys;
102faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private int mKeyQuarterWidthSquared;
1036a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1048a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
1058a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
1068a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
1078a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
1088a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The current key index where this pointer is.
1098a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyIndex = KeyDetector.NOT_A_KEY;
1108a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position where mKeyIndex was recognized for the first time.
1118a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
1128a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
1138a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
1148a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
1158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
1168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
1176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1181a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
1191a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
1201a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
121c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    // true if event is already translated to a key action (long press or mini-keyboard)
122c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    private boolean mKeyAlreadyProcessed;
123c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
1246252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka    // true if this pointer is repeatable key
1256252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka    private boolean mIsRepeatableKey;
1266252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka
127cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    // true if this pointer is in sliding key input
1285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    boolean mIsInSlidingKeyInput;
129cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
13067a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    // true if sliding key is allowed.
13167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka    private boolean mIsAllowedSlidingKeyInput;
13267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
133996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    // ignore modifier key if true
134996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private boolean mIgnoreModifierKey;
135996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
136c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka    // TODO: Remove these hacking variables
137c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka    // true if this pointer is in sliding language switch
138c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka    private boolean mIsInSlidingLanguageSwitch;
139c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka    private int mSpaceKeyIndex;
1405c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    private static SubtypeSwitcher sSubtypeSwitcher;
141c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka
142dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    // Empty {@link KeyboardActionListener}
143dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() {
144dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        @Override
145e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka        public void onPress(int primaryCode, boolean withSliding) {}
146dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        @Override
147e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka        public void onRelease(int primaryCode, boolean withSliding) {}
148dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        @Override
1498aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {}
150dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        @Override
1518aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        public void onTextInput(CharSequence text) {}
152dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        @Override
1538aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        public void onCancelInput() {}
154dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    };
1556e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka
1565c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void init(boolean hasDistinctMultitouch, Context context) {
1575c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (hasDistinctMultitouch) {
1585c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = new PointerTrackerQueue();
1595c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        } else {
1605c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            sPointerTrackerQueue = null;
1615c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1625c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
163f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final Resources res = context.getResources();
1645c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
1655c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
1665c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout);
1675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout);
1685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sTouchNoiseThresholdMillis = res.getInteger(R.integer.config_touch_noise_threshold_millis);
169baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        final float touchNoiseThresholdDistance = res.getDimension(
170baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                R.dimen.config_touch_noise_threshold_distance);
1715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sTouchNoiseThresholdDistanceSquared = (int)(
172baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                touchNoiseThresholdDistance * touchNoiseThresholdDistance);
1735c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sKeyboardSwitcher = KeyboardSwitcher.getInstance();
1745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        sSubtypeSwitcher = SubtypeSwitcher.getInstance();
1756a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
1766a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1775c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
1785c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final List<PointerTracker> trackers = sTrackers;
1795c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1805c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
1815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
1825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            final PointerTracker tracker = new PointerTracker(i, handler);
1835c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
1845c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1855c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1865c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
1875c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1895c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static boolean isAnyInSlidingKeyInput() {
1905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
1915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1925c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void setKeyboardActionListener(KeyboardActionListener listener) {
1945c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
1955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mListener = listener;
1965c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1975c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1985c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1995c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void setKeyDetector(KeyDetector keyDetector) {
2005c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
2015c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
2025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            // Mark that keyboard layout has been changed.
2035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.mKeyboardLayoutHasBeenChanged = true;
2045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2055c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2065c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public static void dismissAllKeyPreviews() {
2085c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (final PointerTracker tracker : sTrackers) {
2095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setReleasedKeyGraphics();
2105c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.dismissKeyPreview();
2115c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2125c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2135c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2145c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public PointerTracker(int id, KeyEventHandler handler) {
2155c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (handler == null)
2165c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            throw new NullPointerException();
2175c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
2185c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
2195c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mListener = handler.getKeyboardActionListener();
2205c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
2215c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
2226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
2236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
2241a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
225e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key, boolean withSliding) {
226996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
227dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
228996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            Log.d(TAG, "onPress    : " + keyCodePrintable(key.mCode) + " sliding=" + withSliding
229996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey);
230996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        if (ignoreModifierKey)
231996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
232e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
233e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            mListener.onPress(key.mCode, withSliding);
234690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
235690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
236690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
237690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
238690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
239dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
240dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
241690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
242690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
243690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    private void callListenerOnCodeInput(Key key, int primaryCode, int[] keyCodes, int x, int y) {
244996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
245dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
2468aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            Log.d(TAG, "onCodeInput: " + keyCodePrintable(primaryCode)
247996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                    + " codes="+ Arrays.toString(keyCodes) + " x=" + x + " y=" + y
248996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                    + " ignoreModifier=" + ignoreModifierKey);
249996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        if (ignoreModifierKey)
250996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
251e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled())
252690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mListener.onCodeInput(primaryCode, keyCodes, x, y);
253dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
254dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
255690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    private void callListenerOnTextInput(Key key) {
256dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
257690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            Log.d(TAG, "onTextInput: text=" + key.mOutputText);
258e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled())
259690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mListener.onTextInput(key.mOutputText);
260dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
261dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
262690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
263690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // primaryCode is different from {@link Key#mCode}.
264e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
265996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
266dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
267996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            Log.d(TAG, "onRelease  : " + keyCodePrintable(primaryCode) + " sliding="
268996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                    + withSliding + " ignoreModifier=" + ignoreModifierKey);
269996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        if (ignoreModifierKey)
270996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
271e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled())
272e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            mListener.onRelease(primaryCode, withSliding);
273dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
274dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
2758aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
276dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_LISTENER)
2778aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            Log.d(TAG, "onCancelInput");
2788aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka        mListener.onCancelInput();
279dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
280dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
2815a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    public void setKeyDetectorInner(KeyDetector keyDetector) {
282a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
2835a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboard = keyDetector.getKeyboard();
2845a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeys = mKeyboard.getKeys();
2855a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        final int keyQuarterWidth = mKeyboard.getKeyWidth() / 4;
286faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka        mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
2875a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
2885a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
289cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
290cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mIsInSlidingKeyInput;
291cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
292cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
293c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka    private boolean isValidKeyIndex(int keyIndex) {
294dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka        return keyIndex >= 0 && keyIndex < mKeys.size();
295c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka    }
296c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka
2976a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    public Key getKey(int keyIndex) {
298dc90d0a15f662cdece97bc2c0ddbd95e703af730Tadashi G. Takaoka        return isValidKeyIndex(keyIndex) ? mKeys.get(keyIndex) : null;
2996a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3006a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
301dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static boolean isModifierCode(int primaryCode) {
302571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        return primaryCode == Keyboard.CODE_SHIFT
303e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka                || primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL;
30440a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka    }
30540a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
306dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private boolean isModifierInternal(int keyIndex) {
307dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final Key key = getKey(keyIndex);
308c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        return key == null ? false : isModifierCode(key.mCode);
309dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
310dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
3112aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
3128a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return isModifierInternal(mKeyIndex);
3132aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
3142aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
3151d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    private boolean isOnModifierKey(int x, int y) {
3162aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka        return isModifierInternal(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null));
3172aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
3182aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
319418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka    public boolean isOnShiftKey(int x, int y) {
320418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka        final Key key = getKey(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null));
321c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        return key != null && key.mCode == Keyboard.CODE_SHIFT;
322418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka    }
323418d80d7de8d24150fc7e1710f7590a33301e546Tadashi G. Takaoka
324a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    public int getKeyIndexOn(int x, int y) {
325a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
326a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
327a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
3283a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public boolean isSpaceKey(int keyIndex) {
3293a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka        Key key = getKey(keyIndex);
330c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        return key != null && key.mCode == Keyboard.CODE_SPACE;
3313a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    }
3323a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka
333d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    public void setReleasedKeyGraphics() {
3348a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        setReleasedKeyGraphics(mKeyIndex);
3354189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka    }
3364189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka
337d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    private void setReleasedKeyGraphics(int keyIndex) {
338d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        final Key key = getKey(keyIndex);
339d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        if (key != null) {
340d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            key.onReleased();
341f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka            mDrawingProxy.invalidateKey(key);
3426a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
3436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
3446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
345d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    private void setPressedKeyGraphics(int keyIndex) {
346d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        final Key key = getKey(keyIndex);
347e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key != null && key.isEnabled()) {
348d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            key.onPressed();
349f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka            mDrawingProxy.invalidateKey(key);
350d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
351c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
352c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
3538a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastX() {
3548a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastX;
3558a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3568a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3578a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public int getLastY() {
3588a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mLastY;
3598a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3608a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3618a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
3628a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
3638a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3648a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onDownKey(int x, int y, long eventTime) {
3668a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
3678a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
3688a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3698a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3708a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onMoveKeyInternal(int x, int y) {
3718a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
3728a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
3738a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
3748a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3758a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3768a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onMoveKey(int x, int y) {
3778a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
3788a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3798a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3808a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onMoveToNewKey(int keyIndex, int x, int y) {
3818a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyIndex = keyIndex;
3828a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
3838a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
3848a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return keyIndex;
3858a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3868a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
3878a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int onUpKey(int x, int y, long eventTime) {
3888a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mUpTime = eventTime;
3898a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyIndex = KeyDetector.NOT_A_KEY;
3908a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
3918a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
3928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
393f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) {
394dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
395dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
3961d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
397f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        mDrawingProxy = handler.getDrawingProxy();
39863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mTimerProxy = handler.getTimerProxy();
399f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyboardActionListener(handler.getKeyboardActionListener());
400f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        setKeyDetectorInner(handler.getKeyDetector());
401baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
4028a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
4035c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        if (deltaT < sTouchNoiseThresholdMillis) {
4048a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dx = x - mLastX;
4058a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            final int dy = y - mLastY;
406baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            final int distanceSquared = (dx * dx + dy * dy);
4075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            if (distanceSquared < sTouchNoiseThresholdDistanceSquared) {
408faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
409faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
410faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                            + " distance=" + distanceSquared);
411d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                mKeyAlreadyProcessed = true;
412baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
413baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
414baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
415baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
4165c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
4171d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
4181d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            if (isOnModifierKey(x, y)) {
4191d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // Before processing a down event of modifier key, all pointers already being
4201d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // tracked should be released.
4211d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                queue.releaseAllPointers(eventTime);
4221d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
4231d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.add(this);
4241d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
4251d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
4261d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
4271d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
428baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onDownEventInternal(int x, int y, long eventTime) {
4298a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        int keyIndex = onDownKey(x, y, eventTime);
43067a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
431101a00e3d4b1c29ef2ecdecd1b72b43efde7791aTadashi G. Takaoka        // from modifier key, or 3) this pointer is on mini-keyboard.
4325c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mIsAllowedSlidingKeyInput = sConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
433101a00e3d4b1c29ef2ecdecd1b72b43efde7791aTadashi G. Takaoka                || mKeyDetector instanceof MiniKeyboardKeyDetector;
4341a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
435c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka        mKeyAlreadyProcessed = false;
4366252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka        mIsRepeatableKey = false;
437cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
438c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        mIsInSlidingLanguageSwitch = false;
439996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIgnoreModifierKey = false;
440d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        if (isValidKeyIndex(keyIndex)) {
4411a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
4421a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update keyIndex according to the new
4431a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
444d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), false))
4458a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                keyIndex = onDownKey(x, y, eventTime);
446996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
447a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            startRepeatKey(keyIndex);
448adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka            startLongPressTimer(keyIndex);
449d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            showKeyPreview(keyIndex);
450d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            setPressedKeyGraphics(keyIndex);
4516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
4526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4536a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
454996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    private void startSlidingKeyInput(Key key) {
455996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        if (!mIsInSlidingKeyInput)
456996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            mIgnoreModifierKey = isModifierCode(key.mCode);
457996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka        mIsInSlidingKeyInput = true;
458996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
459996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
460906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onMoveEvent(int x, int y, long eventTime) {
461dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_MOVE_EVENT)
462dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
463e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka        if (mKeyAlreadyProcessed)
464e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
465baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
466c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        // TODO: Remove this hacking code
467c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (mIsInSlidingLanguageSwitch) {
4688a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            ((LatinKeyboard)mKeyboard).updateSpacebarPreviewIcon(x - mKeyX);
469c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            showKeyPreview(mSpaceKeyIndex);
470c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            return;
471c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        }
4728a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
4738a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
4748a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int oldKeyIndex = mKeyIndex;
475d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        final Key oldKey = getKey(oldKeyIndex);
4768a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        int keyIndex = onMoveKey(x, y);
477c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka        if (isValidKeyIndex(keyIndex)) {
478c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka            if (oldKey == null) {
4799e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
4809e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
4811a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // This onPress call may have changed keyboard layout. Those cases are detected at
4821a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // {@link #setKeyboard}. In those cases, we should update keyIndex according to the
4831a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                // new keyboard layout.
484e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), true))
4858a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                    keyIndex = onMoveKey(x, y);
4868a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                onMoveToNewKey(keyIndex, x, y);
487adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka                startLongPressTimer(keyIndex);
488d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                showKeyPreview(keyIndex);
489d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                setPressedKeyGraphics(keyIndex);
490e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            } else if (isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
4919e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid in to the new key from the previous key, we must call
4929e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onRelease() first to notify that the previous key has been released, then call
4939e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // onPress() to notify that the new key is being pressed.
494d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                setReleasedKeyGraphics(oldKeyIndex);
495e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
496996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
4972321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka                mTimerProxy.cancelKeyTimers();
498a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka                startRepeatKey(keyIndex);
49967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
5001a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // This onPress call may have changed keyboard layout. Those cases are detected
5011a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // at {@link #setKeyboard}. In those cases, we should update keyIndex according
5021a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka                    // to the new keyboard layout.
503e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), true))
5048a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                        keyIndex = onMoveKey(x, y);
5058a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                    onMoveToNewKey(keyIndex, x, y);
50667a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                    startLongPressTimer(keyIndex);
507d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                    setPressedKeyGraphics(keyIndex);
508d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                    showKeyPreview(keyIndex);
50967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
510faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // HACK: On some devices, quick successive touches may be translated to sudden
511faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move by touch panel firmware. This hack detects the case and translates the
512faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    // move event to successive up and down events.
513faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dx = x - lastX;
514faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int dy = y - lastY;
515faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    final int lastMoveSquared = dx * dx + dy * dy;
516faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    if (lastMoveSquared >= mKeyQuarterWidthSquared) {
517faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                        if (DEBUG_MODE)
518faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                            Log.w(TAG, String.format("onMoveEvent: sudden move is translated to "
519faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                                    + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
520d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka                        onUpEventInternal(lastX, lastY, eventTime, true);
521faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                        onDownEventInternal(x, y, eventTime);
522faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    } else {
523d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                        mKeyAlreadyProcessed = true;
524d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka                        dismissKeyPreview();
525d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                        setReleasedKeyGraphics(oldKeyIndex);
526faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                    }
52767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
528c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
529c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            // TODO: Remove this hack code
530c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            else if (isSpaceKey(keyIndex) && !mIsInSlidingLanguageSwitch
531c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                    && mKeyboard instanceof LatinKeyboard) {
532c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                final LatinKeyboard keyboard = ((LatinKeyboard)mKeyboard);
5335c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka                if (sSubtypeSwitcher.useSpacebarLanguageSwitcher()
5345c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka                        && sSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
5358a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                    final int diff = x - mKeyX;
536c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                    if (keyboard.shouldTriggerSpacebarSlidingLanguageSwitch(diff)) {
537c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                        // Detect start sliding language switch.
538c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                        mIsInSlidingLanguageSwitch = true;
539c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                        mSpaceKeyIndex = keyIndex;
540c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                        keyboard.updateSpacebarPreviewIcon(diff);
541c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                        // Display spacebar slide language switcher.
542c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                        showKeyPreview(keyIndex);
5435c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka                        final PointerTrackerQueue queue = sPointerTrackerQueue;
544d00d963b9d47c1bba6f65534033a33fe7c30dde5Tadashi G. Takaoka                        if (queue != null)
545d00d963b9d47c1bba6f65534033a33fe7c30dde5Tadashi G. Takaoka                            queue.releaseAllPointersExcept(this, eventTime, true);
546c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                    }
547c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                }
5486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka            }
5496a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
550e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
5519e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // The pointer has been slid out from the previous key, we must call onRelease() to
5529e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // notify that the previous key has been released.
553d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                setReleasedKeyGraphics(oldKeyIndex);
554e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                callListenerOnRelease(oldKey, oldKey.mCode, true);
555996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka                startSlidingKeyInput(oldKey);
5562321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka                mTimerProxy.cancelLongPressTimers();
55767a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                if (mIsAllowedSlidingKeyInput) {
5588a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka                    onMoveToNewKey(keyIndex, x, y);
55967a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                } else {
560d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka                    mKeyAlreadyProcessed = true;
561d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka                    dismissKeyPreview();
56267a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka                }
56307221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
5646a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
5656a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
5666a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
567906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onUpEvent(int x, int y, long eventTime) {
568dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
569dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
5701d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
5715c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
5721d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        if (queue != null) {
5731d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            if (isModifier()) {
5741d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
5751d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                // tracked should be released.
576c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                queue.releaseAllPointersExcept(this, eventTime, true);
5771d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            } else {
5781d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka                queue.releaseAllPointersOlderThan(this, eventTime);
5791d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
5801d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
5811d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
582d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        onUpEventInternal(x, y, eventTime, true);
5831d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
5841d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
585d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
586d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
587d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
588c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka    public void onPhantomUpEvent(int x, int y, long eventTime, boolean updateReleasedKeyGraphics) {
589c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (DEBUG_EVENT)
590c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            printTouchEvent("onPhntEvent:", x, y, eventTime);
591c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        onUpEventInternal(x, y, eventTime, updateReleasedKeyGraphics);
592d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
5931d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
5941d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
595d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    private void onUpEventInternal(int x, int y, long eventTime,
596d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka            boolean updateReleasedKeyGraphics) {
5972321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
598f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mDrawingProxy.cancelShowKeyPreview(this);
599cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
600d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        final int keyX, keyY;
6018a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        if (isMajorEnoughMoveToBeOnNewKey(x, y, onMoveKey(x, y))) {
602d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            keyX = x;
603d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            keyY = y;
604d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        } else {
605d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            // Use previous fixed key coordinates.
6068a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            keyX = mKeyX;
6078a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka            keyY = mKeyY;
6086a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6098a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int keyIndex = onUpKey(keyX, keyY, eventTime);
610d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        dismissKeyPreview();
611d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        if (updateReleasedKeyGraphics)
612d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka            setReleasedKeyGraphics(keyIndex);
613d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        if (mKeyAlreadyProcessed)
614d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
615c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        // TODO: Remove this hacking code
616c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (mIsInSlidingLanguageSwitch) {
617c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            setReleasedKeyGraphics(mSpaceKeyIndex);
618c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            final int languageDir = ((LatinKeyboard)mKeyboard).getLanguageChangeDirection();
619c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            if (languageDir != 0) {
620c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                final int code = (languageDir == 1)
621c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                        ? LatinKeyboard.CODE_NEXT_LANGUAGE : LatinKeyboard.CODE_PREV_LANGUAGE;
622c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                // This will change keyboard layout.
623c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                mListener.onCodeInput(code, new int[] {code}, keyX, keyY);
624c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
625c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            mIsInSlidingLanguageSwitch = false;
626f172be448c0787fb67612afe05c2a30f944dab77Tadashi G. Takaoka            ((LatinKeyboard)mKeyboard).setSpacebarSlidingLanguageSwitchDiff(0);
627c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            return;
628c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        }
6296252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka        if (!mIsRepeatableKey) {
630d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            detectAndSendKey(keyIndex, keyX, keyY);
6316a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6326a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
634906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onLongPressed() {
635d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        mKeyAlreadyProcessed = true;
63663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        setReleasedKeyGraphics();
63763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        dismissKeyPreview();
6385c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
6391ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        if (queue != null) {
640d00d963b9d47c1bba6f65534033a33fe7c30dde5Tadashi G. Takaoka            queue.remove(this);
6411ddb4897fee79ec00c68e4a255e653568477a995Tadashi G. Takaoka        }
642d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
643d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
644906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka    public void onCancelEvent(int x, int y, long eventTime) {
645dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (DEBUG_EVENT)
646dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
6471d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
6485c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        final PointerTrackerQueue queue = sPointerTrackerQueue;
649c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        if (queue != null) {
650c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            queue.releaseAllPointersExcept(this, eventTime, true);
6511d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            queue.remove(this);
652c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka        }
653baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
6541d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
6551d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
656baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
6572321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        mTimerProxy.cancelKeyTimers();
658f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mDrawingProxy.cancelShowKeyPreview(this);
659d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka        dismissKeyPreview();
6608a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        setReleasedKeyGraphics(mKeyIndex);
661cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
6626a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6636a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
664a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    private void startRepeatKey(int keyIndex) {
665a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        final Key key = getKey(keyIndex);
666101a00e3d4b1c29ef2ecdecd1b72b43efde7791aTadashi G. Takaoka        if (key != null && key.mRepeatable) {
667a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            dismissKeyPreview();
668a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            onRepeatKey(keyIndex);
6695c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            mTimerProxy.startKeyRepeatTimer(sDelayBeforeKeyRepeatStart, keyIndex, this);
670a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            mIsRepeatableKey = true;
671a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        } else {
672a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka            mIsRepeatableKey = false;
673a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka        }
674a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    }
675a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka
676a0537fb4c73dff8beecc328720830af9719d0277Tadashi G. Takaoka    public void onRepeatKey(int keyIndex) {
677c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        Key key = getKey(keyIndex);
678c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        if (key != null) {
679c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka            detectAndSendKey(keyIndex, key.mX, key.mY);
680c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        }
6816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6826a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
683e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, int newKey) {
684a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        if (mKeys == null || mKeyDetector == null)
685a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            throw new NullPointerException("keyboard and/or key detector not set");
6868a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        int curKey = mKeyIndex;
6876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
688e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
689c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka        } else if (isValidKeyIndex(curKey)) {
690a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka            return mKeys.get(curKey).squaredDistanceToEdge(x, y)
691a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka                    >= mKeyDetector.getKeyHysteresisDistanceSquared();
6926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        } else {
693e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
6946a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6956a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6966a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
697101a00e3d4b1c29ef2ecdecd1b72b43efde7791aTadashi G. Takaoka    // The modifier key, such as shift key, should not show its key preview.
698ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka    private boolean isKeyPreviewNotRequired(int keyIndex) {
699967d6073bfd5f3d0e21512754d78d2e87c958e27Tadashi G. Takaoka        final Key key = getKey(keyIndex);
700e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key == null || !key.isEnabled())
701ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka            return true;
702ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka        // Such as spacebar sliding language switch.
703ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka        if (mKeyboard.needSpacebarPreview(keyIndex))
704ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka            return false;
705ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka        final int code = key.mCode;
706ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka        return isModifierCode(code) || code == Keyboard.CODE_DELETE
707ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka                || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE;
708ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka    }
709ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka
710ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka    private void showKeyPreview(int keyIndex) {
711ba9aefcc188b7f8ac99ba6cfef42a032b7d693a4Tadashi G. Takaoka        if (isKeyPreviewNotRequired(keyIndex))
712d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka            return;
713f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mDrawingProxy.showKeyPreview(keyIndex, this);
714d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    }
715d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka
716d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    private void dismissKeyPreview() {
717f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mDrawingProxy.dismissKeyPreview(this);
7186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
7196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
72066e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    private void startLongPressTimer(int keyIndex) {
72166e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka        Key key = getKey(keyIndex);
722c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        if (key.mCode == Keyboard.CODE_SHIFT) {
7235c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            mTimerProxy.startLongPressShiftTimer(sLongPressShiftKeyTimeout, keyIndex, this);
7249d9522abdcee70408c9e99ac20c8e1c224eef19dTadashi G. Takaoka        } else if (key.hasUppercaseLetter() && mKeyboard.isManualTemporaryUpperCase()) {
7252b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka            // We need not start long press timer on the key which has manual temporary upper case
7262b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka            // code defined and the keyboard is in manual temporary upper case mode.
7272b13b4f5e55b2bf5086b112f2d5d438810fdd70fTadashi G. Takaoka            return;
7285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        } else if (sKeyboardSwitcher.isInMomentarySwitchState()) {
7299e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // We use longer timeout for sliding finger input started from the symbols mode key.
7305c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            mTimerProxy.startLongPressTimer(sLongPressKeyTimeout * 3, keyIndex, this);
7314189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        } else {
7325c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            mTimerProxy.startLongPressTimer(sLongPressKeyTimeout, keyIndex, this);
7334189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        }
73466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
73566e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
736c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka    private void detectAndSendKey(int index, int x, int y) {
73783e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        final Key key = getKey(index);
73883e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
7398aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
740dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
741dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
742dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        if (key.mOutputText != null) {
743690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            callListenerOnTextInput(key);
744e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            callListenerOnRelease(key, key.mCode, false);
74583e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        } else {
746c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka            int code = key.mCode;
747dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            final int[] codes = mKeyDetector.newCodeArray();
748dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
74966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
750dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            // If keyboard is in manual temporary upper case state and key has manual temporary
7519d9522abdcee70408c9e99ac20c8e1c224eef19dTadashi G. Takaoka            // uppercase letter as key hint letter, alternate character code should be sent.
7529d9522abdcee70408c9e99ac20c8e1c224eef19dTadashi G. Takaoka            if (mKeyboard.isManualTemporaryUpperCase() && key.hasUppercaseLetter()) {
753520a297ad1d148a57bcf6559a9802d5d49182d70Tadashi G. Takaoka                code = key.mHintLabel.charAt(0);
754dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka                codes[0] = code;
755dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            }
75666e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
757dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            // Swap the first and second values in the codes array if the primary code is not the
758dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            // first value but the second value in the array. This happens when key debouncing is
759dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            // in effect.
760dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            if (codes.length >= 2 && codes[0] != code && codes[1] == code) {
761dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka                codes[1] = codes[0];
762dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka                codes[0] = code;
7636a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka            }
764690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            callListenerOnCodeInput(key, code, codes, x, y);
765e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            callListenerOnRelease(key, code, false);
7666a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
7676a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
7686a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
769dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private long mPreviousEventTime;
770dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
771dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private void printTouchEvent(String title, int x, int y, long eventTime) {
772dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
773dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final Key key = getKey(keyIndex);
774c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka        final String code = (key == null) ? "----" : keyCodePrintable(key.mCode);
775dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final long delta = eventTime - mPreviousEventTime;
776dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %3d(%s)", title,
777dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka                (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, keyIndex, code));
778dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        mPreviousEventTime = eventTime;
779dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
780dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
781dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static String keyCodePrintable(int primaryCode) {
782dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        final String modifier = isModifierCode(primaryCode) ? " modifier" : "";
783dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        return  String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode) + modifier;
7843d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka    }
7856e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
786