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

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

Powered by Google App Engine
This is Rietveld 408576698