PointerTracker.java revision 5a309f57155fb95667c2ccdda730eaf175de8876
16a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka/* 26a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Copyright (C) 2010 Google Inc. 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 195a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView.UIHandler; 205a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME; 215a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.latin.R; 226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 235e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaokaimport android.content.res.Resources; 2440a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaokaimport android.util.Log; 25c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaokaimport android.view.MotionEvent; 266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 276a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaokapublic class PointerTracker { 2840a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka private static final String TAG = "PointerTracker"; 2940a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka private static final boolean DEBUG = false; 30c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka private static final boolean DEBUG_MOVE = false; 3140a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka 326a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public interface UIProxy { 336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public void invalidateKey(Key key); 346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public void showPreview(int keyIndex, PointerTracker tracker); 355e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka public boolean hasDistinctMultitouch(); 366a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 38c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka public final int mPointerId; 39c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka 406a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // Timing constants 415e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka private final int mDelayBeforeKeyRepeatStart; 425e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka private final int mLongPressKeyTimeout; 434189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka private final int mLongPressShiftKeyTimeout; 445e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka private final int mMultiTapKeyTimeout; 456a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // Miscellaneous constants 475a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka private static final int NOT_A_KEY = KeyDetector.NOT_A_KEY; 485a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE }; 496a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 506a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private final UIProxy mProxy; 516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private final UIHandler mHandler; 522085d43daf44752deae1b6b00a14cb0f517d69cbTadashi G. Takaoka private final KeyDetector mKeyDetector; 535a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka private KeyboardActionListener mListener; 54c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka private final boolean mHasDistinctMultitouch; 556a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 565a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka private Keyboard mKeyboard; 576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private Key[] mKeys; 58eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka private int mKeyHysteresisDistanceSquared = -1; 596a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 606e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private final KeyState mKeyState; 616a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 62c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka // true if event is already translated to a key action (long press or mini-keyboard) 63c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka private boolean mKeyAlreadyProcessed; 64c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka 656252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka // true if this pointer is repeatable key 666252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka private boolean mIsRepeatableKey; 676252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka 686a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // For multi-tap 696a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private int mLastSentIndex; 706a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private int mTapCount; 716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private long mLastTapTime; 726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private boolean mInMultiTap; 736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private final StringBuilder mPreviewLabel = new StringBuilder(1); 746a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 756a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // pressed key 76a521b31d96ff51672a3bc399a27a8f02dca840faTadashi G. Takaoka private int mPreviousKey = NOT_A_KEY; 776a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 786e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka // This class keeps track of a key index and a position where this pointer is. 796e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private static class KeyState { 806e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private final KeyDetector mKeyDetector; 816e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 826e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka // The position and time at which first down event occurred. 836e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private int mStartX; 846e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private int mStartY; 856e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private long mDownTime; 866e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 876e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka // The current key index where this pointer is. 886e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private int mKeyIndex = NOT_A_KEY; 896e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka // The position where mKeyIndex was recognized for the first time. 906e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private int mKeyX; 916e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private int mKeyY; 926e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 936e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka // Last pointer position. 946e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private int mLastX; 956e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private int mLastY; 966e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 976e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public KeyState(KeyDetector keyDetecor) { 986e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mKeyDetector = keyDetecor; 996e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1006e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1016e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int getKeyIndex() { 1026e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyIndex; 1036e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1046e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1056e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int getKeyX() { 1066e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyX; 1076e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1086e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1096e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int getKeyY() { 1106e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyY; 1116e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1126e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1136e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int getStartX() { 1146e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mStartX; 1156e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1166e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1176e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int getStartY() { 1186e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mStartY; 1196e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1206e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1216e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public long getDownTime() { 1226e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mDownTime; 1236e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1246e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1256e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int getLastX() { 1266e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mLastX; 1276e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1286e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1296e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int getLastY() { 1306e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mLastY; 1316e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1326e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1336e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int onDownKey(int x, int y, long eventTime) { 1346e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mStartX = x; 1356e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mStartY = y; 1366e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mDownTime = eventTime; 1376e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1386e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return onMoveToNewKey(onMoveKeyInternal(x, y), x, y); 1396e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1406e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1416e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private int onMoveKeyInternal(int x, int y) { 1426e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mLastX = x; 1436e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mLastY = y; 1446e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); 1456e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1466e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1476e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int onMoveKey(int x, int y) { 1486e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return onMoveKeyInternal(x, y); 1496e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1506e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1516e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int onMoveToNewKey(int keyIndex, int x, int y) { 1526e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mKeyIndex = keyIndex; 1536e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mKeyX = x; 1546e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mKeyY = y; 1556e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return keyIndex; 1566e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1576e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1586e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public int onUpKey(int x, int y) { 1596e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return onMoveKeyInternal(x, y); 1606e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1616e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 1626e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka public void onSetKeyboard() { 1636e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mKeyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(mKeyX, mKeyY, null); 1646e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1656e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } 1666e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 167c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy, 1685e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka Resources res) { 1696a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (proxy == null || handler == null || keyDetector == null) 1706a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new NullPointerException(); 171c6cb2ec1f3264a7b626022bcfdc8da180b87920cTadashi G. Takaoka mPointerId = id; 1726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy = proxy; 1736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler = handler; 1746a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeyDetector = keyDetector; 1756e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mKeyState = new KeyState(keyDetector); 1765e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); 1775e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); 1785e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout); 1794189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout); 1805e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mMultiTapKeyTimeout = res.getInteger(R.integer.config_multi_tap_key_timeout); 1816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka resetMultiTap(); 1826a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 1845a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka public void setOnKeyboardActionListener(KeyboardActionListener listener) { 1856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mListener = listener; 1866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 1885a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka public void setKeyboard(Keyboard keyboard, Key[] keys, float keyHysteresisDistance) { 18966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka if (keyboard == null || keys == null || keyHysteresisDistance < 0) 1906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new IllegalArgumentException(); 19166e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka mKeyboard = keyboard; 1926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeys = keys; 193eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); 194efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka // Update current key index because keyboard layout has been changed. 1956e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka mKeyState.onSetKeyboard(); 1966a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 1976a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 198c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka private boolean isValidKeyIndex(int keyIndex) { 199c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka return keyIndex >= 0 && keyIndex < mKeys.length; 200c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 201c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka 2026a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public Key getKey(int keyIndex) { 203c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka return isValidKeyIndex(keyIndex) ? mKeys[keyIndex] : null; 2046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2056a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 2062aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka private boolean isModifierInternal(int keyIndex) { 2072aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka Key key = getKey(keyIndex); 20840a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka if (key == null) 20940a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka return false; 21040a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka int primaryCode = key.codes[0]; 2115a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka return primaryCode == Keyboard.KEYCODE_SHIFT 2125a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka || primaryCode == Keyboard.KEYCODE_MODE_CHANGE; 21340a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka } 21440a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka 2152aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka public boolean isModifier() { 2166e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return isModifierInternal(mKeyState.getKeyIndex()); 2172aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka } 2182aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka 2192aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka public boolean isOnModifierKey(int x, int y) { 2202aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka return isModifierInternal(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null)); 2212aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka } 2222aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka 2233a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka public boolean isSpaceKey(int keyIndex) { 2243a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka Key key = getKey(keyIndex); 2253a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka return key != null && key.codes[0] == LatinIME.KEYCODE_SPACE; 2263a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka } 2273a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka 2284189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka public void releaseKey() { 2294189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka updateKeyGraphics(NOT_A_KEY); 2304189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } 2314189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka 2324189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka private void updateKeyGraphics(int keyIndex) { 2336a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka int oldKeyIndex = mPreviousKey; 2346a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mPreviousKey = keyIndex; 2356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (keyIndex != oldKeyIndex) { 236c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(oldKeyIndex)) { 2376a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // if new key index is not a key, old key was just released inside of the key. 2386a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka final boolean inside = (keyIndex == NOT_A_KEY); 2396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeys[oldKeyIndex].onReleased(inside); 2406a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy.invalidateKey(mKeys[oldKeyIndex]); 2416a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 242c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 2436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeys[keyIndex].onPressed(); 2446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy.invalidateKey(mKeys[keyIndex]); 2456a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2476a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 249c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka public void setAlreadyProcessed() { 250c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka mKeyAlreadyProcessed = true; 251c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka } 252c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka 253c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka public void onTouchEvent(int action, int x, int y, long eventTime) { 254c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka switch (action) { 255c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_MOVE: 256c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka onMoveEvent(x, y, eventTime); 257c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 258c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_DOWN: 259c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_POINTER_DOWN: 260c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka onDownEvent(x, y, eventTime); 261c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 262c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_UP: 263c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_POINTER_UP: 264c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka onUpEvent(x, y, eventTime); 265c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 266c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka case MotionEvent.ACTION_CANCEL: 267c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka onCancelEvent(x, y, eventTime); 268c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka break; 269c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka } 270c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka } 271c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka 272400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public void onDownEvent(int x, int y, long eventTime) { 273efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka if (DEBUG) 274efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka debugLog("onDownEvent:", x, y); 2756e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int keyIndex = mKeyState.onDownKey(x, y, eventTime); 276c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka mKeyAlreadyProcessed = false; 2776252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka mIsRepeatableKey = false; 2786a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka checkMultiTap(eventTime, keyIndex); 2796a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (mListener != null) { 2809dce586eaa8a99fac05da398694d6e26b2a6dfa8Ken Wakasa if (isValidKeyIndex(keyIndex)) { 2819dce586eaa8a99fac05da398694d6e26b2a6dfa8Ken Wakasa mListener.onPress(mKeys[keyIndex].codes[0]); 2829dce586eaa8a99fac05da398694d6e26b2a6dfa8Ken Wakasa // This onPress call may have changed keyboard layout and have updated mKeyIndex. 2839dce586eaa8a99fac05da398694d6e26b2a6dfa8Ken Wakasa // If that's the case, mKeyIndex has been updated in setKeyboard(). 2849dce586eaa8a99fac05da398694d6e26b2a6dfa8Ken Wakasa keyIndex = mKeyState.getKeyIndex(); 2859dce586eaa8a99fac05da398694d6e26b2a6dfa8Ken Wakasa } 2866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 287c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 288c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (mKeys[keyIndex].repeatable) { 289c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka repeatKey(keyIndex); 2905e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); 2916252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka mIsRepeatableKey = true; 292c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 293adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka startLongPressTimer(keyIndex); 2946a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2954189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(keyIndex); 2966a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 2976a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 298400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public void onMoveEvent(int x, int y, long eventTime) { 299efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka if (DEBUG_MOVE) 300efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka debugLog("onMoveEvent:", x, y); 301e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka if (mKeyAlreadyProcessed) 302e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka return; 3036e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka KeyState keyState = mKeyState; 304c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka final int keyIndex = keyState.onMoveKey(x, y); 305c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka final Key oldKey = getKey(keyState.getKeyIndex()); 306c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (isValidKeyIndex(keyIndex)) { 307c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (oldKey == null) { 3086e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x, y); 309adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka startLongPressTimer(keyIndex); 3106e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } else if (!isMinorMoveBounce(x, y, keyIndex)) { 311c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (mListener != null) 312c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka mListener.onRelease(oldKey.codes[0]); 3136a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka resetMultiTap(); 3146e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x, y); 315adf24e2eb49acd32d2655a3964f68da1e54c05ecTadashi G. Takaoka startLongPressTimer(keyIndex); 3166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 318c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (oldKey != null) { 319c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka if (mListener != null) 320c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka mListener.onRelease(oldKey.codes[0]); 3216e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x ,y); 3228aaab7c2867414f53228aa4faa2b8751105ac6ddTadashi G. Takaoka mHandler.cancelLongPressTimers(); 3236e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka } else if (!isMinorMoveBounce(x, y, keyIndex)) { 32407221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka resetMultiTap(); 3256e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka keyState.onMoveToNewKey(keyIndex, x ,y); 3268aaab7c2867414f53228aa4faa2b8751105ac6ddTadashi G. Takaoka mHandler.cancelLongPressTimers(); 32707221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka } 3286a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3294189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(mKeyState.getKeyIndex()); 3306a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3316a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 332400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public void onUpEvent(int x, int y, long eventTime) { 33340a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka if (DEBUG) 334400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka debugLog("onUpEvent :", x, y); 3354189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); 336efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka if (mKeyAlreadyProcessed) 337efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka return; 3386a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelKeyTimers(); 3396a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelPopupPreview(); 3406e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int keyIndex = mKeyState.onUpKey(x, y); 3416e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka if (isMinorMoveBounce(x, y, keyIndex)) { 3426e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka // Use previous fixed key index and coordinates. 3436e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka keyIndex = mKeyState.getKeyIndex(); 3446e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka x = mKeyState.getKeyX(); 3456e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka y = mKeyState.getKeyY(); 3466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3476252f468bc1306f71c9933f65b116dbbb5530de8Tadashi G. Takaoka if (!mIsRepeatableKey) { 3486e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka detectAndSendKey(keyIndex, x, y, eventTime); 3496a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3506e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka 351c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) 3526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy.invalidateKey(mKeys[keyIndex]); 3536a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3546a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 355400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public void onCancelEvent(int x, int y, long eventTime) { 35640a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka if (DEBUG) 357400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka debugLog("onCancelEvt:", x, y); 3586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelKeyTimers(); 3596a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mHandler.cancelPopupPreview(); 3604189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); 3616e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int keyIndex = mKeyState.getKeyIndex(); 362c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isValidKeyIndex(keyIndex)) 3636a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mProxy.invalidateKey(mKeys[keyIndex]); 3646a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3656a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 3666a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public void repeatKey(int keyIndex) { 367c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka Key key = getKey(keyIndex); 368c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (key != null) { 369c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka // While key is repeating, because there is no need to handle multi-tap key, we can 370c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka // pass -1 as eventTime argument. 371c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka detectAndSendKey(keyIndex, key.x, key.y, -1); 372c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } 3736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3746a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 375400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public int getLastX() { 3766e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getLastX(); 377400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka } 378400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka 379400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka public int getLastY() { 3806e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getLastY(); 381400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka } 382400046d62e22899e28efd2a62321c637c7831f81Tadashi G. Takaoka 38307221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka public long getDownTime() { 3846e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getDownTime(); 38507221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka } 38607221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka 3876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // These package scope methods are only for debugging purpose. 3886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka /* package */ int getStartX() { 3896e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getStartX(); 3906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 3926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka /* package */ int getStartY() { 3936e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka return mKeyState.getStartY(); 3946a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 3956a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 3966e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka private boolean isMinorMoveBounce(int x, int y, int newKey) { 397eb68036798f53763768e4ab37c7bfab9a2f36025Tadashi G. Takaoka if (mKeys == null || mKeyHysteresisDistanceSquared < 0) 3986a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka throw new IllegalStateException("keyboard and/or hysteresis not set"); 3996e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int curKey = mKeyState.getKeyIndex(); 4006a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (newKey == curKey) { 4016a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return true; 402c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka } else if (isValidKeyIndex(curKey)) { 40359b7bd07301196ac333dabafb5dd80750fcd2987Tadashi G. Takaoka return mKeys[curKey].squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared; 4046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 4056a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return false; 4066a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4076a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4086a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 4094189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka private void showKeyPreviewAndUpdateKeyGraphics(int keyIndex) { 4104189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka updateKeyGraphics(keyIndex); 411c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka // The modifier key, such as shift key, should not be shown as preview when multi-touch is 412c0b5c9c43e5eb7a6ed768d56f462ca9ed5c5f913Tadashi G. Takaoka // supported. On the other hand, if multi-touch is not supported, the modifier key should 413c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka // be shown as preview. 414efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka if (mHasDistinctMultitouch && isModifier()) { 415efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka mProxy.showPreview(NOT_A_KEY, this); 416efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka } else { 41740a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka mProxy.showPreview(keyIndex, this); 418efc4a437942f0bccd8815059c5f9d823023cfac1Tadashi G. Takaoka } 4196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4206a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 42166e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka private void startLongPressTimer(int keyIndex) { 42266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka Key key = getKey(keyIndex); 4235a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka if (key.codes[0] == Keyboard.KEYCODE_SHIFT) { 4244189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this); 4254189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } else { 4264189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); 4274189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka } 42866e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka } 42966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 430f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka private boolean isManualTemporaryUpperCase() { 43166e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka return mKeyboard instanceof LatinKeyboard 432f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka && ((LatinKeyboard)mKeyboard).isManualTemporaryUpperCase(); 43366e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka } 43466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 4356a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private void detectAndSendKey(int index, int x, int y, long eventTime) { 4365a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka final KeyboardActionListener listener = mListener; 43783e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka final Key key = getKey(index); 43883e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka 43983e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka if (key == null) { 44083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka if (listener != null) 44183e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka listener.onCancel(); 44283e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka } else { 4436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (key.text != null) { 4446a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (listener != null) { 4456a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka listener.onText(key.text); 4466a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka listener.onRelease(NOT_A_KEY); 4476a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4486a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 4496a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka int code = key.codes[0]; 4506a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka //TextEntryState.keyPressedAt(key, x, y); 4516a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka int[] codes = mKeyDetector.newCodeArray(); 4526a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); 4536a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // Multi-tap 4546a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (mInMultiTap) { 4556a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (mTapCount != -1) { 4565a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka mListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE, x, y); 4576a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 4586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mTapCount = 0; 4596a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4606a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka code = key.codes[mTapCount]; 4616a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 46266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 46310227a71a047706a2290ff0b57f3437d5add7b35Tadashi G. Takaoka // If keyboard is in manual temporary upper case state and key has manual temporary 46410227a71a047706a2290ff0b57f3437d5add7b35Tadashi G. Takaoka // shift code, alternate character code should be sent. 465f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka if (isManualTemporaryUpperCase() && key.manualTemporaryUpperCaseCode != 0) { 46610227a71a047706a2290ff0b57f3437d5add7b35Tadashi G. Takaoka code = key.manualTemporaryUpperCaseCode; 46766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka codes[0] = code; 46866e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka } 46966e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka 4706a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka /* 4716a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Swap the first and second values in the codes array if the primary code is not 4726a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * the first value but the second value in the array. This happens when key 4736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * debouncing is in effect. 4746a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka */ 4756a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (codes.length >= 2 && codes[0] != code && codes[1] == code) { 4766a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka codes[1] = codes[0]; 4776a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka codes[0] = code; 4786a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4796a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (listener != null) { 4806a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka listener.onKey(code, codes, x, y); 4816a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka listener.onRelease(code); 4826a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4846a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mLastSentIndex = index; 4856a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mLastTapTime = eventTime; 4866a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4876a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 4886a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 4896a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka /** 4906a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Handle multi-tap keys by producing the key label for the current multi-tap state. 4916a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka */ 4926a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka public CharSequence getPreviewText(Key key) { 4936a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (mInMultiTap) { 4946a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka // Multi-tap 4956a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mPreviewLabel.setLength(0); 4966a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]); 4976a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return mPreviewLabel; 4986a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 4996a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return key.label; 5006a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5016a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5026a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 5036a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private void resetMultiTap() { 5046a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mLastSentIndex = NOT_A_KEY; 5056a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mTapCount = 0; 5066a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mLastTapTime = -1; 5076a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mInMultiTap = false; 5086a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5096a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka 5106a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka private void checkMultiTap(long eventTime, int keyIndex) { 511c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka Key key = getKey(keyIndex); 512c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (key == null) 513c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka return; 514c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka 515c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka final boolean isMultiTap = 5165e02930a7f40b704f357f127d3d38fbdc193ffa1Tadashi G. Takaoka (eventTime < mLastTapTime + mMultiTapKeyTimeout && keyIndex == mLastSentIndex); 5176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka if (key.codes.length > 1) { 5186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mInMultiTap = true; 519c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (isMultiTap) { 5206a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mTapCount = (mTapCount + 1) % key.codes.length; 5216a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return; 5226a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } else { 5236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka mTapCount = -1; 5246a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka return; 5256a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5266a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 527c6df09182cac288c9a4de2cc05628dac6b6db41eTadashi G. Takaoka if (!isMultiTap) { 5286a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka resetMultiTap(); 5296a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5306a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka } 5313d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka 5323d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka private void debugLog(String title, int x, int y) { 5336e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); 5346e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka Key key = getKey(keyIndex); 5353d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka final String code; 5363d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka if (key == null) { 5373d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka code = "----"; 5383d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka } else { 5393d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka int primaryCode = key.codes[0]; 5403d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka code = String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode); 5413d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka } 542afb353277928b9c13a97f2aefde7412f432e060aTadashi G. Takaoka Log.d(TAG, String.format("%s%s[%d] %3d,%3d %3d(%s) %s", title, 543afb353277928b9c13a97f2aefde7412f432e060aTadashi G. Takaoka (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, keyIndex, code, 544afb353277928b9c13a97f2aefde7412f432e060aTadashi G. Takaoka (isModifier() ? "modifier" : ""))); 5453d4123fabb51a0c929401d98fca496759a5aa0d6Tadashi G. Takaoka } 5466e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka} 547