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 f5150578fa24e2d05785fc59fd92a097711df202..1f0beec67bacc1646fde3982711282b7c80050e2 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,8 +322,45 @@ 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]; |
|
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.
|
| + 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(); |
| + mLastSyntheticKeyCode = keyCode; |
| translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, |
| KeyEvent.ACTION_DOWN, keyCode, 0, 0, |
| KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| @@ -346,13 +388,38 @@ public class ImeAdapter { |
| sendKeyEventWithKeyCode(keyCode, |
| KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE); |
| } else { |
| + keyCode = getTypedKeycodeGuess(mLastComposeText, textStr); |
| + mLastComposeText = textStr; |
| + mLastSyntheticKeyCode = keyCode; |
| + |
| + // When typing, there is no issue sending KeyDown and KeyUp events around the |
| + // composition event because those key events do nothing (other than call JS |
| + // handlers). Tying does not cause changes outside of a KeyPress event which |
| + // we don't call here. However, if the key-code is a control key such as |
| + // KEYCODE_DEL then there never is an associated KeyPress event and the KeyDown |
| + // event itself causes the action. The net result below is that the Renderer calls |
| + // cancelComposition() and then Android starts anew with setComposingRegion(). |
| + // This stopping and restarting of composition could be a source of problems |
| + // with 3rd party keyboards. |
| + // |
| + // An alternative is to *not* call nativeSetComposingText() in the non-commit case |
| + // below. This avoids the restart of composition described above but fails to send |
| + // an update to the composition while in composition which, strictly speaking, |
| + // does not match the spec. |
| + // |
| + // For now, the solution is to endure the restarting of composition and only dive |
| + // into the alternate solution should there be problems in the field. --bcwhite |
| + |
| 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); |
| } |
| @@ -362,7 +429,18 @@ public class ImeAdapter { |
| void finishComposingText() { |
| if (mNativeImeAdapterAndroid == 0) return; |
| + /*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.
|
| + 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) { |
| @@ -425,14 +503,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; |
| } |
| @@ -546,6 +625,7 @@ public class ImeAdapter { |
| @CalledByNative |
| private void cancelComposition() { |
| if (mInputConnection != null) mInputConnection.restartInput(); |
| + mLastComposeText = null; |
| } |
| @CalledByNative |