Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java

Issue 373523002: Send correct key-codes when doing composition events instead of always 0. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebased Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698