PointerTracker.java revision beb08b398fa73a26f2d42d6feec87e34a96ca2d9
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.android.inputmethod.keyboard; 18 19import android.os.SystemClock; 20import android.util.Log; 21import android.view.MotionEvent; 22import android.widget.TextView; 23 24import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; 25import com.android.inputmethod.latin.LatinImeLogger; 26 27import java.util.ArrayList; 28import java.util.List; 29import java.util.Set; 30 31public class PointerTracker { 32 private static final String TAG = PointerTracker.class.getSimpleName(); 33 private static final boolean DEBUG_EVENT = false; 34 private static final boolean DEBUG_MOVE_EVENT = false; 35 private static final boolean DEBUG_LISTENER = false; 36 private static boolean DEBUG_MODE = LatinImeLogger.sDBG; 37 38 public interface KeyEventHandler { 39 /** 40 * Get KeyDetector object that is used for this PointerTracker. 41 * @return the KeyDetector object that is used for this PointerTracker 42 */ 43 public KeyDetector getKeyDetector(); 44 45 /** 46 * Get KeyboardActionListener object that is used to register key code and so on. 47 * @return the KeyboardActionListner for this PointerTracker 48 */ 49 public KeyboardActionListener getKeyboardActionListener(); 50 51 /** 52 * Get DrawingProxy object that is used for this PointerTracker. 53 * @return the DrawingProxy object that is used for this PointerTracker 54 */ 55 public DrawingProxy getDrawingProxy(); 56 57 /** 58 * Get TimerProxy object that handles key repeat and long press timer event for this 59 * PointerTracker. 60 * @return the TimerProxy object that handles key repeat and long press timer event. 61 */ 62 public TimerProxy getTimerProxy(); 63 } 64 65 public interface DrawingProxy extends MoreKeysPanel.Controller { 66 public void invalidateKey(Key key); 67 public TextView inflateKeyPreviewText(); 68 public void showKeyPreview(PointerTracker tracker); 69 public void dismissKeyPreview(PointerTracker tracker); 70 } 71 72 public interface TimerProxy { 73 public void startKeyTypedTimer(); 74 public boolean isTyping(); 75 public void startKeyRepeatTimer(PointerTracker tracker); 76 public void startLongPressTimer(PointerTracker tracker); 77 public void startLongPressTimer(int code); 78 public void cancelLongPressTimer(); 79 public void startDoubleTapTimer(); 80 public void cancelDoubleTapTimer(); 81 public boolean isInDoubleTapTimeout(); 82 public void cancelKeyTimers(); 83 84 public static class Adapter implements TimerProxy { 85 @Override 86 public void startKeyTypedTimer() {} 87 @Override 88 public boolean isTyping() { return false; } 89 @Override 90 public void startKeyRepeatTimer(PointerTracker tracker) {} 91 @Override 92 public void startLongPressTimer(PointerTracker tracker) {} 93 @Override 94 public void startLongPressTimer(int code) {} 95 @Override 96 public void cancelLongPressTimer() {} 97 @Override 98 public void startDoubleTapTimer() {} 99 @Override 100 public void cancelDoubleTapTimer() {} 101 @Override 102 public boolean isInDoubleTapTimeout() { return false; } 103 @Override 104 public void cancelKeyTimers() {} 105 } 106 } 107 108 private static KeyboardSwitcher sKeyboardSwitcher; 109 // Parameters for pointer handling. 110 private static LatinKeyboardView.PointerTrackerParams sParams; 111 private static int sTouchNoiseThresholdDistanceSquared; 112 113 private static final List<PointerTracker> sTrackers = new ArrayList<PointerTracker>(); 114 private static PointerTrackerQueue sPointerTrackerQueue; 115 116 public final int mPointerId; 117 118 private DrawingProxy mDrawingProxy; 119 private TimerProxy mTimerProxy; 120 private KeyDetector mKeyDetector; 121 private KeyboardActionListener mListener = EMPTY_LISTENER; 122 123 private Keyboard mKeyboard; 124 private Set<Key> mKeys; 125 private int mKeyQuarterWidthSquared; 126 private final TextView mKeyPreviewText; 127 128 // The position and time at which first down event occurred. 129 private long mDownTime; 130 private long mUpTime; 131 132 // The current key where this pointer is. 133 private Key mCurrentKey = null; 134 // The position where the current key was recognized for the first time. 135 private int mKeyX; 136 private int mKeyY; 137 138 // Last pointer position. 139 private int mLastX; 140 private int mLastY; 141 142 // true if keyboard layout has been changed. 143 private boolean mKeyboardLayoutHasBeenChanged; 144 145 // true if event is already translated to a key action. 146 private boolean mKeyAlreadyProcessed; 147 148 // true if this pointer has been long-pressed and is showing a more keys panel. 149 private boolean mIsShowingMoreKeysPanel; 150 151 // true if this pointer is repeatable key 152 private boolean mIsRepeatableKey; 153 154 // true if this pointer is in sliding key input 155 boolean mIsInSlidingKeyInput; 156 157 // true if sliding key is allowed. 158 private boolean mIsAllowedSlidingKeyInput; 159 160 // ignore modifier key if true 161 private boolean mIgnoreModifierKey; 162 163 // Empty {@link KeyboardActionListener} 164 private static final KeyboardActionListener EMPTY_LISTENER = 165 new KeyboardActionListener.Adapter(); 166 167 public static void init(boolean hasDistinctMultitouch) { 168 if (hasDistinctMultitouch) { 169 sPointerTrackerQueue = new PointerTrackerQueue(); 170 } else { 171 sPointerTrackerQueue = null; 172 } 173 174 setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT); 175 sKeyboardSwitcher = KeyboardSwitcher.getInstance(); 176 } 177 178 public static void setParameters(LatinKeyboardView.PointerTrackerParams params) { 179 sParams = params; 180 sTouchNoiseThresholdDistanceSquared = (int)( 181 params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance); 182 } 183 184 public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) { 185 final List<PointerTracker> trackers = sTrackers; 186 187 // Create pointer trackers until we can get 'id+1'-th tracker, if needed. 188 for (int i = trackers.size(); i <= id; i++) { 189 final PointerTracker tracker = new PointerTracker(i, handler); 190 trackers.add(tracker); 191 } 192 193 return trackers.get(id); 194 } 195 196 public static boolean isAnyInSlidingKeyInput() { 197 return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false; 198 } 199 200 public static void setKeyboardActionListener(KeyboardActionListener listener) { 201 for (final PointerTracker tracker : sTrackers) { 202 tracker.mListener = listener; 203 } 204 } 205 206 public static void setKeyDetector(KeyDetector keyDetector) { 207 for (final PointerTracker tracker : sTrackers) { 208 tracker.setKeyDetectorInner(keyDetector); 209 // Mark that keyboard layout has been changed. 210 tracker.mKeyboardLayoutHasBeenChanged = true; 211 } 212 } 213 214 public static void dismissAllKeyPreviews() { 215 for (final PointerTracker tracker : sTrackers) { 216 tracker.setReleasedKeyGraphics(tracker.mCurrentKey); 217 } 218 } 219 220 public PointerTracker(int id, KeyEventHandler handler) { 221 if (handler == null) 222 throw new NullPointerException(); 223 mPointerId = id; 224 setKeyDetectorInner(handler.getKeyDetector()); 225 mListener = handler.getKeyboardActionListener(); 226 mDrawingProxy = handler.getDrawingProxy(); 227 mTimerProxy = handler.getTimerProxy(); 228 mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText(); 229 } 230 231 public TextView getKeyPreviewText() { 232 return mKeyPreviewText; 233 } 234 235 // Returns true if keyboard has been changed by this callback. 236 private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) { 237 final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); 238 if (DEBUG_LISTENER) { 239 Log.d(TAG, "onPress : " + KeyDetector.printableCode(key) 240 + " ignoreModifier=" + ignoreModifierKey 241 + " enabled=" + key.isEnabled()); 242 } 243 if (ignoreModifierKey) { 244 return false; 245 } 246 if (key.isEnabled()) { 247 mListener.onPressKey(key.mCode); 248 final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; 249 mKeyboardLayoutHasBeenChanged = false; 250 return keyboardLayoutHasBeenChanged; 251 } 252 return false; 253 } 254 255 // Note that we need primaryCode argument because the keyboard may in shifted state and the 256 // primaryCode is different from {@link Key#mCode}. 257 private void callListenerOnCodeInput(Key key, int primaryCode, int[] keyCodes, int x, int y) { 258 final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); 259 final boolean alterCode = key.altCodeWhileTyping() && mTimerProxy.isTyping(); 260 final int code = alterCode ? key.mAltCode : primaryCode; 261 if (DEBUG_LISTENER) { 262 Log.d(TAG, "onCodeInput: " + Keyboard.printableCode(code) + " text=" + key.mOutputText 263 + " codes="+ KeyDetector.printableCodes(keyCodes) + " x=" + x + " y=" + y 264 + " ignoreModifier=" + ignoreModifierKey + " alterCode=" + alterCode 265 + " enabled=" + key.isEnabled()); 266 } 267 if (ignoreModifierKey) { 268 return; 269 } 270 if (key.isEnabled()) { 271 if (code == Keyboard.CODE_OUTPUT_TEXT) { 272 mListener.onTextInput(key.mOutputText); 273 } else if (code != Keyboard.CODE_UNSPECIFIED) { 274 mListener.onCodeInput(code, keyCodes, x, y); 275 } 276 if (!key.altCodeWhileTyping() && !key.isModifier()) { 277 mTimerProxy.startKeyTypedTimer(); 278 } 279 } 280 } 281 282 // Note that we need primaryCode argument because the keyboard may in shifted state and the 283 // primaryCode is different from {@link Key#mCode}. 284 private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) { 285 final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); 286 if (DEBUG_LISTENER) { 287 Log.d(TAG, "onRelease : " + Keyboard.printableCode(primaryCode) 288 + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey 289 + " enabled="+ key.isEnabled()); 290 } 291 if (ignoreModifierKey) { 292 return; 293 } 294 if (key.isEnabled()) { 295 mListener.onReleaseKey(primaryCode, withSliding); 296 } 297 } 298 299 private void callListenerOnCancelInput() { 300 if (DEBUG_LISTENER) 301 Log.d(TAG, "onCancelInput"); 302 mListener.onCancelInput(); 303 } 304 305 private void setKeyDetectorInner(KeyDetector keyDetector) { 306 mKeyDetector = keyDetector; 307 mKeyboard = keyDetector.getKeyboard(); 308 mKeys = mKeyboard.mKeys; 309 final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4; 310 mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth; 311 } 312 313 public boolean isInSlidingKeyInput() { 314 return mIsInSlidingKeyInput; 315 } 316 317 public Key getKey() { 318 return mCurrentKey; 319 } 320 321 public boolean isModifier() { 322 return mCurrentKey != null && mCurrentKey.isModifier(); 323 } 324 325 public Key getKeyOn(int x, int y) { 326 return mKeyDetector.getKeyAndNearbyCodes(x, y, null); 327 } 328 329 private void setReleasedKeyGraphics(Key key) { 330 mDrawingProxy.dismissKeyPreview(this); 331 if (key != null && key.isEnabled()) { 332 key.onReleased(); 333 mDrawingProxy.invalidateKey(key); 334 335 if (key.isShift()) { 336 for (final Key shiftKey : mKeyboard.mShiftKeys) { 337 if (shiftKey != key) { 338 shiftKey.onReleased(); 339 mDrawingProxy.invalidateKey(shiftKey); 340 } 341 } 342 } 343 344 if (key.altCodeWhileTyping()) { 345 final Key altKey = mKeyboard.getKey(key.mAltCode); 346 if (altKey != null) { 347 altKey.onReleased(); 348 mDrawingProxy.invalidateKey(altKey); 349 } 350 } 351 } 352 } 353 354 private void setPressedKeyGraphics(Key key) { 355 if (key != null && key.isEnabled()) { 356 if (!key.noKeyPreview()) { 357 mDrawingProxy.showKeyPreview(this); 358 } 359 key.onPressed(); 360 mDrawingProxy.invalidateKey(key); 361 362 if (key.isShift()) { 363 for (final Key shiftKey : mKeyboard.mShiftKeys) { 364 if (shiftKey != key) { 365 shiftKey.onPressed(); 366 mDrawingProxy.invalidateKey(shiftKey); 367 } 368 } 369 } 370 371 if (key.altCodeWhileTyping() && mTimerProxy.isTyping()) { 372 final Key altKey = mKeyboard.getKey(key.mAltCode); 373 if (altKey != null) { 374 // TODO: Show altKey's preview. 375 altKey.onPressed(); 376 mDrawingProxy.invalidateKey(altKey); 377 } 378 } 379 } 380 } 381 382 public int getLastX() { 383 return mLastX; 384 } 385 386 public int getLastY() { 387 return mLastY; 388 } 389 390 public long getDownTime() { 391 return mDownTime; 392 } 393 394 private Key onDownKey(int x, int y, long eventTime) { 395 mDownTime = eventTime; 396 return onMoveToNewKey(onMoveKeyInternal(x, y), x, y); 397 } 398 399 private Key onMoveKeyInternal(int x, int y) { 400 mLastX = x; 401 mLastY = y; 402 return mKeyDetector.getKeyAndNearbyCodes(x, y, null); 403 } 404 405 private Key onMoveKey(int x, int y) { 406 return onMoveKeyInternal(x, y); 407 } 408 409 private Key onMoveToNewKey(Key newKey, int x, int y) { 410 mCurrentKey = newKey; 411 mKeyX = x; 412 mKeyY = y; 413 return newKey; 414 } 415 416 private Key onUpKey(int x, int y, long eventTime) { 417 mUpTime = eventTime; 418 mCurrentKey = null; 419 return onMoveKeyInternal(x, y); 420 } 421 422 public void processMotionEvent(int action, int x, int y, long eventTime, 423 KeyEventHandler handler) { 424 switch (action) { 425 case MotionEvent.ACTION_DOWN: 426 case MotionEvent.ACTION_POINTER_DOWN: 427 onDownEvent(x, y, eventTime, handler); 428 break; 429 case MotionEvent.ACTION_UP: 430 case MotionEvent.ACTION_POINTER_UP: 431 onUpEvent(x, y, eventTime); 432 break; 433 case MotionEvent.ACTION_MOVE: 434 onMoveEvent(x, y, eventTime); 435 break; 436 case MotionEvent.ACTION_CANCEL: 437 onCancelEvent(x, y, eventTime); 438 break; 439 } 440 } 441 442 public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) { 443 if (DEBUG_EVENT) 444 printTouchEvent("onDownEvent:", x, y, eventTime); 445 446 mDrawingProxy = handler.getDrawingProxy(); 447 mTimerProxy = handler.getTimerProxy(); 448 setKeyboardActionListener(handler.getKeyboardActionListener()); 449 setKeyDetectorInner(handler.getKeyDetector()); 450 // Naive up-to-down noise filter. 451 final long deltaT = eventTime - mUpTime; 452 if (deltaT < sParams.mTouchNoiseThresholdTime) { 453 final int dx = x - mLastX; 454 final int dy = y - mLastY; 455 final int distanceSquared = (dx * dx + dy * dy); 456 if (distanceSquared < sTouchNoiseThresholdDistanceSquared) { 457 if (DEBUG_MODE) 458 Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT 459 + " distance=" + distanceSquared); 460 mKeyAlreadyProcessed = true; 461 return; 462 } 463 } 464 465 final PointerTrackerQueue queue = sPointerTrackerQueue; 466 if (queue != null) { 467 final Key key = getKeyOn(x, y); 468 if (key != null && key.isModifier()) { 469 // Before processing a down event of modifier key, all pointers already being 470 // tracked should be released. 471 queue.releaseAllPointers(eventTime); 472 } 473 queue.add(this); 474 } 475 onDownEventInternal(x, y, eventTime); 476 } 477 478 private void onDownEventInternal(int x, int y, long eventTime) { 479 Key key = onDownKey(x, y, eventTime); 480 // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding 481 // from modifier key, or 3) this pointer's KeyDetector always allows sliding input. 482 mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled 483 || (key != null && key.isModifier()) 484 || mKeyDetector.alwaysAllowsSlidingInput(); 485 mKeyboardLayoutHasBeenChanged = false; 486 mKeyAlreadyProcessed = false; 487 mIsRepeatableKey = false; 488 mIsInSlidingKeyInput = false; 489 mIgnoreModifierKey = false; 490 if (key != null) { 491 // This onPress call may have changed keyboard layout. Those cases are detected at 492 // {@link #setKeyboard}. In those cases, we should update key according to the new 493 // keyboard layout. 494 if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { 495 key = onDownKey(x, y, eventTime); 496 } 497 498 startRepeatKey(key); 499 startLongPressTimer(key); 500 setPressedKeyGraphics(key); 501 } 502 } 503 504 private void startSlidingKeyInput(Key key) { 505 if (!mIsInSlidingKeyInput) { 506 mIgnoreModifierKey = key.isModifier(); 507 } 508 mIsInSlidingKeyInput = true; 509 } 510 511 public void onMoveEvent(int x, int y, long eventTime) { 512 if (DEBUG_MOVE_EVENT) 513 printTouchEvent("onMoveEvent:", x, y, eventTime); 514 if (mKeyAlreadyProcessed) 515 return; 516 517 final int lastX = mLastX; 518 final int lastY = mLastY; 519 final Key oldKey = mCurrentKey; 520 Key key = onMoveKey(x, y); 521 if (key != null) { 522 if (oldKey == null) { 523 // The pointer has been slid in to the new key, but the finger was not on any keys. 524 // In this case, we must call onPress() to notify that the new key is being pressed. 525 // This onPress call may have changed keyboard layout. Those cases are detected at 526 // {@link #setKeyboard}. In those cases, we should update key according to the 527 // new keyboard layout. 528 if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { 529 key = onMoveKey(x, y); 530 } 531 onMoveToNewKey(key, x, y); 532 startLongPressTimer(key); 533 setPressedKeyGraphics(key); 534 } else if (isMajorEnoughMoveToBeOnNewKey(x, y, key)) { 535 // The pointer has been slid in to the new key from the previous key, we must call 536 // onRelease() first to notify that the previous key has been released, then call 537 // onPress() to notify that the new key is being pressed. 538 setReleasedKeyGraphics(oldKey); 539 callListenerOnRelease(oldKey, oldKey.mCode, true); 540 startSlidingKeyInput(oldKey); 541 mTimerProxy.cancelKeyTimers(); 542 startRepeatKey(key); 543 if (mIsAllowedSlidingKeyInput) { 544 // This onPress call may have changed keyboard layout. Those cases are detected 545 // at {@link #setKeyboard}. In those cases, we should update key according 546 // to the new keyboard layout. 547 if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { 548 key = onMoveKey(x, y); 549 } 550 onMoveToNewKey(key, x, y); 551 startLongPressTimer(key); 552 setPressedKeyGraphics(key); 553 } else { 554 // HACK: On some devices, quick successive touches may be translated to sudden 555 // move by touch panel firmware. This hack detects the case and translates the 556 // move event to successive up and down events. 557 final int dx = x - lastX; 558 final int dy = y - lastY; 559 final int lastMoveSquared = dx * dx + dy * dy; 560 if (lastMoveSquared >= mKeyQuarterWidthSquared) { 561 if (DEBUG_MODE) 562 Log.w(TAG, String.format("onMoveEvent: sudden move is translated to " 563 + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y)); 564 onUpEventInternal(lastX, lastY, eventTime); 565 onDownEventInternal(x, y, eventTime); 566 } else { 567 mKeyAlreadyProcessed = true; 568 setReleasedKeyGraphics(oldKey); 569 } 570 } 571 } 572 } else { 573 if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, key)) { 574 // The pointer has been slid out from the previous key, we must call onRelease() to 575 // notify that the previous key has been released. 576 setReleasedKeyGraphics(oldKey); 577 callListenerOnRelease(oldKey, oldKey.mCode, true); 578 startSlidingKeyInput(oldKey); 579 mTimerProxy.cancelLongPressTimer(); 580 if (mIsAllowedSlidingKeyInput) { 581 onMoveToNewKey(key, x, y); 582 } else { 583 mKeyAlreadyProcessed = true; 584 } 585 } 586 } 587 } 588 589 public void onUpEvent(int x, int y, long eventTime) { 590 if (DEBUG_EVENT) 591 printTouchEvent("onUpEvent :", x, y, eventTime); 592 593 final PointerTrackerQueue queue = sPointerTrackerQueue; 594 if (queue != null) { 595 if (mCurrentKey != null && mCurrentKey.isModifier()) { 596 // Before processing an up event of modifier key, all pointers already being 597 // tracked should be released. 598 queue.releaseAllPointersExcept(this, eventTime); 599 } else { 600 queue.releaseAllPointersOlderThan(this, eventTime); 601 } 602 queue.remove(this); 603 } 604 onUpEventInternal(x, y, eventTime); 605 } 606 607 // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event. 608 // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a 609 // "virtual" up event. 610 public void onPhantomUpEvent(int x, int y, long eventTime) { 611 if (DEBUG_EVENT) 612 printTouchEvent("onPhntEvent:", x, y, eventTime); 613 onUpEventInternal(x, y, eventTime); 614 mKeyAlreadyProcessed = true; 615 } 616 617 private void onUpEventInternal(int x, int y, long eventTime) { 618 mTimerProxy.cancelKeyTimers(); 619 mIsInSlidingKeyInput = false; 620 final int keyX, keyY; 621 if (isMajorEnoughMoveToBeOnNewKey(x, y, onMoveKey(x, y))) { 622 keyX = x; 623 keyY = y; 624 } else { 625 // Use previous fixed key coordinates. 626 keyX = mKeyX; 627 keyY = mKeyY; 628 } 629 final Key key = onUpKey(keyX, keyY, eventTime); 630 setReleasedKeyGraphics(key); 631 if (mIsShowingMoreKeysPanel) { 632 mDrawingProxy.dismissMoreKeysPanel(); 633 mIsShowingMoreKeysPanel = false; 634 } 635 if (mKeyAlreadyProcessed) 636 return; 637 if (!mIsRepeatableKey) { 638 detectAndSendKey(key, keyX, keyY); 639 } 640 } 641 642 public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) { 643 onLongPressed(); 644 onDownEvent(x, y, SystemClock.uptimeMillis(), handler); 645 mIsShowingMoreKeysPanel = true; 646 } 647 648 public void onLongPressed() { 649 mKeyAlreadyProcessed = true; 650 setReleasedKeyGraphics(mCurrentKey); 651 final PointerTrackerQueue queue = sPointerTrackerQueue; 652 if (queue != null) { 653 queue.remove(this); 654 } 655 } 656 657 public void onCancelEvent(int x, int y, long eventTime) { 658 if (DEBUG_EVENT) 659 printTouchEvent("onCancelEvt:", x, y, eventTime); 660 661 final PointerTrackerQueue queue = sPointerTrackerQueue; 662 if (queue != null) { 663 queue.releaseAllPointersExcept(this, eventTime); 664 queue.remove(this); 665 } 666 onCancelEventInternal(); 667 } 668 669 private void onCancelEventInternal() { 670 mTimerProxy.cancelKeyTimers(); 671 setReleasedKeyGraphics(mCurrentKey); 672 mIsInSlidingKeyInput = false; 673 if (mIsShowingMoreKeysPanel) { 674 mDrawingProxy.dismissMoreKeysPanel(); 675 mIsShowingMoreKeysPanel = false; 676 } 677 } 678 679 private void startRepeatKey(Key key) { 680 if (key != null && key.isRepeatable()) { 681 onRepeatKey(key); 682 mTimerProxy.startKeyRepeatTimer(this); 683 mIsRepeatableKey = true; 684 } else { 685 mIsRepeatableKey = false; 686 } 687 } 688 689 public void onRepeatKey(Key key) { 690 if (key != null) { 691 detectAndSendKey(key, key.mX, key.mY); 692 } 693 } 694 695 private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, Key newKey) { 696 if (mKeys == null || mKeyDetector == null) 697 throw new NullPointerException("keyboard and/or key detector not set"); 698 Key curKey = mCurrentKey; 699 if (newKey == curKey) { 700 return false; 701 } else if (curKey != null) { 702 return curKey.squaredDistanceToEdge(x, y) 703 >= mKeyDetector.getKeyHysteresisDistanceSquared(); 704 } else { 705 return true; 706 } 707 } 708 709 private void startLongPressTimer(Key key) { 710 if (key != null && key.isLongPressEnabled()) { 711 mTimerProxy.startLongPressTimer(this); 712 } 713 } 714 715 private void detectAndSendKey(Key key, int x, int y) { 716 if (key == null) { 717 callListenerOnCancelInput(); 718 return; 719 } 720 721 int code = key.mCode; 722 final int[] codes = mKeyDetector.newCodeArray(); 723 mKeyDetector.getKeyAndNearbyCodes(x, y, codes); 724 725 // Swap the first and second values in the codes array if the primary code is not the 726 // first value but the second value in the array. This happens when key debouncing is 727 // in effect. 728 if (codes.length >= 2 && codes[0] != code && codes[1] == code) { 729 codes[1] = codes[0]; 730 codes[0] = code; 731 } 732 callListenerOnCodeInput(key, code, codes, x, y); 733 callListenerOnRelease(key, code, false); 734 } 735 736 private long mPreviousEventTime; 737 738 private void printTouchEvent(String title, int x, int y, long eventTime) { 739 final Key key = mKeyDetector.getKeyAndNearbyCodes(x, y, null); 740 final String code = KeyDetector.printableCode(key); 741 final long delta = eventTime - mPreviousEventTime; 742 Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title, 743 (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, code)); 744 mPreviousEventTime = eventTime; 745 } 746} 747