Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.content.browser.input; | 5 package org.chromium.content.browser.input; |
| 6 | 6 |
| 7 import android.os.Handler; | 7 import android.os.Handler; |
| 8 import android.os.ResultReceiver; | 8 import android.os.ResultReceiver; |
| 9 import android.os.SystemClock; | 9 import android.os.SystemClock; |
| 10 import android.text.Editable; | 10 import android.text.Editable; |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 109 static int sTextInputTypeUrl; | 109 static int sTextInputTypeUrl; |
| 110 static int sTextInputTypeEmail; | 110 static int sTextInputTypeEmail; |
| 111 static int sTextInputTypeTel; | 111 static int sTextInputTypeTel; |
| 112 static int sTextInputTypeNumber; | 112 static int sTextInputTypeNumber; |
| 113 static int sTextInputTypeContentEditable; | 113 static int sTextInputTypeContentEditable; |
| 114 static int sModifierShift; | 114 static int sModifierShift; |
| 115 static int sModifierAlt; | 115 static int sModifierAlt; |
| 116 static int sModifierCtrl; | 116 static int sModifierCtrl; |
| 117 static int sModifierCapsLockOn; | 117 static int sModifierCapsLockOn; |
| 118 static int sModifierNumLockOn; | 118 static int sModifierNumLockOn; |
| 119 static KeyCharacterMap sKeyCharacterMap; | |
| 119 | 120 |
| 120 private long mNativeImeAdapterAndroid; | 121 private long mNativeImeAdapterAndroid; |
| 121 private InputMethodManagerWrapper mInputMethodManagerWrapper; | 122 private InputMethodManagerWrapper mInputMethodManagerWrapper; |
| 122 private AdapterInputConnection mInputConnection; | 123 private AdapterInputConnection mInputConnection; |
| 123 private final ImeAdapterDelegate mViewEmbedder; | 124 private final ImeAdapterDelegate mViewEmbedder; |
| 124 private final Handler mHandler; | 125 private final Handler mHandler; |
| 125 private DelayedDismissInput mDismissInput = null; | 126 private DelayedDismissInput mDismissInput = null; |
| 126 private int mTextInputType; | 127 private int mTextInputType; |
| 128 private String mLastComposeText; | |
| 129 | |
| 130 @VisibleForTesting | |
| 131 int mLastSyntheticKeyCode; | |
| 127 | 132 |
| 128 @VisibleForTesting | 133 @VisibleForTesting |
| 129 boolean mIsShowWithoutHideOutstanding = false; | 134 boolean mIsShowWithoutHideOutstanding = false; |
| 130 | 135 |
| 131 /** | 136 /** |
| 132 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to | 137 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to |
| 133 * InputMethodManager. | 138 * InputMethodManager. |
| 134 * @param embedder The view that is used for callbacks from ImeAdapter. | 139 * @param embedder The view that is used for callbacks from ImeAdapter. |
| 135 */ | 140 */ |
| 136 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { | 141 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 310 } | 315 } |
| 311 | 316 |
| 312 private int shouldSendKeyEventWithKeyCode(String text) { | 317 private int shouldSendKeyEventWithKeyCode(String text) { |
| 313 if (text.length() != 1) return COMPOSITION_KEY_CODE; | 318 if (text.length() != 1) return COMPOSITION_KEY_CODE; |
| 314 | 319 |
| 315 if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER; | 320 if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER; |
| 316 else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB; | 321 else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB; |
| 317 else return COMPOSITION_KEY_CODE; | 322 else return COMPOSITION_KEY_CODE; |
| 318 } | 323 } |
| 319 | 324 |
| 325 private static int androidKeyCodeForCharacter(char chr) { | |
| 326 if (sKeyCharacterMap == null) { | |
| 327 sKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYB OARD); | |
| 328 } | |
| 329 char[] chars = new char[1]; | |
| 330 chars[0] = chr; | |
| 331 KeyEvent[] events = sKeyCharacterMap.getEvents(chars); | |
| 332 return events[0].getKeyCode(); | |
| 333 } | |
| 334 | |
| 335 @VisibleForTesting | |
| 336 public static int getTypedKeycodeGuess(String oldtext, String newtext) { | |
| 337 // Starting typing a new composition should add only a single character. Any composition | |
| 338 // beginning with text longer than that must come from something other t han typing so | |
| 339 // return 0. | |
| 340 if (oldtext == null) { | |
| 341 if (newtext.length() == 1) { | |
| 342 return androidKeyCodeForCharacter(newtext.charAt(0)); | |
| 343 } else { | |
| 344 return 0; | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 // The content has grown in length: assume the last character is the key that caused it. | |
| 349 if (newtext.length() > oldtext.length() && newtext.startsWith(oldtext)) | |
| 350 return androidKeyCodeForCharacter(newtext.charAt(newtext.length() - 1)); | |
| 351 | |
| 352 // The content has shrunk in length: assume that backspace was pressed. | |
| 353 if (oldtext.length() > newtext.length() && oldtext.startsWith(newtext)) | |
| 354 return KeyEvent.KEYCODE_DEL; | |
| 355 | |
| 356 // The content is unchanged or has undergone a complex change (i.e. not a simple tail | |
| 357 // modification) so return an unknown key-code. | |
| 358 return 0; | |
| 359 } | |
| 360 | |
| 320 void sendKeyEventWithKeyCode(int keyCode, int flags) { | 361 void sendKeyEventWithKeyCode(int keyCode, int flags) { |
| 321 long eventTime = SystemClock.uptimeMillis(); | 362 long eventTime = SystemClock.uptimeMillis(); |
| 322 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, | 363 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, |
| 323 KeyEvent.ACTION_DOWN, keyCode, 0, 0, | 364 KeyEvent.ACTION_DOWN, keyCode, 0, 0, |
| 324 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 365 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| 325 flags)); | 366 flags)); |
| 326 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev entTime, | 367 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev entTime, |
| 327 KeyEvent.ACTION_UP, keyCode, 0, 0, | 368 KeyEvent.ACTION_UP, keyCode, 0, 0, |
| 328 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 369 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| 329 flags)); | 370 flags)); |
| 330 } | 371 } |
| 331 | 372 |
| 332 // Calls from Java to C++ | 373 // Calls from Java to C++ |
| 333 | 374 |
| 334 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP osition, | 375 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP osition, |
| 335 boolean isCommit) { | 376 boolean isCommit) { |
| 336 if (mNativeImeAdapterAndroid == 0) return false; | 377 if (mNativeImeAdapterAndroid == 0) return false; |
| 337 String textStr = text.toString(); | 378 String textStr = text.toString(); |
| 338 | 379 |
| 339 // Committing an empty string finishes the current composition. | 380 // Committing an empty string finishes the current composition. |
| 340 boolean isFinish = textStr.isEmpty(); | 381 boolean isFinish = textStr.isEmpty(); |
| 341 mViewEmbedder.onImeEvent(isFinish); | 382 mViewEmbedder.onImeEvent(isFinish); |
| 342 int keyCode = shouldSendKeyEventWithKeyCode(textStr); | 383 int keyCode = shouldSendKeyEventWithKeyCode(textStr); |
| 343 long timeStampMs = SystemClock.uptimeMillis(); | 384 long timeStampMs = SystemClock.uptimeMillis(); |
| 344 | 385 |
| 345 if (keyCode != COMPOSITION_KEY_CODE) { | 386 if (keyCode != COMPOSITION_KEY_CODE) { |
| 346 sendKeyEventWithKeyCode(keyCode, | 387 sendKeyEventWithKeyCode(keyCode, |
| 347 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ; | 388 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ; |
| 348 } else { | 389 } else { |
| 349 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown, | 390 keyCode = getTypedKeycodeGuess(mLastComposeText, textStr); |
| 350 timeStampMs, keyCode, 0); | 391 mLastComposeText = textStr; |
| 392 mLastSyntheticKeyCode = keyCode; | |
| 393 | |
| 394 if (keyCode != 0) | |
| 395 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventType RawKeyDown, | |
| 396 timeStampMs, keyCode, 0); | |
| 351 if (isCommit) { | 397 if (isCommit) { |
| 352 nativeCommitText(mNativeImeAdapterAndroid, textStr); | 398 nativeCommitText(mNativeImeAdapterAndroid, textStr); |
| 399 mLastComposeText = null; | |
| 353 } else { | 400 } else { |
| 354 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition); | 401 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition); |
| 355 } | 402 } |
| 356 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p, | 403 if (keyCode != 0) |
| 357 timeStampMs, keyCode, 0); | 404 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventType KeyUp, |
| 405 timeStampMs, keyCode, 0); | |
| 358 } | 406 } |
| 359 | 407 |
| 360 return true; | 408 return true; |
| 361 } | 409 } |
| 362 | 410 |
| 363 void finishComposingText() { | 411 void finishComposingText() { |
| 364 if (mNativeImeAdapterAndroid == 0) return; | 412 if (mNativeImeAdapterAndroid == 0) return; |
| 413 /*int keyCode = mLastComposeText != null && mLastComposeText.length() == 1 ? | |
| 414 KeyEvent.KEYCODE_DEL : 0; | |
| 415 long timeStampMs = SystemClock.uptimeMillis(); | |
| 416 mLastComposeText = null; | |
| 417 | |
| 418 if (keyCode != 0) | |
| 419 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown, | |
| 420 timeStampMs, keyCode, 0);*/ | |
| 365 nativeFinishComposingText(mNativeImeAdapterAndroid); | 421 nativeFinishComposingText(mNativeImeAdapterAndroid); |
| 422 /*if (keyCode != 0) | |
| 423 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p, | |
| 424 timeStampMs, keyCode, 0);*/ | |
| 366 } | 425 } |
| 367 | 426 |
| 368 boolean translateAndSendNativeEvents(KeyEvent event) { | 427 boolean translateAndSendNativeEvents(KeyEvent event) { |
| 369 if (mNativeImeAdapterAndroid == 0) return false; | 428 if (mNativeImeAdapterAndroid == 0) return false; |
| 370 | 429 |
| 371 int action = event.getAction(); | 430 int action = event.getAction(); |
| 372 if (action != KeyEvent.ACTION_DOWN && | 431 if (action != KeyEvent.ACTION_DOWN && |
| 373 action != KeyEvent.ACTION_UP) { | 432 action != KeyEvent.ACTION_UP) { |
| 374 // action == KeyEvent.ACTION_MULTIPLE | 433 // action == KeyEvent.ACTION_MULTIPLE |
| 375 // TODO(bulach): confirm the actual behavior. Apparently: | 434 // TODO(bulach): confirm the actual behavior. Apparently: |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 401 * Send a request to the native counterpart to delete a given range of chara cters. | 460 * Send a request to the native counterpart to delete a given range of chara cters. |
| 402 * @param beforeLength Number of characters to extend the selection by befor e the existing | 461 * @param beforeLength Number of characters to extend the selection by befor e the existing |
| 403 * selection. | 462 * selection. |
| 404 * @param afterLength Number of characters to extend the selection by after the existing | 463 * @param afterLength Number of characters to extend the selection by after the existing |
| 405 * selection. | 464 * selection. |
| 406 * @return Whether the native counterpart of ImeAdapter received the call. | 465 * @return Whether the native counterpart of ImeAdapter received the call. |
| 407 */ | 466 */ |
| 408 boolean deleteSurroundingText(int beforeLength, int afterLength) { | 467 boolean deleteSurroundingText(int beforeLength, int afterLength) { |
| 409 mViewEmbedder.onImeEvent(false); | 468 mViewEmbedder.onImeEvent(false); |
| 410 if (mNativeImeAdapterAndroid == 0) return false; | 469 if (mNativeImeAdapterAndroid == 0) return false; |
| 411 // Can't send the deletion key code yet because it will delete an extra char at the end. | 470 int keyCode = beforeLength > 0 && afterLength == 0 ? |
| 412 // Also the deleteSurroundingText message is not always ordered properly with key event | 471 KeyEvent.KEYCODE_DEL : 0; |
| 413 // messages yet. | 472 long timeStampMs = SystemClock.uptimeMillis(); |
| 414 // TODO(guohui): fix the ordering and send the deletion key code for sin gle-char deletion. | 473 mLastSyntheticKeyCode = keyCode; |
| 415 sendSyntheticKeyEvent( | 474 |
| 416 sEventTypeRawKeyDown, SystemClock.uptimeMillis(), KeyEvent.KEYCO DE_UNKNOWN, 0); | 475 // TODO(guohui): The ordering of the following three events is not maint ained with the |
| 476 // "delete" message typically arriving before the other two (which do ar rive in order). | |
| 477 // It is possibly due to the processing of Key messages to begin/end oth er operations. | |
| 478 if (keyCode != 0) | |
| 479 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown, | |
|
guohui
2014/07/11 15:20:09
please see review comments in https://codereview.c
guohui
2014/07/11 19:00:37
just tried your patch locally, it suffered the sam
| |
| 480 timeStampMs, keyCode, 0); | |
| 417 nativeDeleteSurroundingText(mNativeImeAdapterAndroid, beforeLength, afte rLength); | 481 nativeDeleteSurroundingText(mNativeImeAdapterAndroid, beforeLength, afte rLength); |
| 418 sendSyntheticKeyEvent( | 482 if (keyCode != 0) |
| 419 sEventTypeKeyUp, SystemClock.uptimeMillis(), KeyEvent.KEYCODE_UN KNOWN, 0); | 483 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p, |
| 484 timeStampMs, keyCode, 0); | |
| 420 return true; | 485 return true; |
| 421 } | 486 } |
| 422 | 487 |
| 423 /** | 488 /** |
| 424 * Send a request to the native counterpart to set the selection to given ra nge. | 489 * Send a request to the native counterpart to set the selection to given ra nge. |
| 425 * @param start Selection start index. | 490 * @param start Selection start index. |
| 426 * @param end Selection end index. | 491 * @param end Selection end index. |
| 427 * @return Whether the native counterpart of ImeAdapter received the call. | 492 * @return Whether the native counterpart of ImeAdapter received the call. |
| 428 */ | 493 */ |
| 429 boolean setEditableSelectionOffsets(int start, int end) { | 494 boolean setEditableSelectionOffsets(int start, int end) { |
| 430 if (mNativeImeAdapterAndroid == 0) return false; | 495 if (mNativeImeAdapterAndroid == 0) return false; |
| 431 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); | 496 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); |
| 432 return true; | 497 return true; |
| 433 } | 498 } |
| 434 | 499 |
| 435 /** | 500 /** |
| 436 * Send a request to the native counterpart to set compositing region to giv en indices. | 501 * Send a request to the native counterpart to set composing region to given indices. |
| 437 * @param start The start of the composition. | 502 * @param start The start of the composition. |
| 438 * @param end The end of the composition. | 503 * @param end The end of the composition. |
| 439 * @return Whether the native counterpart of ImeAdapter received the call. | 504 * @return Whether the native counterpart of ImeAdapter received the call. |
| 440 */ | 505 */ |
| 441 boolean setComposingRegion(int start, int end) { | 506 boolean setComposingRegion(CharSequence text, int start, int end) { |
| 442 if (mNativeImeAdapterAndroid == 0) return false; | 507 if (mNativeImeAdapterAndroid == 0) return false; |
| 443 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); | 508 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); |
| 509 mLastComposeText = text != null ? text.toString() : null; | |
| 444 return true; | 510 return true; |
| 445 } | 511 } |
| 446 | 512 |
| 447 /** | 513 /** |
| 448 * Send a request to the native counterpart to unselect text. | 514 * Send a request to the native counterpart to unselect text. |
| 449 * @return Whether the native counterpart of ImeAdapter received the call. | 515 * @return Whether the native counterpart of ImeAdapter received the call. |
| 450 */ | 516 */ |
| 451 public boolean unselect() { | 517 public boolean unselect() { |
| 452 if (mNativeImeAdapterAndroid == 0) return false; | 518 if (mNativeImeAdapterAndroid == 0) return false; |
| 453 nativeUnselect(mNativeImeAdapterAndroid); | 519 nativeUnselect(mNativeImeAdapterAndroid); |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 547 } else if (span instanceof UnderlineSpan) { | 613 } else if (span instanceof UnderlineSpan) { |
| 548 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), | 614 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), |
| 549 spannableString.getSpanEnd(span)); | 615 spannableString.getSpanEnd(span)); |
| 550 } | 616 } |
| 551 } | 617 } |
| 552 } | 618 } |
| 553 | 619 |
| 554 @CalledByNative | 620 @CalledByNative |
| 555 private void cancelComposition() { | 621 private void cancelComposition() { |
| 556 if (mInputConnection != null) mInputConnection.restartInput(); | 622 if (mInputConnection != null) mInputConnection.restartInput(); |
| 623 mLastComposeText = null; | |
| 557 } | 624 } |
| 558 | 625 |
| 559 @CalledByNative | 626 @CalledByNative |
| 560 void detach() { | 627 void detach() { |
| 561 if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput); | 628 if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput); |
| 562 mNativeImeAdapterAndroid = 0; | 629 mNativeImeAdapterAndroid = 0; |
| 563 mTextInputType = 0; | 630 mTextInputType = 0; |
| 564 } | 631 } |
| 565 | 632 |
| 566 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, | 633 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 592 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , | 659 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , |
| 593 int before, int after); | 660 int before, int after); |
| 594 | 661 |
| 595 private native void nativeUnselect(long nativeImeAdapterAndroid); | 662 private native void nativeUnselect(long nativeImeAdapterAndroid); |
| 596 private native void nativeSelectAll(long nativeImeAdapterAndroid); | 663 private native void nativeSelectAll(long nativeImeAdapterAndroid); |
| 597 private native void nativeCut(long nativeImeAdapterAndroid); | 664 private native void nativeCut(long nativeImeAdapterAndroid); |
| 598 private native void nativeCopy(long nativeImeAdapterAndroid); | 665 private native void nativeCopy(long nativeImeAdapterAndroid); |
| 599 private native void nativePaste(long nativeImeAdapterAndroid); | 666 private native void nativePaste(long nativeImeAdapterAndroid); |
| 600 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); | 667 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); |
| 601 } | 668 } |
| OLD | NEW |