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

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: address review comments 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 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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698