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