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 char[] sSingleCharArray = new char[1]; | |
| 120 static KeyCharacterMap sKeyCharacterMap; | |
| 119 | 121 |
| 120 private long mNativeImeAdapterAndroid; | 122 private long mNativeImeAdapterAndroid; |
| 121 private InputMethodManagerWrapper mInputMethodManagerWrapper; | 123 private InputMethodManagerWrapper mInputMethodManagerWrapper; |
| 122 private AdapterInputConnection mInputConnection; | 124 private AdapterInputConnection mInputConnection; |
| 123 private final ImeAdapterDelegate mViewEmbedder; | 125 private final ImeAdapterDelegate mViewEmbedder; |
| 124 private final Handler mHandler; | 126 private final Handler mHandler; |
| 125 private DelayedDismissInput mDismissInput = null; | 127 private DelayedDismissInput mDismissInput = null; |
| 126 private int mTextInputType; | 128 private int mTextInputType; |
| 129 private String mLastComposeText; | |
| 130 | |
| 131 @VisibleForTesting | |
| 132 int mLastSyntheticKeyCode; | |
| 127 | 133 |
| 128 @VisibleForTesting | 134 @VisibleForTesting |
| 129 boolean mIsShowWithoutHideOutstanding = false; | 135 boolean mIsShowWithoutHideOutstanding = false; |
| 130 | 136 |
| 131 /** | 137 /** |
| 132 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to | 138 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to |
| 133 * InputMethodManager. | 139 * InputMethodManager. |
| 134 * @param embedder The view that is used for callbacks from ImeAdapter. | 140 * @param embedder The view that is used for callbacks from ImeAdapter. |
| 135 */ | 141 */ |
| 136 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { | 142 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 310 } | 316 } |
| 311 | 317 |
| 312 private int shouldSendKeyEventWithKeyCode(String text) { | 318 private int shouldSendKeyEventWithKeyCode(String text) { |
| 313 if (text.length() != 1) return COMPOSITION_KEY_CODE; | 319 if (text.length() != 1) return COMPOSITION_KEY_CODE; |
| 314 | 320 |
| 315 if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER; | 321 if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER; |
| 316 else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB; | 322 else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB; |
| 317 else return COMPOSITION_KEY_CODE; | 323 else return COMPOSITION_KEY_CODE; |
| 318 } | 324 } |
| 319 | 325 |
| 326 private static int androidKeyCodeForCharacter(char chr) { | |
| 327 if (sKeyCharacterMap == null) { | |
| 328 sKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYB OARD); | |
| 329 } | |
| 330 sSingleCharArray[0] = chr; | |
| 331 KeyEvent[] events = sKeyCharacterMap.getEvents(sSingleCharArray); | |
| 332 if (events == null || events.length != 2) // One key-down event and one key-up event. | |
| 333 return KeyEvent.KEYCODE_UNKNOWN; | |
| 334 return events[0].getKeyCode(); | |
| 335 } | |
| 336 | |
| 337 @VisibleForTesting | |
| 338 public static int getTypedKeycodeGuess(String oldtext, String newtext) { | |
|
aurimas (slooooooooow)
2014/07/22 16:25:43
Can you write a javadoc style comment for this met
bcwhite
2014/07/22 17:28:33
Done.
| |
| 339 // Starting typing a new composition should add only a single character. Any composition | |
| 340 // beginning with text longer than that must come from something other t han typing so | |
| 341 // return 0. | |
| 342 if (oldtext == null) { | |
| 343 if (newtext.length() == 1) { | |
| 344 return androidKeyCodeForCharacter(newtext.charAt(0)); | |
| 345 } else { | |
| 346 return 0; | |
| 347 } | |
| 348 } | |
| 349 | |
| 350 // The content has grown in length: assume the last character is the key that caused it. | |
| 351 if (newtext.length() > oldtext.length() && newtext.startsWith(oldtext)) | |
| 352 return androidKeyCodeForCharacter(newtext.charAt(newtext.length() - 1)); | |
| 353 | |
| 354 // The content has shrunk in length: assume that backspace was pressed. | |
| 355 if (oldtext.length() > newtext.length() && oldtext.startsWith(newtext)) | |
| 356 return KeyEvent.KEYCODE_DEL; | |
| 357 | |
| 358 // The content is unchanged or has undergone a complex change (i.e. not a simple tail | |
| 359 // modification) so return an unknown key-code. | |
| 360 return 0; | |
| 361 } | |
| 362 | |
| 320 void sendKeyEventWithKeyCode(int keyCode, int flags) { | 363 void sendKeyEventWithKeyCode(int keyCode, int flags) { |
| 321 long eventTime = SystemClock.uptimeMillis(); | 364 long eventTime = SystemClock.uptimeMillis(); |
| 365 mLastSyntheticKeyCode = keyCode; | |
| 322 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, | 366 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, |
| 323 KeyEvent.ACTION_DOWN, keyCode, 0, 0, | 367 KeyEvent.ACTION_DOWN, keyCode, 0, 0, |
| 324 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 368 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| 325 flags)); | 369 flags)); |
| 326 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev entTime, | 370 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev entTime, |
| 327 KeyEvent.ACTION_UP, keyCode, 0, 0, | 371 KeyEvent.ACTION_UP, keyCode, 0, 0, |
| 328 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 372 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| 329 flags)); | 373 flags)); |
| 330 } | 374 } |
| 331 | 375 |
| 332 // Calls from Java to C++ | 376 // Calls from Java to C++ |
| 333 | 377 |
| 334 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP osition, | 378 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP osition, |
| 335 boolean isCommit) { | 379 boolean isCommit) { |
| 336 if (mNativeImeAdapterAndroid == 0) return false; | 380 if (mNativeImeAdapterAndroid == 0) return false; |
| 337 String textStr = text.toString(); | 381 String textStr = text.toString(); |
| 338 | 382 |
| 339 // Committing an empty string finishes the current composition. | 383 // Committing an empty string finishes the current composition. |
| 340 boolean isFinish = textStr.isEmpty(); | 384 boolean isFinish = textStr.isEmpty(); |
| 341 mViewEmbedder.onImeEvent(isFinish); | 385 mViewEmbedder.onImeEvent(isFinish); |
| 342 int keyCode = shouldSendKeyEventWithKeyCode(textStr); | 386 int keyCode = shouldSendKeyEventWithKeyCode(textStr); |
| 343 long timeStampMs = SystemClock.uptimeMillis(); | 387 long timeStampMs = SystemClock.uptimeMillis(); |
| 344 | 388 |
| 345 if (keyCode != COMPOSITION_KEY_CODE) { | 389 if (keyCode != COMPOSITION_KEY_CODE) { |
| 346 sendKeyEventWithKeyCode(keyCode, | 390 sendKeyEventWithKeyCode(keyCode, |
| 347 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ; | 391 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ; |
| 348 } else { | 392 } else { |
| 393 keyCode = getTypedKeycodeGuess(mLastComposeText, textStr); | |
| 394 mLastComposeText = textStr; | |
| 395 mLastSyntheticKeyCode = keyCode; | |
| 396 | |
| 397 // When typing, there is no issue sending KeyDown and KeyUp events a round the | |
| 398 // composition event because those key events do nothing (other than call JS | |
| 399 // handlers). Typing does not cause changes outside of a KeyPress e vent which | |
| 400 // we don't call here. However, if the key-code is a control key su ch as | |
| 401 // KEYCODE_DEL then there never is an associated KeyPress event and the KeyDown | |
| 402 // event itself causes the action. The net result below is that the Renderer calls | |
| 403 // cancelComposition() and then Android starts anew with setComposin gRegion(). | |
| 404 // This stopping and restarting of composition could be a source of problems | |
| 405 // with 3rd party keyboards. | |
| 406 // | |
| 407 // An alternative is to *not* call nativeSetComposingText() in the n on-commit case | |
| 408 // below. This avoids the restart of composition described above bu t fails to send | |
| 409 // an update to the composition while in composition which, strictly speaking, | |
| 410 // does not match the spec. | |
| 411 // | |
| 412 // For now, the solution is to endure the restarting of composition and only dive | |
| 413 // into the alternate solution should there be problems in the field . --bcwhite | |
| 414 | |
| 349 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown, | 415 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown, |
| 350 timeStampMs, keyCode, 0); | 416 timeStampMs, keyCode, 0); |
| 417 | |
| 351 if (isCommit) { | 418 if (isCommit) { |
| 352 nativeCommitText(mNativeImeAdapterAndroid, textStr); | 419 nativeCommitText(mNativeImeAdapterAndroid, textStr); |
| 420 mLastComposeText = null; | |
| 353 } else { | 421 } else { |
| 354 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition); | 422 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition); |
| 355 } | 423 } |
| 424 | |
| 356 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p, | 425 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p, |
| 357 timeStampMs, keyCode, 0); | 426 timeStampMs, keyCode, 0); |
| 358 } | 427 } |
| 359 | 428 |
| 360 return true; | 429 return true; |
| 361 } | 430 } |
| 362 | 431 |
| 363 void finishComposingText() { | 432 void finishComposingText() { |
| 364 if (mNativeImeAdapterAndroid == 0) return; | 433 if (mNativeImeAdapterAndroid == 0) return; |
| 365 nativeFinishComposingText(mNativeImeAdapterAndroid); | 434 nativeFinishComposingText(mNativeImeAdapterAndroid); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 418 * @param end Selection end index. | 487 * @param end Selection end index. |
| 419 * @return Whether the native counterpart of ImeAdapter received the call. | 488 * @return Whether the native counterpart of ImeAdapter received the call. |
| 420 */ | 489 */ |
| 421 boolean setEditableSelectionOffsets(int start, int end) { | 490 boolean setEditableSelectionOffsets(int start, int end) { |
| 422 if (mNativeImeAdapterAndroid == 0) return false; | 491 if (mNativeImeAdapterAndroid == 0) return false; |
| 423 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); | 492 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); |
| 424 return true; | 493 return true; |
| 425 } | 494 } |
| 426 | 495 |
| 427 /** | 496 /** |
| 428 * Send a request to the native counterpart to set compositing region to giv en indices. | 497 * Send a request to the native counterpart to set composing region to given indices. |
| 429 * @param start The start of the composition. | 498 * @param start The start of the composition. |
| 430 * @param end The end of the composition. | 499 * @param end The end of the composition. |
| 431 * @return Whether the native counterpart of ImeAdapter received the call. | 500 * @return Whether the native counterpart of ImeAdapter received the call. |
| 432 */ | 501 */ |
| 433 boolean setComposingRegion(int start, int end) { | 502 boolean setComposingRegion(CharSequence text, int start, int end) { |
| 434 if (mNativeImeAdapterAndroid == 0) return false; | 503 if (mNativeImeAdapterAndroid == 0) return false; |
| 435 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); | 504 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); |
| 505 mLastComposeText = text != null ? text.toString() : null; | |
| 436 return true; | 506 return true; |
| 437 } | 507 } |
| 438 | 508 |
| 439 /** | 509 /** |
| 440 * Send a request to the native counterpart to unselect text. | 510 * Send a request to the native counterpart to unselect text. |
| 441 * @return Whether the native counterpart of ImeAdapter received the call. | 511 * @return Whether the native counterpart of ImeAdapter received the call. |
| 442 */ | 512 */ |
| 443 public boolean unselect() { | 513 public boolean unselect() { |
| 444 if (mNativeImeAdapterAndroid == 0) return false; | 514 if (mNativeImeAdapterAndroid == 0) return false; |
| 445 nativeUnselect(mNativeImeAdapterAndroid); | 515 nativeUnselect(mNativeImeAdapterAndroid); |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 539 } else if (span instanceof UnderlineSpan) { | 609 } else if (span instanceof UnderlineSpan) { |
| 540 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), | 610 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), |
| 541 spannableString.getSpanEnd(span)); | 611 spannableString.getSpanEnd(span)); |
| 542 } | 612 } |
| 543 } | 613 } |
| 544 } | 614 } |
| 545 | 615 |
| 546 @CalledByNative | 616 @CalledByNative |
| 547 private void cancelComposition() { | 617 private void cancelComposition() { |
| 548 if (mInputConnection != null) mInputConnection.restartInput(); | 618 if (mInputConnection != null) mInputConnection.restartInput(); |
| 619 mLastComposeText = null; | |
| 549 } | 620 } |
| 550 | 621 |
| 551 @CalledByNative | 622 @CalledByNative |
| 552 void detach() { | 623 void detach() { |
| 553 if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput); | 624 if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput); |
| 554 mNativeImeAdapterAndroid = 0; | 625 mNativeImeAdapterAndroid = 0; |
| 555 mTextInputType = 0; | 626 mTextInputType = 0; |
| 556 } | 627 } |
| 557 | 628 |
| 558 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, | 629 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 584 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , | 655 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , |
| 585 int before, int after); | 656 int before, int after); |
| 586 | 657 |
| 587 private native void nativeUnselect(long nativeImeAdapterAndroid); | 658 private native void nativeUnselect(long nativeImeAdapterAndroid); |
| 588 private native void nativeSelectAll(long nativeImeAdapterAndroid); | 659 private native void nativeSelectAll(long nativeImeAdapterAndroid); |
| 589 private native void nativeCut(long nativeImeAdapterAndroid); | 660 private native void nativeCut(long nativeImeAdapterAndroid); |
| 590 private native void nativeCopy(long nativeImeAdapterAndroid); | 661 private native void nativeCopy(long nativeImeAdapterAndroid); |
| 591 private native void nativePaste(long nativeImeAdapterAndroid); | 662 private native void nativePaste(long nativeImeAdapterAndroid); |
| 592 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); | 663 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); |
| 593 } | 664 } |
| OLD | NEW |