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 |
deleted file mode 100644 |
index b4e41b99537acdb459bf5b73df5d05e6b3596395..0000000000000000000000000000000000000000 |
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java |
+++ /dev/null |
@@ -1,546 +0,0 @@ |
-// Copyright 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-package org.chromium.content.browser.input; |
- |
-import android.os.Handler; |
-import android.os.ResultReceiver; |
-import android.view.KeyCharacterMap; |
-import android.view.KeyEvent; |
-import android.view.View; |
-import android.view.inputmethod.EditorInfo; |
- |
-import com.google.common.annotations.VisibleForTesting; |
- |
-import org.chromium.base.CalledByNative; |
-import org.chromium.base.JNINamespace; |
- |
-/** |
- * Adapts and plumbs android IME service onto the chrome text input API. |
- * ImeAdapter provides an interface in both ways native <-> java: |
- * 1. InputConnectionAdapter notifies native code of text composition state and |
- * dispatch key events from java -> WebKit. |
- * 2. Native ImeAdapter notifies java side to clear composition text. |
- * |
- * The basic flow is: |
- * 1. When InputConnectionAdapter gets called with composition or result text: |
- * If we receive a composition text or a result text, then we just need to |
- * dispatch a synthetic key event with special keycode 229, and then dispatch |
- * the composition or result text. |
- * 2. Intercept dispatchKeyEvent() method for key events not handled by IME, we |
- * need to dispatch them to webkit and check webkit's reply. Then inject a |
- * new key event for further processing if webkit didn't handle it. |
- * |
- * Note that the native peer object does not take any strong reference onto the |
- * instance of this java object, hence it is up to the client of this class (e.g. |
- * the ViewEmbedder implementor) to hold a strong reference to it for the required |
- * lifetime of the object. |
- */ |
-@JNINamespace("content") |
-public class ImeAdapter { |
- /** |
- * Interface for the delegate that needs to be notified of IME changes. |
- */ |
- public interface ImeAdapterDelegate { |
- /** |
- * @param isFinish whether the event is occurring because input is finished. |
- */ |
- void onImeEvent(boolean isFinish); |
- void onSetFieldValue(); |
- void onDismissInput(); |
- View getAttachedView(); |
- ResultReceiver getNewShowKeyboardReceiver(); |
- } |
- |
- private class DelayedDismissInput implements Runnable { |
- private final long mNativeImeAdapter; |
- |
- DelayedDismissInput(long nativeImeAdapter) { |
- mNativeImeAdapter = nativeImeAdapter; |
- } |
- |
- @Override |
- public void run() { |
- attach(mNativeImeAdapter, sTextInputTypeNone, AdapterInputConnection.INVALID_SELECTION, |
- AdapterInputConnection.INVALID_SELECTION); |
- dismissInput(true); |
- } |
- } |
- |
- private static final int COMPOSITION_KEY_CODE = 229; |
- |
- // Delay introduced to avoid hiding the keyboard if new show requests are received. |
- // The time required by the unfocus-focus events triggered by tab has been measured in soju: |
- // Mean: 18.633 ms, Standard deviation: 7.9837 ms. |
- // The value here should be higher enough to cover these cases, but not too high to avoid |
- // letting the user perceiving important delays. |
- private static final int INPUT_DISMISS_DELAY = 150; |
- |
- // All the constants that are retrieved from the C++ code. |
- // They get set through initializeWebInputEvents and initializeTextInputTypes calls. |
- static int sEventTypeRawKeyDown; |
- static int sEventTypeKeyUp; |
- static int sEventTypeChar; |
- static int sTextInputTypeNone; |
- static int sTextInputTypeText; |
- static int sTextInputTypeTextArea; |
- static int sTextInputTypePassword; |
- static int sTextInputTypeSearch; |
- static int sTextInputTypeUrl; |
- static int sTextInputTypeEmail; |
- static int sTextInputTypeTel; |
- static int sTextInputTypeNumber; |
- static int sTextInputTypeContentEditable; |
- static int sModifierShift; |
- static int sModifierAlt; |
- static int sModifierCtrl; |
- static int sModifierCapsLockOn; |
- static int sModifierNumLockOn; |
- |
- private long mNativeImeAdapterAndroid; |
- private InputMethodManagerWrapper mInputMethodManagerWrapper; |
- private AdapterInputConnection mInputConnection; |
- private final ImeAdapterDelegate mViewEmbedder; |
- private final Handler mHandler; |
- private DelayedDismissInput mDismissInput = null; |
- private int mTextInputType; |
- private int mInitialSelectionStart; |
- private int mInitialSelectionEnd; |
- |
- @VisibleForTesting |
- boolean mIsShowWithoutHideOutstanding = false; |
- |
- /** |
- * @param wrapper InputMethodManagerWrapper that should receive all the call directed to |
- * InputMethodManager. |
- * @param embedder The view that is used for callbacks from ImeAdapter. |
- */ |
- public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embedder) { |
- mInputMethodManagerWrapper = wrapper; |
- mViewEmbedder = embedder; |
- mHandler = new Handler(); |
- } |
- |
- /** |
- * Default factory for AdapterInputConnection classes. |
- */ |
- public static class AdapterInputConnectionFactory { |
- public AdapterInputConnection get(View view, ImeAdapter imeAdapter, EditorInfo outAttrs) { |
- return new AdapterInputConnection(view, imeAdapter, outAttrs); |
- } |
- } |
- |
- @VisibleForTesting |
- public void setInputMethodManagerWrapper(InputMethodManagerWrapper immw) { |
- mInputMethodManagerWrapper = immw; |
- } |
- |
- /** |
- * Should be only used by AdapterInputConnection. |
- * @return InputMethodManagerWrapper that should receive all the calls directed to |
- * InputMethodManager. |
- */ |
- InputMethodManagerWrapper getInputMethodManagerWrapper() { |
- return mInputMethodManagerWrapper; |
- } |
- |
- /** |
- * Set the current active InputConnection when a new InputConnection is constructed. |
- * @param inputConnection The input connection that is currently used with IME. |
- */ |
- void setInputConnection(AdapterInputConnection inputConnection) { |
- mInputConnection = inputConnection; |
- } |
- |
- /** |
- * Should be only used by AdapterInputConnection. |
- * @return The input type of currently focused element. |
- */ |
- int getTextInputType() { |
- return mTextInputType; |
- } |
- |
- /** |
- * Should be only used by AdapterInputConnection. |
- * @return The starting index of the initial text selection. |
- */ |
- int getInitialSelectionStart() { |
- return mInitialSelectionStart; |
- } |
- |
- /** |
- * Should be only used by AdapterInputConnection. |
- * @return The ending index of the initial text selection. |
- */ |
- int getInitialSelectionEnd() { |
- return mInitialSelectionEnd; |
- } |
- |
- public static int getTextInputTypeNone() { |
- return sTextInputTypeNone; |
- } |
- |
- private static int getModifiers(int metaState) { |
- int modifiers = 0; |
- if ((metaState & KeyEvent.META_SHIFT_ON) != 0) { |
- modifiers |= sModifierShift; |
- } |
- if ((metaState & KeyEvent.META_ALT_ON) != 0) { |
- modifiers |= sModifierAlt; |
- } |
- if ((metaState & KeyEvent.META_CTRL_ON) != 0) { |
- modifiers |= sModifierCtrl; |
- } |
- if ((metaState & KeyEvent.META_CAPS_LOCK_ON) != 0) { |
- modifiers |= sModifierCapsLockOn; |
- } |
- if ((metaState & KeyEvent.META_NUM_LOCK_ON) != 0) { |
- modifiers |= sModifierNumLockOn; |
- } |
- return modifiers; |
- } |
- |
- public boolean isActive() { |
- return mInputConnection != null && mInputConnection.isActive(); |
- } |
- |
- private boolean isFor(int nativeImeAdapter, int textInputType) { |
- return mNativeImeAdapterAndroid == nativeImeAdapter && |
- mTextInputType == textInputType; |
- } |
- |
- public void attachAndShowIfNeeded(int nativeImeAdapter, int textInputType, |
- int selectionStart, int selectionEnd, boolean showIfNeeded) { |
- mHandler.removeCallbacks(mDismissInput); |
- |
- // If current input type is none and showIfNeeded is false, IME should not be shown |
- // and input type should remain as none. |
- if (mTextInputType == sTextInputTypeNone && !showIfNeeded) { |
- return; |
- } |
- |
- if (!isFor(nativeImeAdapter, textInputType)) { |
- // Set a delayed task to perform unfocus. This avoids hiding the keyboard when tabbing |
- // through text inputs or when JS rapidly changes focus to another text element. |
- if (textInputType == sTextInputTypeNone) { |
- mDismissInput = new DelayedDismissInput(nativeImeAdapter); |
- mHandler.postDelayed(mDismissInput, INPUT_DISMISS_DELAY); |
- return; |
- } |
- |
- attach(nativeImeAdapter, textInputType, selectionStart, selectionEnd); |
- |
- mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView()); |
- if (showIfNeeded) { |
- showKeyboard(); |
- } |
- } else if (hasInputType() && showIfNeeded) { |
- showKeyboard(); |
- } |
- } |
- |
- public void attach(long nativeImeAdapter, int textInputType, int selectionStart, |
- int selectionEnd) { |
- if (mNativeImeAdapterAndroid != 0) { |
- nativeResetImeAdapter(mNativeImeAdapterAndroid); |
- } |
- mNativeImeAdapterAndroid = nativeImeAdapter; |
- mTextInputType = textInputType; |
- mInitialSelectionStart = selectionStart; |
- mInitialSelectionEnd = selectionEnd; |
- if (nativeImeAdapter != 0) { |
- nativeAttachImeAdapter(mNativeImeAdapterAndroid); |
- } |
- } |
- |
- /** |
- * Attaches the imeAdapter to its native counterpart. This is needed to start forwarding |
- * keyboard events to WebKit. |
- * @param nativeImeAdapter The pointer to the native ImeAdapter object. |
- */ |
- public void attach(long nativeImeAdapter) { |
- if (mNativeImeAdapterAndroid != 0) { |
- nativeResetImeAdapter(mNativeImeAdapterAndroid); |
- } |
- mNativeImeAdapterAndroid = nativeImeAdapter; |
- if (nativeImeAdapter != 0) { |
- nativeAttachImeAdapter(mNativeImeAdapterAndroid); |
- } |
- } |
- |
- private void showKeyboard() { |
- mIsShowWithoutHideOutstanding = true; |
- mInputMethodManagerWrapper.showSoftInput(mViewEmbedder.getAttachedView(), 0, |
- mViewEmbedder.getNewShowKeyboardReceiver()); |
- } |
- |
- private void dismissInput(boolean unzoomIfNeeded) { |
- mIsShowWithoutHideOutstanding = false; |
- View view = mViewEmbedder.getAttachedView(); |
- if (mInputMethodManagerWrapper.isActive(view)) { |
- mInputMethodManagerWrapper.hideSoftInputFromWindow(view.getWindowToken(), 0, |
- unzoomIfNeeded ? mViewEmbedder.getNewShowKeyboardReceiver() : null); |
- } |
- mViewEmbedder.onDismissInput(); |
- } |
- |
- private boolean hasInputType() { |
- return mTextInputType != sTextInputTypeNone; |
- } |
- |
- private static boolean isTextInputType(int type) { |
- return type != sTextInputTypeNone && !InputDialogContainer.isDialogInputType(type); |
- } |
- |
- public boolean hasTextInputType() { |
- return isTextInputType(mTextInputType); |
- } |
- |
- public boolean dispatchKeyEvent(KeyEvent event) { |
- return translateAndSendNativeEvents(event); |
- } |
- |
- private int shouldSendKeyEventWithKeyCode(String text) { |
- if (text.length() != 1) return COMPOSITION_KEY_CODE; |
- |
- if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER; |
- else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB; |
- else return COMPOSITION_KEY_CODE; |
- } |
- |
- void sendKeyEventWithKeyCode(int keyCode, int flags) { |
- long eventTime = System.currentTimeMillis(); |
- translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, |
- KeyEvent.ACTION_DOWN, keyCode, 0, 0, |
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
- flags)); |
- translateAndSendNativeEvents(new KeyEvent(System.currentTimeMillis(), eventTime, |
- KeyEvent.ACTION_UP, keyCode, 0, 0, |
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
- flags)); |
- } |
- |
- // Calls from Java to C++ |
- |
- boolean checkCompositionQueueAndCallNative(String text, int newCursorPosition, |
- boolean isCommit) { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- |
- // Committing an empty string finishes the current composition. |
- boolean isFinish = text.isEmpty(); |
- mViewEmbedder.onImeEvent(isFinish); |
- int keyCode = shouldSendKeyEventWithKeyCode(text); |
- long timeStampMs = System.currentTimeMillis(); |
- |
- if (keyCode != COMPOSITION_KEY_CODE) { |
- sendKeyEventWithKeyCode(keyCode, |
- KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE); |
- } else { |
- nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawKeyDown, |
- timeStampMs, keyCode, 0); |
- if (isCommit) { |
- nativeCommitText(mNativeImeAdapterAndroid, text); |
- } else { |
- nativeSetComposingText(mNativeImeAdapterAndroid, text, newCursorPosition); |
- } |
- nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyUp, |
- timeStampMs, keyCode, 0); |
- } |
- |
- return true; |
- } |
- |
- void finishComposingText() { |
- if (mNativeImeAdapterAndroid == 0) return; |
- nativeFinishComposingText(mNativeImeAdapterAndroid); |
- } |
- |
- boolean translateAndSendNativeEvents(KeyEvent event) { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- |
- int action = event.getAction(); |
- if (action != KeyEvent.ACTION_DOWN && |
- action != KeyEvent.ACTION_UP) { |
- // action == KeyEvent.ACTION_MULTIPLE |
- // TODO(bulach): confirm the actual behavior. Apparently: |
- // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a |
- // composition key down (229) followed by a commit text with the |
- // string from event.getUnicodeChars(). |
- // Otherwise, we'd need to send an event with a |
- // WebInputEvent::IsAutoRepeat modifier. We also need to verify when |
- // we receive ACTION_MULTIPLE: we may receive it after an ACTION_DOWN, |
- // and if that's the case, we'll need to review when to send the Char |
- // event. |
- return false; |
- } |
- mViewEmbedder.onImeEvent(false); |
- return nativeSendKeyEvent(mNativeImeAdapterAndroid, event, event.getAction(), |
- getModifiers(event.getMetaState()), event.getEventTime(), event.getKeyCode(), |
- event.isSystem(), event.getUnicodeChar()); |
- } |
- |
- boolean sendSyntheticKeyEvent( |
- int eventType, long timestampMs, int keyCode, int unicodeChar) { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- |
- nativeSendSyntheticKeyEvent( |
- mNativeImeAdapterAndroid, eventType, timestampMs, keyCode, unicodeChar); |
- return true; |
- } |
- |
- boolean deleteSurroundingText(int beforeLength, int afterLength) { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- nativeDeleteSurroundingText(mNativeImeAdapterAndroid, beforeLength, afterLength); |
- return true; |
- } |
- |
- boolean setEditableSelectionOffsets(int start, int end) { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); |
- return true; |
- } |
- |
- /** |
- * Send a request to the native counterpart to set compositing 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) { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); |
- return true; |
- } |
- |
- /** |
- * Send a request to the native counterpart to unselect text. |
- * @return Whether the native counterpart of ImeAdapter received the call. |
- */ |
- public boolean unselect() { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- nativeUnselect(mNativeImeAdapterAndroid); |
- return true; |
- } |
- |
- /** |
- * Send a request to the native counterpart of ImeAdapter to select all the text. |
- * @return Whether the native counterpart of ImeAdapter received the call. |
- */ |
- public boolean selectAll() { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- nativeSelectAll(mNativeImeAdapterAndroid); |
- return true; |
- } |
- |
- /** |
- * Send a request to the native counterpart of ImeAdapter to cut the selected text. |
- * @return Whether the native counterpart of ImeAdapter received the call. |
- */ |
- public boolean cut() { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- nativeCut(mNativeImeAdapterAndroid); |
- return true; |
- } |
- |
- /** |
- * Send a request to the native counterpart of ImeAdapter to copy the selected text. |
- * @return Whether the native counterpart of ImeAdapter received the call. |
- */ |
- public boolean copy() { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- nativeCopy(mNativeImeAdapterAndroid); |
- return true; |
- } |
- |
- /** |
- * Send a request to the native counterpart of ImeAdapter to paste the text from the clipboard. |
- * @return Whether the native counterpart of ImeAdapter received the call. |
- */ |
- public boolean paste() { |
- if (mNativeImeAdapterAndroid == 0) return false; |
- nativePaste(mNativeImeAdapterAndroid); |
- return true; |
- } |
- |
- // Calls from C++ to Java |
- |
- @CalledByNative |
- private static void initializeWebInputEvents(int eventTypeRawKeyDown, int eventTypeKeyUp, |
- int eventTypeChar, int modifierShift, int modifierAlt, int modifierCtrl, |
- int modifierCapsLockOn, int modifierNumLockOn) { |
- sEventTypeRawKeyDown = eventTypeRawKeyDown; |
- sEventTypeKeyUp = eventTypeKeyUp; |
- sEventTypeChar = eventTypeChar; |
- sModifierShift = modifierShift; |
- sModifierAlt = modifierAlt; |
- sModifierCtrl = modifierCtrl; |
- sModifierCapsLockOn = modifierCapsLockOn; |
- sModifierNumLockOn = modifierNumLockOn; |
- } |
- |
- @CalledByNative |
- private static void initializeTextInputTypes(int textInputTypeNone, int textInputTypeText, |
- int textInputTypeTextArea, int textInputTypePassword, int textInputTypeSearch, |
- int textInputTypeUrl, int textInputTypeEmail, int textInputTypeTel, |
- int textInputTypeNumber, int textInputTypeContentEditable) { |
- sTextInputTypeNone = textInputTypeNone; |
- sTextInputTypeText = textInputTypeText; |
- sTextInputTypeTextArea = textInputTypeTextArea; |
- sTextInputTypePassword = textInputTypePassword; |
- sTextInputTypeSearch = textInputTypeSearch; |
- sTextInputTypeUrl = textInputTypeUrl; |
- sTextInputTypeEmail = textInputTypeEmail; |
- sTextInputTypeTel = textInputTypeTel; |
- sTextInputTypeNumber = textInputTypeNumber; |
- sTextInputTypeContentEditable = textInputTypeContentEditable; |
- } |
- |
- @CalledByNative |
- private void focusedNodeChanged(boolean isEditable) { |
- if (mInputConnection != null && isEditable) mInputConnection.restartInput(); |
- } |
- |
- @CalledByNative |
- private void cancelComposition() { |
- if (mInputConnection != null) mInputConnection.restartInput(); |
- } |
- |
- @CalledByNative |
- void detach() { |
- if (mDismissInput != null) mHandler.removeCallbacks(mDismissInput); |
- mNativeImeAdapterAndroid = 0; |
- mTextInputType = 0; |
- } |
- |
- private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndroid, |
- int eventType, long timestampMs, int keyCode, int unicodeChar); |
- |
- private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyEvent event, |
- int action, int modifiers, long timestampMs, int keyCode, boolean isSystemKey, |
- int unicodeChar); |
- |
- private native void nativeSetComposingText(long nativeImeAdapterAndroid, String text, |
- int newCursorPosition); |
- |
- private native void nativeCommitText(long nativeImeAdapterAndroid, String text); |
- |
- private native void nativeFinishComposingText(long nativeImeAdapterAndroid); |
- |
- private native void nativeAttachImeAdapter(long nativeImeAdapterAndroid); |
- |
- private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterAndroid, |
- int start, int end); |
- |
- private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, int start, int end); |
- |
- private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid, |
- int before, int after); |
- |
- private native void nativeUnselect(long nativeImeAdapterAndroid); |
- private native void nativeSelectAll(long nativeImeAdapterAndroid); |
- private native void nativeCut(long nativeImeAdapterAndroid); |
- private native void nativeCopy(long nativeImeAdapterAndroid); |
- private native void nativePaste(long nativeImeAdapterAndroid); |
- private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); |
-} |