| 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/ImeAdapter.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
|
| similarity index 48%
|
| rename from content/public/android/java/src/org/chromium/content/browser/ImeAdapter.java
|
| rename to content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
|
| index 1f73502d70a17ff4705137121f602660c23553eb..1ea57b4a105d086d732c319d68876474f895155f 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/ImeAdapter.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
|
| @@ -2,22 +2,15 @@
|
| // 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;
|
| +package org.chromium.content.browser.input;
|
|
|
| import android.content.Context;
|
| import android.os.Handler;
|
| import android.os.ResultReceiver;
|
| -import android.text.Editable;
|
| -import android.text.InputType;
|
| -import android.text.Selection;
|
| -import android.util.Log;
|
| import android.view.KeyCharacterMap;
|
| import android.view.KeyEvent;
|
| import android.view.View;
|
| -import android.view.inputmethod.BaseInputConnection;
|
| import android.view.inputmethod.EditorInfo;
|
| -import android.view.inputmethod.ExtractedText;
|
| -import android.view.inputmethod.ExtractedTextRequest;
|
|
|
| import com.google.common.annotations.VisibleForTesting;
|
|
|
| @@ -46,25 +39,44 @@ import org.chromium.base.JNINamespace;
|
| * lifetime of the object.
|
| */
|
| @JNINamespace("content")
|
| -class ImeAdapter {
|
| - interface ViewEmbedder {
|
| +public class ImeAdapter {
|
| + public interface ViewEmbedder {
|
| /**
|
| * @param isFinish whether the event is occurring because input is finished.
|
| */
|
| - public void onImeEvent(boolean isFinish);
|
| - public void onSetFieldValue();
|
| - public void onDismissInput();
|
| - public View getAttachedView();
|
| - public ResultReceiver getNewShowKeyboardReceiver();
|
| + void onImeEvent(boolean isFinish);
|
| + void onSetFieldValue();
|
| + void onDismissInput();
|
| + View getAttachedView();
|
| + ResultReceiver getNewShowKeyboardReceiver();
|
| }
|
|
|
| - static final int COMPOSITION_KEY_CODE = 229;
|
| - /**
|
| - * Selection value should be -1 if not known. See EditorInfo.java for details.
|
| - */
|
| - static final int INVALID_SELECTION = -1;
|
| - static final int INVALID_COMPOSITION = -1;
|
| + private class DelayedDismissInput implements Runnable {
|
| + private final int mNativeImeAdapter;
|
| +
|
| + DelayedDismissInput(int 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;
|
| @@ -85,80 +97,29 @@ class ImeAdapter {
|
| static int sModifierCapsLockOn;
|
| static int sModifierNumLockOn;
|
|
|
| - @CalledByNative
|
| - 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
|
| - static void initializeTextInputTypes(int textInputTypeNone, int textInputTypeText,
|
| - int textInputTypeTextArea, int textInputTypePassword, int textInputTypeSearch,
|
| - int textInputTypeUrl, int textInputTypeEmail, int textInputTypeTel,
|
| - int textInputTypeNumber, int textInputTypeDate, int textInputTypeDateTime,
|
| - int textInputTypeDateTimeLocal, int textInputTypeMonth, int textInputTypeTime,
|
| - int textInputTypeWeek, int textInputTypeContentEditable) {
|
| - sTextInputTypeNone = textInputTypeNone;
|
| - sTextInputTypeText = textInputTypeText;
|
| - sTextInputTypeTextArea = textInputTypeTextArea;
|
| - sTextInputTypePassword = textInputTypePassword;
|
| - sTextInputTypeSearch = textInputTypeSearch;
|
| - sTextInputTypeUrl = textInputTypeUrl;
|
| - sTextInputTypeEmail = textInputTypeEmail;
|
| - sTextInputTypeTel = textInputTypeTel;
|
| - sTextInputTypeNumber = textInputTypeNumber;
|
| - sTextInputTypeWeek = textInputTypeWeek;
|
| - sTextInputTypeContentEditable = textInputTypeContentEditable;
|
| - }
|
| -
|
| private int mNativeImeAdapterAndroid;
|
| - private int mTextInputType;
|
| - private int mInitialSelectionStart;
|
| - private int mInitialSelectionEnd;
|
| -
|
| private final Context mContext;
|
| private InputMethodManagerWrapper mInputMethodManagerWrapper;
|
| - private final SelectionHandleController mSelectionHandleController;
|
| - private final InsertionHandleController mInsertionHandleController;
|
| private AdapterInputConnection mInputConnection;
|
| private final ViewEmbedder mViewEmbedder;
|
| private final Handler mHandler;
|
| -
|
| - private class DelayedDismissInput implements Runnable {
|
| - private final int mNativeImeAdapter;
|
| -
|
| - DelayedDismissInput(int nativeImeAdapter) {
|
| - mNativeImeAdapter = nativeImeAdapter;
|
| - }
|
| -
|
| - @Override
|
| - public void run() {
|
| - attach(mNativeImeAdapter, sTextInputTypeNone, INVALID_SELECTION, INVALID_SELECTION);
|
| - dismissInput(true);
|
| - }
|
| - }
|
| -
|
| private DelayedDismissInput mDismissInput = null;
|
| + private final SelectionHandleController mSelectionHandleController;
|
| + private final InsertionHandleController mInsertionHandleController;
|
| + private int mTextInputType;
|
| + private int mInitialSelectionStart;
|
| + private int mInitialSelectionEnd;
|
|
|
| @VisibleForTesting
|
| - protected boolean mIsShowWithoutHideOutstanding = false;
|
| + boolean mIsShowWithoutHideOutstanding = false;
|
|
|
| - // 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;
|
| -
|
| - ImeAdapter(Context context, SelectionHandleController selectionHandleController,
|
| + /**
|
| + * @param context View context.
|
| + * @param selectionHandleController The controller that handles selection.
|
| + * @param insertionHandleController The controller that handles insertion.
|
| + * @param embedder The view that is used for callbacks from ImeAdapter.
|
| + */
|
| + public ImeAdapter(Context context, SelectionHandleController selectionHandleController,
|
| InsertionHandleController insertionHandleController, ViewEmbedder embedder) {
|
| mContext = context;
|
| mInputMethodManagerWrapper = new InputMethodManagerWrapper(context);
|
| @@ -168,9 +129,11 @@ class ImeAdapter {
|
| mHandler = new Handler();
|
| }
|
|
|
| - boolean isFor(int nativeImeAdapter, int textInputType) {
|
| - return mNativeImeAdapterAndroid == nativeImeAdapter &&
|
| - mTextInputType == textInputType;
|
| + public static class AdapterInputConnectionFactory {
|
| + public AdapterInputConnection get(View view, ImeAdapter imeAdapter,
|
| + EditorInfo outAttrs) {
|
| + return new AdapterInputConnection(view, imeAdapter, outAttrs);
|
| + }
|
| }
|
|
|
| @VisibleForTesting
|
| @@ -178,11 +141,86 @@ class ImeAdapter {
|
| mInputMethodManagerWrapper = immw;
|
| }
|
|
|
| - private InputMethodManagerWrapper getInputMethodManagerWrapper() {
|
| + /**
|
| + * Should be only used by AdapterInputConnection.
|
| + * @return InputMethodManagerWrapper that should receive all the calls directed to
|
| + * InputMethodManager.
|
| + */
|
| + InputMethodManagerWrapper getInputMethodManagerWrapper() {
|
| return mInputMethodManagerWrapper;
|
| }
|
|
|
| - void attachAndShowIfNeeded(int nativeImeAdapter, int textInputType,
|
| + /**
|
| + * 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;
|
| + }
|
| +
|
| + void hideSelectionAndInsertionHandleControllers() {
|
| + mSelectionHandleController.hideAndDisallowAutomaticShowing();
|
| + mInsertionHandleController.hideAndDisallowAutomaticShowing();
|
| + }
|
| +
|
| + 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);
|
|
|
| @@ -213,7 +251,8 @@ class ImeAdapter {
|
| }
|
| }
|
|
|
| - void attach(int nativeImeAdapter, int textInputType, int selectionStart, int selectionEnd) {
|
| + public void attach(int nativeImeAdapter, int textInputType, int selectionStart,
|
| + int selectionEnd) {
|
| mNativeImeAdapterAndroid = nativeImeAdapter;
|
| mTextInputType = textInputType;
|
| mInitialSelectionStart = selectionStart;
|
| @@ -226,7 +265,7 @@ class ImeAdapter {
|
| * keyboard events to WebKit.
|
| * @param nativeImeAdapter The pointer to the native ImeAdapter object.
|
| */
|
| - void attach(int nativeImeAdapter) {
|
| + public void attach(int nativeImeAdapter) {
|
| mNativeImeAdapterAndroid = nativeImeAdapter;
|
| if (nativeImeAdapter != 0) {
|
| nativeAttachImeAdapter(mNativeImeAdapterAndroid);
|
| @@ -237,7 +276,7 @@ class ImeAdapter {
|
| * Used to check whether the native counterpart of the ImeAdapter has been attached yet.
|
| * @return Whether native ImeAdapter has been attached and its pointer is currently nonzero.
|
| */
|
| - boolean isNativeImeAdapterAttached() {
|
| + public boolean isNativeImeAdapterAttached() {
|
| return mNativeImeAdapterAndroid != 0;
|
| }
|
|
|
| @@ -261,13 +300,7 @@ class ImeAdapter {
|
| }
|
| }
|
|
|
| - @CalledByNative
|
| - void detach() {
|
| - mNativeImeAdapterAndroid = 0;
|
| - mTextInputType = 0;
|
| - }
|
| -
|
| - boolean hasInputType() {
|
| + private boolean hasInputType() {
|
| return mTextInputType != sTextInputTypeNone;
|
| }
|
|
|
| @@ -275,35 +308,41 @@ class ImeAdapter {
|
| return type != sTextInputTypeNone && !InputDialogContainer.isDialogInputType(type);
|
| }
|
|
|
| - boolean hasTextInputType() {
|
| + public boolean hasTextInputType() {
|
| return isTextInputType(mTextInputType);
|
| }
|
|
|
| - boolean dispatchKeyEvent(KeyEvent event) {
|
| + public boolean dispatchKeyEvent(KeyEvent event) {
|
| return translateAndSendNativeEvents(event);
|
| }
|
|
|
| - void commitText() {
|
| - cancelComposition();
|
| - if (mNativeImeAdapterAndroid != 0) {
|
| - nativeCommitText(mNativeImeAdapterAndroid, "");
|
| - }
|
| + 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;
|
| }
|
|
|
| - @SuppressWarnings("unused")
|
| - @CalledByNative
|
| - private void cancelComposition() {
|
| - if (mInputConnection != null) {
|
| - mInputConnection.restartInput();
|
| - }
|
| + 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++
|
| +
|
| @VisibleForTesting
|
| 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();
|
| if (!isFinish) {
|
| @@ -332,27 +371,7 @@ class ImeAdapter {
|
| return true;
|
| }
|
|
|
| - 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;
|
| - }
|
| -
|
| - private 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));
|
| - }
|
| -
|
| - private boolean translateAndSendNativeEvents(KeyEvent event) {
|
| + boolean translateAndSendNativeEvents(KeyEvent event) {
|
| if (mNativeImeAdapterAndroid == 0) return false;
|
|
|
| int action = event.getAction();
|
| @@ -376,35 +395,7 @@ class ImeAdapter {
|
| event.isSystem(), event.getUnicodeChar());
|
| }
|
|
|
| - private void setInputConnection(AdapterInputConnection inputConnection) {
|
| - mInputConnection = inputConnection;
|
| - }
|
| -
|
| - 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;
|
| - }
|
| -
|
| - boolean isActive() {
|
| - return mInputConnection != null && mInputConnection.isActive();
|
| - }
|
| -
|
| - private boolean sendSyntheticKeyEvent(
|
| + boolean sendSyntheticKeyEvent(
|
| int eventType, long timestampMs, int keyCode, int unicodeChar) {
|
| if (mNativeImeAdapterAndroid == 0) return false;
|
|
|
| @@ -413,9 +404,8 @@ class ImeAdapter {
|
| return true;
|
| }
|
|
|
| - private boolean deleteSurroundingText(int leftLength, int rightLength) {
|
| + boolean deleteSurroundingText(int leftLength, int rightLength) {
|
| if (mNativeImeAdapterAndroid == 0) return false;
|
| -
|
| nativeDeleteSurroundingText(mNativeImeAdapterAndroid, leftLength, rightLength);
|
| return true;
|
| }
|
| @@ -423,407 +413,131 @@ class ImeAdapter {
|
| @VisibleForTesting
|
| protected boolean setEditableSelectionOffsets(int start, int end) {
|
| if (mNativeImeAdapterAndroid == 0) return false;
|
| -
|
| nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end);
|
| return true;
|
| }
|
|
|
| - private void batchStateChanged(boolean isBegin) {
|
| + void batchStateChanged(boolean isBegin) {
|
| if (mNativeImeAdapterAndroid == 0) return;
|
| nativeImeBatchStateChanged(mNativeImeAdapterAndroid, isBegin);
|
| }
|
|
|
| - private boolean setComposingRegion(int start, int end) {
|
| - if (mNativeImeAdapterAndroid == 0) return false;
|
| + void commitText() {
|
| + cancelComposition();
|
| + if (mNativeImeAdapterAndroid != 0) {
|
| + nativeCommitText(mNativeImeAdapterAndroid, "");
|
| + }
|
| + }
|
|
|
| + /**
|
| + * 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;
|
| }
|
|
|
| - boolean unselect() {
|
| + /**
|
| + * 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;
|
| }
|
|
|
| - boolean selectAll() {
|
| + /**
|
| + * 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;
|
| }
|
|
|
| - boolean cut() {
|
| + /**
|
| + * 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;
|
| }
|
|
|
| - boolean copy() {
|
| + /**
|
| + * 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;
|
| }
|
|
|
| - boolean paste() {
|
| + /**
|
| + * 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;
|
| }
|
|
|
| - public static class AdapterInputConnectionFactory {
|
| - public AdapterInputConnection get(View view, ImeAdapter imeAdapter,
|
| - EditorInfo outAttrs) {
|
| - return new AdapterInputConnection(view, imeAdapter, outAttrs);
|
| - }
|
| - }
|
| -
|
| - // This InputConnection is created by ContentView.onCreateInputConnection.
|
| - // It then adapts android's IME to chrome's RenderWidgetHostView using the
|
| - // native ImeAdapterAndroid via the outer class ImeAdapter.
|
| - public static class AdapterInputConnection extends BaseInputConnection {
|
| - private static final String TAG =
|
| - "org.chromium.content.browser.ImeAdapter$AdapterInputConnection";
|
| - private static final boolean DEBUG = false;
|
| - private final View mInternalView;
|
| - private final ImeAdapter mImeAdapter;
|
| -
|
| - private boolean mSingleLine;
|
| - private int mNumNestedBatchEdits = 0;
|
| - private boolean mIgnoreTextInputStateUpdates = false;
|
| -
|
| - private int mLastUpdateSelectionStart = INVALID_SELECTION;
|
| - private int mLastUpdateSelectionEnd = INVALID_SELECTION;
|
| - private int mLastUpdateCompositionStart = INVALID_COMPOSITION;
|
| - private int mLastUpdateCompositionEnd = INVALID_COMPOSITION;
|
| -
|
| - /**
|
| - * Updates the AdapterInputConnection's internal representation of the text
|
| - * being edited and its selection and composition properties. The resulting
|
| - * Editable is accessible through the getEditable() method.
|
| - * If the text has not changed, this also calls updateSelection on the InputMethodManager.
|
| - * @param text The String contents of the field being edited
|
| - * @param selectionStart The character offset of the selection start, or the caret
|
| - * position if there is no selection
|
| - * @param selectionEnd The character offset of the selection end, or the caret
|
| - * position if there is no selection
|
| - * @param compositionStart The character offset of the composition start, or -1
|
| - * if there is no composition
|
| - * @param compositionEnd The character offset of the composition end, or -1
|
| - * if there is no selection
|
| - */
|
| - public void setEditableText(String text, int selectionStart, int selectionEnd,
|
| - int compositionStart, int compositionEnd) {
|
| - if (DEBUG) {
|
| - Log.w(TAG, "setEditableText [" + text + "] [" + selectionStart + " " + selectionEnd
|
| - + "] [" + compositionStart + " " + compositionEnd + "]");
|
| - }
|
| - Editable editable = getEditable();
|
| -
|
| - int prevSelectionStart = Selection.getSelectionStart(editable);
|
| - int prevSelectionEnd = Selection.getSelectionEnd(editable);
|
| - int prevCompositionStart = getComposingSpanStart(editable);
|
| - int prevCompositionEnd = getComposingSpanEnd(editable);
|
| - String prevText = editable.toString();
|
| -
|
| - selectionStart = Math.min(selectionStart, text.length());
|
| - selectionEnd = Math.min(selectionEnd, text.length());
|
| - compositionStart = Math.min(compositionStart, text.length());
|
| - compositionEnd = Math.min(compositionEnd, text.length());
|
| -
|
| - boolean textUnchanged = prevText.equals(text);
|
| -
|
| - if (!textUnchanged) {
|
| - editable.replace(0, editable.length(), text);
|
| - }
|
| -
|
| - if (prevSelectionStart == selectionStart && prevSelectionEnd == selectionEnd
|
| - && prevCompositionStart == compositionStart
|
| - && prevCompositionEnd == compositionEnd) {
|
| - // Nothing has changed; don't need to do anything
|
| - return;
|
| - }
|
| -
|
| - Selection.setSelection(editable, selectionStart, selectionEnd);
|
| - super.setComposingRegion(compositionStart, compositionEnd);
|
| -
|
| - if (mIgnoreTextInputStateUpdates) return;
|
| - updateSelection(selectionStart, selectionEnd, compositionStart, compositionEnd);
|
| - }
|
| -
|
| - @VisibleForTesting
|
| - protected void updateSelection(
|
| - int selectionStart, int selectionEnd,
|
| - int compositionStart, int compositionEnd) {
|
| - // Avoid sending update if we sent an exact update already previously.
|
| - if (mLastUpdateSelectionStart == selectionStart &&
|
| - mLastUpdateSelectionEnd == selectionEnd &&
|
| - mLastUpdateCompositionStart == compositionStart &&
|
| - mLastUpdateCompositionEnd == compositionEnd) {
|
| - return;
|
| - }
|
| - if (DEBUG) {
|
| - Log.w(TAG, "updateSelection [" + selectionStart + " " + selectionEnd + "] ["
|
| - + compositionStart + " " + compositionEnd + "]");
|
| - }
|
| - // updateSelection should be called every time the selection or composition changes
|
| - // if it happens not within a batch edit, or at the end of each top level batch edit.
|
| - getInputMethodManagerWrapper().updateSelection(mInternalView,
|
| - selectionStart, selectionEnd, compositionStart, compositionEnd);
|
| - mLastUpdateSelectionStart = selectionStart;
|
| - mLastUpdateSelectionEnd = selectionEnd;
|
| - mLastUpdateCompositionStart = compositionStart;
|
| - mLastUpdateCompositionEnd = compositionEnd;
|
| - }
|
| -
|
| - @Override
|
| - public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
| - if (DEBUG) Log.w(TAG, "setComposingText [" + text + "] [" + newCursorPosition + "]");
|
| - super.setComposingText(text, newCursorPosition);
|
| - return mImeAdapter.checkCompositionQueueAndCallNative(text.toString(),
|
| - newCursorPosition, false);
|
| - }
|
| -
|
| - @Override
|
| - public boolean commitText(CharSequence text, int newCursorPosition) {
|
| - if (DEBUG) Log.w(TAG, "commitText [" + text + "] [" + newCursorPosition + "]");
|
| - super.commitText(text, newCursorPosition);
|
| - return mImeAdapter.checkCompositionQueueAndCallNative(text.toString(),
|
| - newCursorPosition, text.length() > 0);
|
| - }
|
| -
|
| - @Override
|
| - public boolean performEditorAction(int actionCode) {
|
| - if (DEBUG) Log.w(TAG, "performEditorAction [" + actionCode + "]");
|
| - if (actionCode == EditorInfo.IME_ACTION_NEXT) {
|
| - restartInput();
|
| - // Send TAB key event
|
| - long timeStampMs = System.currentTimeMillis();
|
| - mImeAdapter.sendSyntheticKeyEvent(
|
| - sEventTypeRawKeyDown, timeStampMs, KeyEvent.KEYCODE_TAB, 0);
|
| - } else {
|
| - mImeAdapter.sendKeyEventWithKeyCode(KeyEvent.KEYCODE_ENTER,
|
| - KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
|
| - | KeyEvent.FLAG_EDITOR_ACTION);
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - @Override
|
| - public boolean performContextMenuAction(int id) {
|
| - if (DEBUG) Log.w(TAG, "performContextMenuAction [" + id + "]");
|
| - switch (id) {
|
| - case android.R.id.selectAll:
|
| - return mImeAdapter.selectAll();
|
| - case android.R.id.cut:
|
| - return mImeAdapter.cut();
|
| - case android.R.id.copy:
|
| - return mImeAdapter.copy();
|
| - case android.R.id.paste:
|
| - return mImeAdapter.paste();
|
| - default:
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
|
| - if (DEBUG) Log.w(TAG, "getExtractedText");
|
| - ExtractedText et = new ExtractedText();
|
| - Editable editable = getEditable();
|
| - et.text = editable.toString();
|
| - et.partialEndOffset = editable.length();
|
| - et.selectionStart = Selection.getSelectionStart(editable);
|
| - et.selectionEnd = Selection.getSelectionEnd(editable);
|
| - et.flags = mSingleLine ? ExtractedText.FLAG_SINGLE_LINE : 0;
|
| - return et;
|
| - }
|
| -
|
| - @Override
|
| - public boolean beginBatchEdit() {
|
| - if (DEBUG) Log.w(TAG, "beginBatchEdit [" + (mNumNestedBatchEdits == 0) + "]");
|
| - if (mNumNestedBatchEdits == 0) mImeAdapter.batchStateChanged(true);
|
| -
|
| - mNumNestedBatchEdits++;
|
| - return false;
|
| - }
|
| -
|
| - @Override
|
| - public boolean endBatchEdit() {
|
| - if (mNumNestedBatchEdits == 0) return false;
|
| -
|
| - --mNumNestedBatchEdits;
|
| - if (DEBUG) Log.w(TAG, "endBatchEdit [" + (mNumNestedBatchEdits == 0) + "]");
|
| - if (mNumNestedBatchEdits == 0) mImeAdapter.batchStateChanged(false);
|
| - return false;
|
| - }
|
| -
|
| - @Override
|
| - public boolean deleteSurroundingText(int leftLength, int rightLength) {
|
| - if (DEBUG) {
|
| - Log.w(TAG, "deleteSurroundingText [" + leftLength + " " + rightLength + "]");
|
| - }
|
| - if (!super.deleteSurroundingText(leftLength, rightLength)) {
|
| - return false;
|
| - }
|
| - return mImeAdapter.deleteSurroundingText(leftLength, rightLength);
|
| - }
|
| -
|
| - @Override
|
| - public boolean sendKeyEvent(KeyEvent event) {
|
| - if (DEBUG) Log.w(TAG, "sendKeyEvent [" + event.getAction() + "]");
|
| - mImeAdapter.mSelectionHandleController.hideAndDisallowAutomaticShowing();
|
| - mImeAdapter.mInsertionHandleController.hideAndDisallowAutomaticShowing();
|
| -
|
| - // If this is a key-up, and backspace/del or if the key has a character representation,
|
| - // need to update the underlying Editable (i.e. the local representation of the text
|
| - // being edited).
|
| - if (event.getAction() == KeyEvent.ACTION_UP) {
|
| - if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
|
| - super.deleteSurroundingText(1, 0);
|
| - } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
|
| - super.deleteSurroundingText(0, 1);
|
| - } else {
|
| - int unicodeChar = event.getUnicodeChar();
|
| - if (unicodeChar != 0) {
|
| - Editable editable = getEditable();
|
| - int selectionStart = Selection.getSelectionStart(editable);
|
| - int selectionEnd = Selection.getSelectionEnd(editable);
|
| - if (selectionStart > selectionEnd) {
|
| - int temp = selectionStart;
|
| - selectionStart = selectionEnd;
|
| - selectionEnd = temp;
|
| - }
|
| - editable.replace(selectionStart, selectionEnd,
|
| - Character.toString((char)unicodeChar));
|
| - }
|
| - }
|
| - }
|
| - mImeAdapter.translateAndSendNativeEvents(event);
|
| - return true;
|
| - }
|
| -
|
| - @Override
|
| - public boolean finishComposingText() {
|
| - if (DEBUG) Log.w(TAG, "finishComposingText");
|
| - Editable editable = getEditable();
|
| - if (getComposingSpanStart(editable) == getComposingSpanEnd(editable)) {
|
| - return true;
|
| - }
|
| - super.finishComposingText();
|
| - return mImeAdapter.checkCompositionQueueAndCallNative("", 0, true);
|
| - }
|
| -
|
| - @Override
|
| - public boolean setSelection(int start, int end) {
|
| - if (DEBUG) Log.w(TAG, "setSelection");
|
| - if (start < 0 || end < 0) return true;
|
| - super.setSelection(start, end);
|
| - return mImeAdapter.setEditableSelectionOffsets(start, end);
|
| - }
|
| -
|
| - /**
|
| - * Informs the InputMethodManager and InputMethodSession (i.e. the IME) that the text
|
| - * state is no longer what the IME has and that it needs to be updated.
|
| - */
|
| - void restartInput() {
|
| - if (DEBUG) Log.w(TAG, "restartInput");
|
| - getInputMethodManagerWrapper().restartInput(mInternalView);
|
| - mIgnoreTextInputStateUpdates = false;
|
| - mNumNestedBatchEdits = 0;
|
| - }
|
| -
|
| - @Override
|
| - public boolean setComposingRegion(int start, int end) {
|
| - if (DEBUG) Log.w(TAG, "setComposingRegion [" + start + " " + end + "]");
|
| - int a = Math.min(start, end);
|
| - int b = Math.max(start, end);
|
| - super.setComposingRegion(a, b);
|
| - return mImeAdapter.setComposingRegion(a, b);
|
| - }
|
| -
|
| - boolean isActive() {
|
| - return getInputMethodManagerWrapper().isActive(mInternalView);
|
| - }
|
| -
|
| - void setIgnoreTextInputStateUpdates(boolean shouldIgnore) {
|
| - mIgnoreTextInputStateUpdates = shouldIgnore;
|
| - if (shouldIgnore) return;
|
| + // Calls from C++ to Java
|
|
|
| - Editable editable = getEditable();
|
| - updateSelection(Selection.getSelectionStart(editable),
|
| - Selection.getSelectionEnd(editable),
|
| - getComposingSpanStart(editable),
|
| - getComposingSpanEnd(editable));
|
| - }
|
| + @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;
|
| + }
|
|
|
| - @VisibleForTesting
|
| - protected boolean isIgnoringTextInputStateUpdates() {
|
| - return mIgnoreTextInputStateUpdates;
|
| - }
|
| + @CalledByNative
|
| + private static void initializeTextInputTypes(int textInputTypeNone, int textInputTypeText,
|
| + int textInputTypeTextArea, int textInputTypePassword, int textInputTypeSearch,
|
| + int textInputTypeUrl, int textInputTypeEmail, int textInputTypeTel,
|
| + int textInputTypeNumber, int textInputTypeDate, int textInputTypeDateTime,
|
| + int textInputTypeDateTimeLocal, int textInputTypeMonth, int textInputTypeTime,
|
| + int textInputTypeWeek, int textInputTypeContentEditable) {
|
| + sTextInputTypeNone = textInputTypeNone;
|
| + sTextInputTypeText = textInputTypeText;
|
| + sTextInputTypeTextArea = textInputTypeTextArea;
|
| + sTextInputTypePassword = textInputTypePassword;
|
| + sTextInputTypeSearch = textInputTypeSearch;
|
| + sTextInputTypeUrl = textInputTypeUrl;
|
| + sTextInputTypeEmail = textInputTypeEmail;
|
| + sTextInputTypeTel = textInputTypeTel;
|
| + sTextInputTypeNumber = textInputTypeNumber;
|
| + sTextInputTypeWeek = textInputTypeWeek;
|
| + sTextInputTypeContentEditable = textInputTypeContentEditable;
|
| + }
|
|
|
| - private InputMethodManagerWrapper getInputMethodManagerWrapper() {
|
| - return mImeAdapter.getInputMethodManagerWrapper();
|
| + @CalledByNative
|
| + private void cancelComposition() {
|
| + if (mInputConnection != null) {
|
| + mInputConnection.restartInput();
|
| }
|
| + }
|
|
|
| - @VisibleForTesting
|
| - protected AdapterInputConnection(View view, ImeAdapter imeAdapter, EditorInfo outAttrs) {
|
| - super(view, true);
|
| - mInternalView = view;
|
| - mImeAdapter = imeAdapter;
|
| - mImeAdapter.setInputConnection(this);
|
| - mSingleLine = true;
|
| - outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
| - outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT
|
| - | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
|
| - if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeText) {
|
| - // Normal text field
|
| - outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
| - } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeTextArea ||
|
| - imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeContentEditable) {
|
| - // TextArea or contenteditable.
|
| - outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
|
| - | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
|
| - | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
|
| - outAttrs.imeOptions |= EditorInfo.IME_ACTION_NONE;
|
| - mSingleLine = false;
|
| - } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypePassword) {
|
| - // Password
|
| - outAttrs.inputType = InputType.TYPE_CLASS_TEXT
|
| - | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD;
|
| - outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
| - } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeSearch) {
|
| - // Search
|
| - outAttrs.imeOptions |= EditorInfo.IME_ACTION_SEARCH;
|
| - } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeUrl) {
|
| - // Url
|
| - // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
|
| - // exclude it for now.
|
| - outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
| - } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeEmail) {
|
| - // Email
|
| - outAttrs.inputType = InputType.TYPE_CLASS_TEXT
|
| - | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
|
| - outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
| - } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeTel) {
|
| - // Telephone
|
| - // Number and telephone do not have both a Tab key and an
|
| - // action in default OSK, so set the action to NEXT
|
| - outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
|
| - outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
|
| - } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeNumber) {
|
| - // Number
|
| - outAttrs.inputType = InputType.TYPE_CLASS_NUMBER
|
| - | InputType.TYPE_NUMBER_VARIATION_NORMAL;
|
| - outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
|
| - }
|
| - outAttrs.initialSelStart = imeAdapter.mInitialSelectionStart;
|
| - outAttrs.initialSelEnd = imeAdapter.mInitialSelectionStart;
|
| - }
|
| + @CalledByNative
|
| + void detach() {
|
| + mNativeImeAdapterAndroid = 0;
|
| + mTextInputType = 0;
|
| }
|
|
|
| private native boolean nativeSendSyntheticKeyEvent(int nativeImeAdapterAndroid,
|
|
|