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

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: added comments and fixed tests to work with Hui's change 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];
jdduke (slow) 2014/07/21 16:08:26 Can we avoid new'ing a character array here? A sta
bcwhite 2014/07/21 19:43:13 Done.
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();
363 mLastSyntheticKeyCode = keyCode;
322 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, 364 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime,
323 KeyEvent.ACTION_DOWN, keyCode, 0, 0, 365 KeyEvent.ACTION_DOWN, keyCode, 0, 0,
324 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 366 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
325 flags)); 367 flags));
326 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev entTime, 368 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev entTime,
327 KeyEvent.ACTION_UP, keyCode, 0, 0, 369 KeyEvent.ACTION_UP, keyCode, 0, 0,
328 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 370 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
329 flags)); 371 flags));
330 } 372 }
331 373
332 // Calls from Java to C++ 374 // Calls from Java to C++
333 375
334 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP osition, 376 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP osition,
335 boolean isCommit) { 377 boolean isCommit) {
336 if (mNativeImeAdapterAndroid == 0) return false; 378 if (mNativeImeAdapterAndroid == 0) return false;
337 String textStr = text.toString(); 379 String textStr = text.toString();
338 380
339 // Committing an empty string finishes the current composition. 381 // Committing an empty string finishes the current composition.
340 boolean isFinish = textStr.isEmpty(); 382 boolean isFinish = textStr.isEmpty();
341 mViewEmbedder.onImeEvent(isFinish); 383 mViewEmbedder.onImeEvent(isFinish);
342 int keyCode = shouldSendKeyEventWithKeyCode(textStr); 384 int keyCode = shouldSendKeyEventWithKeyCode(textStr);
343 long timeStampMs = SystemClock.uptimeMillis(); 385 long timeStampMs = SystemClock.uptimeMillis();
344 386
345 if (keyCode != COMPOSITION_KEY_CODE) { 387 if (keyCode != COMPOSITION_KEY_CODE) {
346 sendKeyEventWithKeyCode(keyCode, 388 sendKeyEventWithKeyCode(keyCode,
347 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ; 389 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ;
348 } else { 390 } else {
391 keyCode = getTypedKeycodeGuess(mLastComposeText, textStr);
392 mLastComposeText = textStr;
393 mLastSyntheticKeyCode = keyCode;
394
395 // When typing, there is no issue sending KeyDown and KeyUp events a round the
396 // composition event because those key events do nothing (other than call JS
397 // handlers). Tying does not cause changes outside of a KeyPress ev ent which
398 // we don't call here. However, if the key-code is a control key su ch as
399 // KEYCODE_DEL then there never is an associated KeyPress event and the KeyDown
400 // event itself causes the action. The net result below is that the Renderer calls
401 // cancelComposition() and then Android starts anew with setComposin gRegion().
402 // This stopping and restarting of composition could be a source of problems
403 // with 3rd party keyboards.
404 //
405 // An alternative is to *not* call nativeSetComposingText() in the n on-commit case
406 // below. This avoids the restart of composition described above bu t fails to send
407 // an update to the composition while in composition which, strictly speaking,
408 // does not match the spec.
409 //
410 // For now, the solution is to endure the restarting of composition and only dive
411 // into the alternate solution should there be problems in the field . --bcwhite
412
349 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown, 413 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown,
350 timeStampMs, keyCode, 0); 414 timeStampMs, keyCode, 0);
415
351 if (isCommit) { 416 if (isCommit) {
352 nativeCommitText(mNativeImeAdapterAndroid, textStr); 417 nativeCommitText(mNativeImeAdapterAndroid, textStr);
418 mLastComposeText = null;
353 } else { 419 } else {
354 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition); 420 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition);
355 } 421 }
422
356 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p, 423 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p,
357 timeStampMs, keyCode, 0); 424 timeStampMs, keyCode, 0);
358 } 425 }
359 426
360 return true; 427 return true;
361 } 428 }
362 429
363 void finishComposingText() { 430 void finishComposingText() {
364 if (mNativeImeAdapterAndroid == 0) return; 431 if (mNativeImeAdapterAndroid == 0) return;
432 /*int keyCode = mLastComposeText != null && mLastComposeText.length() == 1 ?
jdduke (slow) 2014/07/21 16:08:26 Should this commented code be deleted?
bcwhite 2014/07/21 19:43:13 Oops! Done.
433 KeyEvent.KEYCODE_DEL : 0;
434 long timeStampMs = SystemClock.uptimeMillis();
435 mLastComposeText = null;
436
437 if (keyCode != 0)
438 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown,
439 timeStampMs, keyCode, 0);*/
365 nativeFinishComposingText(mNativeImeAdapterAndroid); 440 nativeFinishComposingText(mNativeImeAdapterAndroid);
441 /*if (keyCode != 0)
442 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p,
443 timeStampMs, keyCode, 0);*/
366 } 444 }
367 445
368 boolean translateAndSendNativeEvents(KeyEvent event) { 446 boolean translateAndSendNativeEvents(KeyEvent event) {
369 if (mNativeImeAdapterAndroid == 0) return false; 447 if (mNativeImeAdapterAndroid == 0) return false;
370 448
371 int action = event.getAction(); 449 int action = event.getAction();
372 if (action != KeyEvent.ACTION_DOWN && 450 if (action != KeyEvent.ACTION_DOWN &&
373 action != KeyEvent.ACTION_UP) { 451 action != KeyEvent.ACTION_UP) {
374 // action == KeyEvent.ACTION_MULTIPLE 452 // action == KeyEvent.ACTION_MULTIPLE
375 // TODO(bulach): confirm the actual behavior. Apparently: 453 // TODO(bulach): confirm the actual behavior. Apparently:
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 * @param end Selection end index. 496 * @param end Selection end index.
419 * @return Whether the native counterpart of ImeAdapter received the call. 497 * @return Whether the native counterpart of ImeAdapter received the call.
420 */ 498 */
421 boolean setEditableSelectionOffsets(int start, int end) { 499 boolean setEditableSelectionOffsets(int start, int end) {
422 if (mNativeImeAdapterAndroid == 0) return false; 500 if (mNativeImeAdapterAndroid == 0) return false;
423 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); 501 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end);
424 return true; 502 return true;
425 } 503 }
426 504
427 /** 505 /**
428 * Send a request to the native counterpart to set compositing region to giv en indices. 506 * Send a request to the native counterpart to set composing region to given indices.
429 * @param start The start of the composition. 507 * @param start The start of the composition.
430 * @param end The end of the composition. 508 * @param end The end of the composition.
431 * @return Whether the native counterpart of ImeAdapter received the call. 509 * @return Whether the native counterpart of ImeAdapter received the call.
432 */ 510 */
433 boolean setComposingRegion(int start, int end) { 511 boolean setComposingRegion(CharSequence text, int start, int end) {
434 if (mNativeImeAdapterAndroid == 0) return false; 512 if (mNativeImeAdapterAndroid == 0) return false;
435 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); 513 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end);
514 mLastComposeText = text != null ? text.toString() : null;
436 return true; 515 return true;
437 } 516 }
438 517
439 /** 518 /**
440 * Send a request to the native counterpart to unselect text. 519 * Send a request to the native counterpart to unselect text.
441 * @return Whether the native counterpart of ImeAdapter received the call. 520 * @return Whether the native counterpart of ImeAdapter received the call.
442 */ 521 */
443 public boolean unselect() { 522 public boolean unselect() {
444 if (mNativeImeAdapterAndroid == 0) return false; 523 if (mNativeImeAdapterAndroid == 0) return false;
445 nativeUnselect(mNativeImeAdapterAndroid); 524 nativeUnselect(mNativeImeAdapterAndroid);
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
539 } else if (span instanceof UnderlineSpan) { 618 } else if (span instanceof UnderlineSpan) {
540 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), 619 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span),
541 spannableString.getSpanEnd(span)); 620 spannableString.getSpanEnd(span));
542 } 621 }
543 } 622 }
544 } 623 }
545 624
546 @CalledByNative 625 @CalledByNative
547 private void cancelComposition() { 626 private void cancelComposition() {
548 if (mInputConnection != null) mInputConnection.restartInput(); 627 if (mInputConnection != null) mInputConnection.restartInput();
628 mLastComposeText = null;
549 } 629 }
550 630
551 @CalledByNative 631 @CalledByNative
552 void detach() { 632 void detach() {
553 if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput); 633 if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput);
554 mNativeImeAdapterAndroid = 0; 634 mNativeImeAdapterAndroid = 0;
555 mTextInputType = 0; 635 mTextInputType = 0;
556 } 636 }
557 637
558 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, 638 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid,
(...skipping 25 matching lines...) Expand all
584 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , 664 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid ,
585 int before, int after); 665 int before, int after);
586 666
587 private native void nativeUnselect(long nativeImeAdapterAndroid); 667 private native void nativeUnselect(long nativeImeAdapterAndroid);
588 private native void nativeSelectAll(long nativeImeAdapterAndroid); 668 private native void nativeSelectAll(long nativeImeAdapterAndroid);
589 private native void nativeCut(long nativeImeAdapterAndroid); 669 private native void nativeCut(long nativeImeAdapterAndroid);
590 private native void nativeCopy(long nativeImeAdapterAndroid); 670 private native void nativeCopy(long nativeImeAdapterAndroid);
591 private native void nativePaste(long nativeImeAdapterAndroid); 671 private native void nativePaste(long nativeImeAdapterAndroid);
592 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); 672 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid);
593 } 673 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698