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 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
117 static int sModifierCapsLockOn; | 117 static int sModifierCapsLockOn; |
118 static int sModifierNumLockOn; | 118 static int sModifierNumLockOn; |
119 | 119 |
120 private long mNativeImeAdapterAndroid; | 120 private long mNativeImeAdapterAndroid; |
121 private InputMethodManagerWrapper mInputMethodManagerWrapper; | 121 private InputMethodManagerWrapper mInputMethodManagerWrapper; |
122 private AdapterInputConnection mInputConnection; | 122 private AdapterInputConnection mInputConnection; |
123 private final ImeAdapterDelegate mViewEmbedder; | 123 private final ImeAdapterDelegate mViewEmbedder; |
124 private final Handler mHandler; | 124 private final Handler mHandler; |
125 private DelayedDismissInput mDismissInput = null; | 125 private DelayedDismissInput mDismissInput = null; |
126 private int mTextInputType; | 126 private int mTextInputType; |
127 private String mLastComposeText; | |
128 | |
129 @VisibleForTesting | |
130 int mLastComposeKeyCode; | |
127 | 131 |
128 @VisibleForTesting | 132 @VisibleForTesting |
129 boolean mIsShowWithoutHideOutstanding = false; | 133 boolean mIsShowWithoutHideOutstanding = false; |
130 | 134 |
131 /** | 135 /** |
132 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to | 136 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to |
133 * InputMethodManager. | 137 * InputMethodManager. |
134 * @param embedder The view that is used for callbacks from ImeAdapter. | 138 * @param embedder The view that is used for callbacks from ImeAdapter. |
135 */ | 139 */ |
136 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { | 140 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
310 } | 314 } |
311 | 315 |
312 private int shouldSendKeyEventWithKeyCode(String text) { | 316 private int shouldSendKeyEventWithKeyCode(String text) { |
313 if (text.length() != 1) return COMPOSITION_KEY_CODE; | 317 if (text.length() != 1) return COMPOSITION_KEY_CODE; |
314 | 318 |
315 if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER; | 319 if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER; |
316 else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB; | 320 else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB; |
317 else return COMPOSITION_KEY_CODE; | 321 else return COMPOSITION_KEY_CODE; |
318 } | 322 } |
319 | 323 |
324 private int androidKeyCodeForCharacter(char chr) { | |
325 // This is currently only called for letters due to the nature of | |
326 // composition on Android. | |
327 switch (chr) { | |
328 case 'A': | |
329 case 'a': | |
330 return KeyEvent.KEYCODE_A; | |
331 case 'B': | |
332 case 'b': | |
333 return KeyEvent.KEYCODE_B; | |
334 case 'C': | |
335 case 'c': | |
336 return KeyEvent.KEYCODE_C; | |
337 case 'D': | |
338 case 'd': | |
339 return KeyEvent.KEYCODE_D; | |
340 case 'E': | |
341 case 'e': | |
342 return KeyEvent.KEYCODE_E; | |
343 case 'F': | |
344 case 'f': | |
345 return KeyEvent.KEYCODE_F; | |
346 case 'G': | |
347 case 'g': | |
348 return KeyEvent.KEYCODE_G; | |
349 case 'H': | |
350 case 'h': | |
351 return KeyEvent.KEYCODE_H; | |
352 case 'I': | |
353 case 'i': | |
354 return KeyEvent.KEYCODE_I; | |
355 case 'J': | |
356 case 'j': | |
357 return KeyEvent.KEYCODE_J; | |
358 case 'K': | |
359 case 'k': | |
360 return KeyEvent.KEYCODE_K; | |
361 case 'L': | |
362 case 'l': | |
363 return KeyEvent.KEYCODE_L; | |
364 case 'M': | |
365 case 'm': | |
366 return KeyEvent.KEYCODE_M; | |
367 case 'N': | |
368 case 'n': | |
369 return KeyEvent.KEYCODE_N; | |
370 case 'O': | |
371 case 'o': | |
372 return KeyEvent.KEYCODE_O; | |
373 case 'P': | |
374 case 'p': | |
375 return KeyEvent.KEYCODE_P; | |
376 case 'Q': | |
377 case 'q': | |
378 return KeyEvent.KEYCODE_Q; | |
379 case 'R': | |
380 case 'r': | |
381 return KeyEvent.KEYCODE_R; | |
382 case 'S': | |
383 case 's': | |
384 return KeyEvent.KEYCODE_S; | |
385 case 'T': | |
386 case 't': | |
387 return KeyEvent.KEYCODE_T; | |
388 case 'U': | |
389 case 'u': | |
390 return KeyEvent.KEYCODE_U; | |
391 case 'V': | |
392 case 'v': | |
393 return KeyEvent.KEYCODE_V; | |
394 case 'W': | |
395 case 'w': | |
396 return KeyEvent.KEYCODE_W; | |
397 case 'X': | |
398 case 'x': | |
399 return KeyEvent.KEYCODE_X; | |
400 case 'Y': | |
401 case 'y': | |
402 return KeyEvent.KEYCODE_Y; | |
403 case 'Z': | |
404 case 'z': | |
405 return KeyEvent.KEYCODE_Z; | |
406 | |
407 default: | |
408 return 0; | |
409 } | |
410 } | |
411 | |
320 void sendKeyEventWithKeyCode(int keyCode, int flags) { | 412 void sendKeyEventWithKeyCode(int keyCode, int flags) { |
321 long eventTime = SystemClock.uptimeMillis(); | 413 long eventTime = SystemClock.uptimeMillis(); |
322 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, | 414 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, |
323 KeyEvent.ACTION_DOWN, keyCode, 0, 0, | 415 KeyEvent.ACTION_DOWN, keyCode, 0, 0, |
324 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 416 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
325 flags)); | 417 flags)); |
326 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev entTime, | 418 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev entTime, |
327 KeyEvent.ACTION_UP, keyCode, 0, 0, | 419 KeyEvent.ACTION_UP, keyCode, 0, 0, |
328 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 420 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
329 flags)); | 421 flags)); |
330 } | 422 } |
331 | 423 |
332 // Calls from Java to C++ | 424 // Calls from Java to C++ |
333 | 425 |
334 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP osition, | 426 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP osition, |
335 boolean isCommit) { | 427 boolean isCommit) { |
336 if (mNativeImeAdapterAndroid == 0) return false; | 428 if (mNativeImeAdapterAndroid == 0) return false; |
337 String textStr = text.toString(); | 429 String textStr = text.toString(); |
338 | 430 |
339 // Committing an empty string finishes the current composition. | 431 // Committing an empty string finishes the current composition. |
340 boolean isFinish = textStr.isEmpty(); | 432 boolean isFinish = textStr.isEmpty(); |
341 mViewEmbedder.onImeEvent(isFinish); | 433 mViewEmbedder.onImeEvent(isFinish); |
342 int keyCode = shouldSendKeyEventWithKeyCode(textStr); | 434 int keyCode = shouldSendKeyEventWithKeyCode(textStr); |
343 long timeStampMs = SystemClock.uptimeMillis(); | 435 long timeStampMs = SystemClock.uptimeMillis(); |
344 | 436 |
345 if (keyCode != COMPOSITION_KEY_CODE) { | 437 if (keyCode != COMPOSITION_KEY_CODE) { |
346 sendKeyEventWithKeyCode(keyCode, | 438 sendKeyEventWithKeyCode(keyCode, |
347 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ; | 439 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ; |
348 } else { | 440 } else { |
441 if (mLastComposeText == null) { | |
442 if (textStr.length() == 1) | |
443 keyCode = androidKeyCodeForCharacter(textStr.charAt(0)); | |
aurimas (slooooooooow)
2014/07/07 17:05:20
Android already has tools to convert characters to
bcwhite
2014/07/08 18:56:03
A little convoluted what with creating the event a
jdduke (slow)
2014/07/08 19:53:54
Hmm, should we be using that function even though
| |
444 } else { | |
445 if (textStr.length() == mLastComposeText.length() + 1) | |
446 keyCode = androidKeyCodeForCharacter(textStr.charAt(textStr. length() - 1)); | |
447 else if (textStr.length() == mLastComposeText.length() - 1) | |
448 keyCode = KeyEvent.KEYCODE_DEL; | |
449 } | |
guohui
2014/07/04 21:56:13
we can get more accurate results if you check both
| |
450 mLastComposeText = textStr; | |
451 mLastComposeKeyCode = keyCode; | |
452 | |
349 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown, | 453 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown, |
350 timeStampMs, keyCode, 0); | 454 timeStampMs, keyCode, 0); |
351 if (isCommit) { | 455 if (isCommit) { |
352 nativeCommitText(mNativeImeAdapterAndroid, textStr); | 456 nativeCommitText(mNativeImeAdapterAndroid, textStr); |
353 } else { | 457 } else { |
354 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition); | 458 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition); |
355 } | 459 } |
356 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p, | 460 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p, |
357 timeStampMs, keyCode, 0); | 461 timeStampMs, keyCode, 0); |
358 } | 462 } |
359 | 463 |
360 return true; | 464 return true; |
361 } | 465 } |
362 | 466 |
363 void finishComposingText() { | 467 void finishComposingText() { |
364 if (mNativeImeAdapterAndroid == 0) return; | 468 if (mNativeImeAdapterAndroid == 0) return; |
469 mLastComposeText = null; | |
365 nativeFinishComposingText(mNativeImeAdapterAndroid); | 470 nativeFinishComposingText(mNativeImeAdapterAndroid); |
366 } | 471 } |
367 | 472 |
368 boolean translateAndSendNativeEvents(KeyEvent event) { | 473 boolean translateAndSendNativeEvents(KeyEvent event) { |
369 if (mNativeImeAdapterAndroid == 0) return false; | 474 if (mNativeImeAdapterAndroid == 0) return false; |
370 | 475 |
371 int action = event.getAction(); | 476 int action = event.getAction(); |
372 if (action != KeyEvent.ACTION_DOWN && | 477 if (action != KeyEvent.ACTION_DOWN && |
373 action != KeyEvent.ACTION_UP) { | 478 action != KeyEvent.ACTION_UP) { |
374 // action == KeyEvent.ACTION_MULTIPLE | 479 // action == KeyEvent.ACTION_MULTIPLE |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
546 } else if (span instanceof UnderlineSpan) { | 651 } else if (span instanceof UnderlineSpan) { |
547 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), | 652 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), |
548 spannableString.getSpanEnd(span)); | 653 spannableString.getSpanEnd(span)); |
549 } | 654 } |
550 } | 655 } |
551 } | 656 } |
552 | 657 |
553 @CalledByNative | 658 @CalledByNative |
554 private void cancelComposition() { | 659 private void cancelComposition() { |
555 if (mInputConnection != null) mInputConnection.restartInput(); | 660 if (mInputConnection != null) mInputConnection.restartInput(); |
661 mLastComposeText = null; | |
556 } | 662 } |
557 | 663 |
558 @CalledByNative | 664 @CalledByNative |
559 void detach() { | 665 void detach() { |
560 if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput); | 666 if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput); |
561 mNativeImeAdapterAndroid = 0; | 667 mNativeImeAdapterAndroid = 0; |
562 mTextInputType = 0; | 668 mTextInputType = 0; |
563 } | 669 } |
564 | 670 |
565 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, | 671 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, |
(...skipping 25 matching lines...) Expand all Loading... | |
591 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , | 697 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , |
592 int before, int after); | 698 int before, int after); |
593 | 699 |
594 private native void nativeUnselect(long nativeImeAdapterAndroid); | 700 private native void nativeUnselect(long nativeImeAdapterAndroid); |
595 private native void nativeSelectAll(long nativeImeAdapterAndroid); | 701 private native void nativeSelectAll(long nativeImeAdapterAndroid); |
596 private native void nativeCut(long nativeImeAdapterAndroid); | 702 private native void nativeCut(long nativeImeAdapterAndroid); |
597 private native void nativeCopy(long nativeImeAdapterAndroid); | 703 private native void nativeCopy(long nativeImeAdapterAndroid); |
598 private native void nativePaste(long nativeImeAdapterAndroid); | 704 private native void nativePaste(long nativeImeAdapterAndroid); |
599 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); | 705 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); |
600 } | 706 } |
OLD | NEW |