Chromium Code Reviews| Index: content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java |
| diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java |
| index 73735e0cdd4183676469dc4f4eb327f4af1d42d0..470a0dae53340ec06af5910bf620485c122fcad6 100644 |
| --- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java |
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java |
| @@ -116,6 +116,7 @@ public class ImeAdapter { |
| static int sModifierCtrl; |
| static int sModifierCapsLockOn; |
| static int sModifierNumLockOn; |
| + static KeyCharacterMap sKeyCharacterMap; |
| private long mNativeImeAdapterAndroid; |
| private InputMethodManagerWrapper mInputMethodManagerWrapper; |
| @@ -124,6 +125,10 @@ public class ImeAdapter { |
| private final Handler mHandler; |
| private DelayedDismissInput mDismissInput = null; |
| private int mTextInputType; |
| + private String mLastComposeText; |
| + |
| + @VisibleForTesting |
| + int mLastSyntheticKeyCode; |
| @VisibleForTesting |
| boolean mIsShowWithoutHideOutstanding = false; |
| @@ -317,6 +322,42 @@ public class ImeAdapter { |
| else return COMPOSITION_KEY_CODE; |
| } |
| + private static int androidKeyCodeForCharacter(char chr) { |
| + if (sKeyCharacterMap == null) { |
| + sKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); |
| + } |
| + char[] chars = new char[1]; |
| + chars[0] = chr; |
| + KeyEvent[] events = sKeyCharacterMap.getEvents(chars); |
| + return events[0].getKeyCode(); |
| + } |
| + |
| + @VisibleForTesting |
| + public static int getTypedKeycodeGuess(String oldtext, String newtext) { |
| + // Starting typing a new composition should add only a single character. Any composition |
| + // beginning with text longer than that must come from something other than typing so |
| + // return 0. |
| + if (oldtext == null) { |
| + if (newtext.length() == 1) { |
| + return androidKeyCodeForCharacter(newtext.charAt(0)); |
| + } else { |
| + return 0; |
| + } |
| + } |
| + |
| + // The content has grown in length: assume the last character is the key that caused it. |
| + if (newtext.length() > oldtext.length() && newtext.startsWith(oldtext)) |
| + return androidKeyCodeForCharacter(newtext.charAt(newtext.length() - 1)); |
| + |
| + // The content has shrunk in length: assume that backspace was pressed. |
| + if (oldtext.length() > newtext.length() && oldtext.startsWith(newtext)) |
| + return KeyEvent.KEYCODE_DEL; |
| + |
| + // The content is unchanged or has undergone a complex change (i.e. not a simple tail |
| + // modification) so return an unknown key-code. |
| + return 0; |
| + } |
| + |
| void sendKeyEventWithKeyCode(int keyCode, int flags) { |
| long eventTime = SystemClock.uptimeMillis(); |
| translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, |
| @@ -346,15 +387,22 @@ public class ImeAdapter { |
| sendKeyEventWithKeyCode(keyCode, |
| KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE); |
| } else { |
| - nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawKeyDown, |
| - timeStampMs, keyCode, 0); |
| + keyCode = getTypedKeycodeGuess(mLastComposeText, textStr); |
| + mLastComposeText = textStr; |
| + mLastSyntheticKeyCode = keyCode; |
| + |
| + if (keyCode != 0) |
| + nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawKeyDown, |
| + timeStampMs, keyCode, 0); |
| if (isCommit) { |
| nativeCommitText(mNativeImeAdapterAndroid, textStr); |
| + mLastComposeText = null; |
| } else { |
| nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition); |
| } |
| - nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyUp, |
| - timeStampMs, keyCode, 0); |
| + if (keyCode != 0) |
| + nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyUp, |
| + timeStampMs, keyCode, 0); |
| } |
| return true; |
| @@ -362,7 +410,18 @@ public class ImeAdapter { |
| void finishComposingText() { |
| if (mNativeImeAdapterAndroid == 0) return; |
| + /*int keyCode = mLastComposeText != null && mLastComposeText.length() == 1 ? |
| + KeyEvent.KEYCODE_DEL : 0; |
| + long timeStampMs = SystemClock.uptimeMillis(); |
| + mLastComposeText = null; |
| + |
| + if (keyCode != 0) |
| + nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawKeyDown, |
| + timeStampMs, keyCode, 0);*/ |
| nativeFinishComposingText(mNativeImeAdapterAndroid); |
| + /*if (keyCode != 0) |
| + nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyUp, |
| + timeStampMs, keyCode, 0);*/ |
| } |
| boolean translateAndSendNativeEvents(KeyEvent event) { |
| @@ -408,15 +467,21 @@ public class ImeAdapter { |
| boolean deleteSurroundingText(int beforeLength, int afterLength) { |
| mViewEmbedder.onImeEvent(false); |
| if (mNativeImeAdapterAndroid == 0) return false; |
| - // Can't send the deletion key code yet because it will delete an extra char at the end. |
| - // Also the deleteSurroundingText message is not always ordered properly with key event |
| - // messages yet. |
| - // TODO(guohui): fix the ordering and send the deletion key code for single-char deletion. |
| - sendSyntheticKeyEvent( |
| - sEventTypeRawKeyDown, SystemClock.uptimeMillis(), KeyEvent.KEYCODE_UNKNOWN, 0); |
| + int keyCode = beforeLength > 0 && afterLength == 0 ? |
| + KeyEvent.KEYCODE_DEL : 0; |
| + long timeStampMs = SystemClock.uptimeMillis(); |
| + mLastSyntheticKeyCode = keyCode; |
| + |
| + // TODO(guohui): The ordering of the following three events is not maintained with the |
| + // "delete" message typically arriving before the other two (which do arrive in order). |
| + // It is possibly due to the processing of Key messages to begin/end other operations. |
| + if (keyCode != 0) |
| + nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawKeyDown, |
|
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
|
| + timeStampMs, keyCode, 0); |
| nativeDeleteSurroundingText(mNativeImeAdapterAndroid, beforeLength, afterLength); |
| - sendSyntheticKeyEvent( |
| - sEventTypeKeyUp, SystemClock.uptimeMillis(), KeyEvent.KEYCODE_UNKNOWN, 0); |
| + if (keyCode != 0) |
| + nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyUp, |
| + timeStampMs, keyCode, 0); |
| return true; |
| } |
| @@ -433,14 +498,15 @@ public class ImeAdapter { |
| } |
| /** |
| - * Send a request to the native counterpart to set compositing region to given indices. |
| + * Send a request to the native counterpart to set composing region to given indices. |
| * @param start The start of the composition. |
| * @param end The end of the composition. |
| * @return Whether the native counterpart of ImeAdapter received the call. |
| */ |
| - boolean setComposingRegion(int start, int end) { |
| + boolean setComposingRegion(CharSequence text, int start, int end) { |
| if (mNativeImeAdapterAndroid == 0) return false; |
| nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); |
| + mLastComposeText = text != null ? text.toString() : null; |
| return true; |
| } |
| @@ -554,6 +620,7 @@ public class ImeAdapter { |
| @CalledByNative |
| private void cancelComposition() { |
| if (mInputConnection != null) mInputConnection.restartInput(); |
| + mLastComposeText = null; |
| } |
| @CalledByNative |