diff options
-rw-r--r-- | build/android/findbugs_filter/findbugs_known_bugs.txt | 2 | ||||
-rw-r--r-- | chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java (renamed from chrome/android/javatests/src/org/chromium/chrome/browser/test/AutofillTest.java) | 20 | ||||
-rw-r--r-- | chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java (renamed from chrome/android/javatests/src/org/chromium/chrome/browser/test/SelectPopupOtherContentViewTest.java) | 4 | ||||
-rw-r--r-- | content/content_jni.gypi | 4 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java | 35 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/ImeAdapter.java | 858 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java | 410 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/CursorController.java (renamed from content/public/android/java/src/org/chromium/content/browser/CursorController.java) | 2 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java (renamed from content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java) | 3 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/DateTimePickerDialog.java (renamed from content/public/android/java/src/org/chromium/content/browser/DateTimePickerDialog.java) | 2 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/HandleView.java (renamed from content/public/android/java/src/org/chromium/content/browser/HandleView.java) | 13 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java | 572 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/InputDialogContainer.java (renamed from content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java) | 8 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/InputMethodManagerWrapper.java (renamed from content/public/android/java/src/org/chromium/content/browser/InputMethodManagerWrapper.java) | 4 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/InsertionHandleController.java (renamed from content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java) | 29 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/MonthPicker.java (renamed from content/public/android/java/src/org/chromium/content/browser/MonthPicker.java) | 2 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/MonthPickerDialog.java (renamed from content/public/android/java/src/org/chromium/content/browser/MonthPickerDialog.java) | 4 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/SelectPopupDialog.java (renamed from content/public/android/java/src/org/chromium/content/browser/SelectPopupDialog.java) | 8 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/input/SelectionHandleController.java (renamed from content/public/android/java/src/org/chromium/content/browser/SelectionHandleController.java) | 47 | ||||
-rw-r--r-- | content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java (renamed from content/public/android/javatests/src/org/chromium/content/browser/ImeTest.java) | 11 | ||||
-rw-r--r-- | content/public/android/javatests/src/org/chromium/content/browser/input/InsertionHandleTest.java (renamed from content/public/android/javatests/src/org/chromium/content/browser/InsertionHandleTest.java) | 4 | ||||
-rw-r--r-- | content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java (renamed from content/public/android/javatests/src/org/chromium/content/browser/SelectPopupTest.java) | 3 |
22 files changed, 1066 insertions, 979 deletions
diff --git a/build/android/findbugs_filter/findbugs_known_bugs.txt b/build/android/findbugs_filter/findbugs_known_bugs.txt index 61fc567..cb37ca2 100644 --- a/build/android/findbugs_filter/findbugs_known_bugs.txt +++ b/build/android/findbugs_filter/findbugs_known_bugs.txt @@ -25,11 +25,9 @@ M C RCN: Nullcheck of GestureDetector.mVelocityTracker at line 630 of value prev M C USELESS_STRING: Invocation of toString on certChain in org.chromium.net.X509Util.verifyServerCertificates(byte[][], String) At X509Util.java M D DMI: Hard coded reference to an absolute pathname in org.chromium.android_webview.test.ArchiveTest.testAutoBadPath() At ArchiveTest.java M D DMI: Hard coded reference to an absolute pathname in org.chromium.android_webview.test.ArchiveTest.testExplicitBadPath() At ArchiveTest.java -M D ICAST: integral division result cast to double or float in org.chromium.content.browser.HandleView.setOrientation(int) At HandleView.java M D SF: Switch statement found in org.chromium.chrome.browser.ChromeBrowserProvider.insert(Uri, ContentValues) where one case falls through to the next case At ChromeBrowserProvider.java M D SF: Switch statement found in org.chromium.chrome.browser.database.SQLiteCursor.fillWindow(int, CursorWindow) where default case is missing At SQLiteCursor.java M D SF: Switch statement found in org.chromium.content.browser.ContentSettings$EventHandler$1.handleMessage(Message) where default case is missing At ContentSettings.java -M D SF: Switch statement found in org.chromium.content.browser.HandleView.onTouchEvent(MotionEvent) where default case is missing At HandleView.java M D SF: Switch statement found in org.chromium.content.browser.third_party.GestureDetector.onTouchEvent(MotionEvent) where default case is missing At GestureDetector.java M D ST: Write to static field org.chromium.content.browser.ContentSettings.sAppCachePathIsSet from instance method org.chromium.content.browser.ContentSettings.setAppCachePath(String) At ContentSettings.java M M LI: Incorrect lazy initialization and update of static field org.chromium.content.browser.ContentVideoView.sContentVideoView in org.chromium.content.browser.ContentVideoView.createContentVideoView(int) At ContentVideoView.java diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/test/AutofillTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java index 47bdbe0..54d15d3 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/test/AutofillTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java @@ -2,33 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.test; - -import android.app.Activity; -import android.content.ContentProvider; -import android.content.ContentResolver; -import android.os.SystemClock; -import android.test.ActivityInstrumentationTestCase2; -import android.test.IsolatedContext; -import android.test.mock.MockContentResolver; -import android.test.suitebuilder.annotation.MediumTest; +package org.chromium.chrome.browser.autofill; + import android.test.suitebuilder.annotation.SmallTest; -import android.util.Pair; -import android.view.MotionEvent; -import android.view.View; -import android.view.inputmethod.ExtractedText; -import android.view.inputmethod.ExtractedTextRequest; import org.chromium.base.test.util.Feature; -import org.chromium.chrome.browser.ChromeBrowserProvider; -import org.chromium.chrome.browser.autofill.AutofillListAdapter; import org.chromium.chrome.browser.autofill.AutofillPopup; import org.chromium.chrome.browser.autofill.AutofillPopup.AutofillPopupDelegate; import org.chromium.chrome.browser.autofill.AutofillSuggestion; import org.chromium.chrome.testshell.ChromiumTestShellActivity; import org.chromium.chrome.testshell.ChromiumTestShellTestBase; import org.chromium.content.browser.ContainerViewDelegate; -import org.chromium.content.browser.ContentView; import org.chromium.content.browser.test.util.TouchCommon; import org.chromium.content.browser.test.util.UiUtils; import org.chromium.content.browser.test.util.Criteria; diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/test/SelectPopupOtherContentViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java index ce4a7a5..d09c682 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/test/SelectPopupOtherContentViewTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java @@ -2,7 +2,7 @@ // 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.chrome.browser.input; import android.test.suitebuilder.annotation.LargeTest; @@ -10,7 +10,7 @@ import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.UrlUtils; import org.chromium.content.browser.ContentView; -import org.chromium.content.browser.SelectPopupDialog; +import org.chromium.content.browser.input.SelectPopupDialog; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.DOMUtils; diff --git a/content/content_jni.gypi b/content/content_jni.gypi index 3116c04..b52eaa3 100644 --- a/content/content_jni.gypi +++ b/content/content_jni.gypi @@ -18,10 +18,10 @@ 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', - 'public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', - 'public/android/java/src/org/chromium/content/browser/ImeAdapter.java', + 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', + 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java index 9b8cde6..6d44e04 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java @@ -42,8 +42,14 @@ import org.chromium.base.JNINamespace; import org.chromium.base.WeakContext; import org.chromium.content.R; import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate; -import org.chromium.content.browser.ImeAdapter.AdapterInputConnectionFactory; import org.chromium.content.browser.accessibility.AccessibilityInjector; +import org.chromium.content.browser.input.AdapterInputConnection; +import org.chromium.content.browser.input.HandleView; +import org.chromium.content.browser.input.ImeAdapter; +import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory; +import org.chromium.content.browser.input.InsertionHandleController; +import org.chromium.content.browser.input.SelectPopupDialog; +import org.chromium.content.browser.input.SelectionHandleController; import org.chromium.content.common.TraceEvent; import org.chromium.ui.gfx.NativeWindow; @@ -192,7 +198,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient { // Only valid when focused on a text / password field. private ImeAdapter mImeAdapter; private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory; - private ImeAdapter.AdapterInputConnection mInputConnection; + private AdapterInputConnection mInputConnection; private SelectionHandleController mSelectionHandleController; private InsertionHandleController mInsertionHandleController; @@ -326,17 +332,17 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient { } @VisibleForTesting - protected ImeAdapter getImeAdapterForTest() { + public ImeAdapter getImeAdapterForTest() { return mImeAdapter; } @VisibleForTesting - protected void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) { + public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) { mAdapterInputConnectionFactory = factory; } @VisibleForTesting - protected ImeAdapter.AdapterInputConnection getInputConnectionForTest() { + public AdapterInputConnection getInputConnectionForTest() { return mInputConnection; } @@ -1293,8 +1299,9 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient { if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) { mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore), - ImeAdapter.sTextInputTypeNone, ImeAdapter.INVALID_SELECTION, - ImeAdapter.INVALID_SELECTION); + ImeAdapter.getTextInputTypeNone(), + AdapterInputConnection.INVALID_SELECTION, + AdapterInputConnection.INVALID_SELECTION); InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); manager.restartInput(mContainerView); @@ -1662,7 +1669,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient { return mZoomManager.isMultiTouchZoomSupported(); } - void selectPopupMenuItems(int[] indices) { + public void selectPopupMenuItems(int[] indices) { if (mNativeContentViewCore != 0) { nativeSelectPopupMenuItems(mNativeContentViewCore, indices); } @@ -2057,11 +2064,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient { String text, int selectionStart, int selectionEnd, int compositionStart, int compositionEnd, boolean showImeIfNeeded) { TraceEvent.begin(); - - // Non-breaking spaces can cause the IME to get confused. Replace with normal spaces. - text = text.replace('\u00A0', ' '); - - mSelectionEditable = (textInputType != ImeAdapter.sTextInputTypeNone); + mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone()); if (mActionMode != null) mActionMode.invalidate(); @@ -2069,12 +2072,6 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient { selectionStart, selectionEnd, showImeIfNeeded); if (mInputConnection != null) { - // In WebKit if there's a composition then the selection will usually be the - // same as the composition, whereas Android IMEs expect the selection to be - // just a caret at the end of the composition. - if (selectionStart == compositionStart && selectionEnd == compositionEnd) { - selectionStart = selectionEnd; - } mInputConnection.setEditableText(text, selectionStart, selectionEnd, compositionStart, compositionEnd); } diff --git a/content/public/android/java/src/org/chromium/content/browser/ImeAdapter.java b/content/public/android/java/src/org/chromium/content/browser/ImeAdapter.java deleted file mode 100644 index 1f73502..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/ImeAdapter.java +++ /dev/null @@ -1,858 +0,0 @@ -// Copyright (c) 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; - -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; - -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") -class ImeAdapter { - 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(); - } - - 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; - - 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 sTextInputTypeWeek; - static int sTextInputTypeContentEditable; - static int sModifierShift; - static int sModifierAlt; - static int sModifierCtrl; - 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; - - @VisibleForTesting - protected 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, - InsertionHandleController insertionHandleController, ViewEmbedder embedder) { - mContext = context; - mInputMethodManagerWrapper = new InputMethodManagerWrapper(context); - mSelectionHandleController = selectionHandleController; - mInsertionHandleController = insertionHandleController; - mViewEmbedder = embedder; - mHandler = new Handler(); - } - - boolean isFor(int nativeImeAdapter, int textInputType) { - return mNativeImeAdapterAndroid == nativeImeAdapter && - mTextInputType == textInputType; - } - - @VisibleForTesting - protected void setInputMethodManagerWrapper(InputMethodManagerWrapper immw) { - mInputMethodManagerWrapper = immw; - } - - private InputMethodManagerWrapper getInputMethodManagerWrapper() { - return mInputMethodManagerWrapper; - } - - 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; - } - - int previousType = mTextInputType; - attach(nativeImeAdapter, textInputType, selectionStart, selectionEnd); - - mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView()); - if (showIfNeeded) { - showKeyboard(); - } - } else if (hasInputType() && showIfNeeded) { - showKeyboard(); - } - } - - void attach(int nativeImeAdapter, int textInputType, int selectionStart, int selectionEnd) { - mNativeImeAdapterAndroid = nativeImeAdapter; - mTextInputType = textInputType; - mInitialSelectionStart = selectionStart; - mInitialSelectionEnd = selectionEnd; - 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. - */ - void attach(int nativeImeAdapter) { - mNativeImeAdapterAndroid = nativeImeAdapter; - if (nativeImeAdapter != 0) { - nativeAttachImeAdapter(mNativeImeAdapterAndroid); - } - } - - /** - * 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() { - return mNativeImeAdapterAndroid != 0; - } - - private void showKeyboard() { - mIsShowWithoutHideOutstanding = true; - mInputMethodManagerWrapper.showSoftInput(mViewEmbedder.getAttachedView(), 0, - mViewEmbedder.getNewShowKeyboardReceiver()); - } - - private void dismissInput(boolean unzoomIfNeeded) { - hideKeyboard(unzoomIfNeeded); - mViewEmbedder.onDismissInput(); - } - - private void hideKeyboard(boolean unzoomIfNeeded) { - mIsShowWithoutHideOutstanding = false; - View view = mViewEmbedder.getAttachedView(); - if (mInputMethodManagerWrapper.isActive(view)) { - mInputMethodManagerWrapper.hideSoftInputFromWindow(view.getWindowToken(), 0, - unzoomIfNeeded ? mViewEmbedder.getNewShowKeyboardReceiver() : null); - } - } - - @CalledByNative - void detach() { - mNativeImeAdapterAndroid = 0; - mTextInputType = 0; - } - - boolean hasInputType() { - return mTextInputType != sTextInputTypeNone; - } - - static boolean isTextInputType(int type) { - return type != sTextInputTypeNone && !InputDialogContainer.isDialogInputType(type); - } - - boolean hasTextInputType() { - return isTextInputType(mTextInputType); - } - - boolean dispatchKeyEvent(KeyEvent event) { - return translateAndSendNativeEvents(event); - } - - void commitText() { - cancelComposition(); - if (mNativeImeAdapterAndroid != 0) { - nativeCommitText(mNativeImeAdapterAndroid, ""); - } - } - - @SuppressWarnings("unused") - @CalledByNative - private void cancelComposition() { - if (mInputConnection != null) { - mInputConnection.restartInput(); - } - } - - @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) { - mSelectionHandleController.hideAndDisallowAutomaticShowing(); - mInsertionHandleController.hideAndDisallowAutomaticShowing(); - } - 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; - } - - 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) { - 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()); - } - - 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( - int eventType, long timestampMs, int keyCode, int unicodeChar) { - if (mNativeImeAdapterAndroid == 0) return false; - - nativeSendSyntheticKeyEvent( - mNativeImeAdapterAndroid, eventType, timestampMs, keyCode, unicodeChar); - return true; - } - - private boolean deleteSurroundingText(int leftLength, int rightLength) { - if (mNativeImeAdapterAndroid == 0) return false; - - nativeDeleteSurroundingText(mNativeImeAdapterAndroid, leftLength, rightLength); - return true; - } - - @VisibleForTesting - protected boolean setEditableSelectionOffsets(int start, int end) { - if (mNativeImeAdapterAndroid == 0) return false; - - nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); - return true; - } - - private void batchStateChanged(boolean isBegin) { - if (mNativeImeAdapterAndroid == 0) return; - nativeImeBatchStateChanged(mNativeImeAdapterAndroid, isBegin); - } - - private boolean setComposingRegion(int start, int end) { - if (mNativeImeAdapterAndroid == 0) return false; - - nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); - return true; - } - - boolean unselect() { - if (mNativeImeAdapterAndroid == 0) return false; - - nativeUnselect(mNativeImeAdapterAndroid); - return true; - } - - boolean selectAll() { - if (mNativeImeAdapterAndroid == 0) return false; - - nativeSelectAll(mNativeImeAdapterAndroid); - return true; - } - - boolean cut() { - if (mNativeImeAdapterAndroid == 0) return false; - - nativeCut(mNativeImeAdapterAndroid); - return true; - } - - boolean copy() { - if (mNativeImeAdapterAndroid == 0) return false; - - nativeCopy(mNativeImeAdapterAndroid); - return true; - } - - 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; - - Editable editable = getEditable(); - updateSelection(Selection.getSelectionStart(editable), - Selection.getSelectionEnd(editable), - getComposingSpanStart(editable), - getComposingSpanEnd(editable)); - } - - @VisibleForTesting - protected boolean isIgnoringTextInputStateUpdates() { - return mIgnoreTextInputStateUpdates; - } - - private InputMethodManagerWrapper getInputMethodManagerWrapper() { - return mImeAdapter.getInputMethodManagerWrapper(); - } - - @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; - } - } - - private native boolean nativeSendSyntheticKeyEvent(int nativeImeAdapterAndroid, - int eventType, long timestampMs, int keyCode, int unicodeChar); - - private native boolean nativeSendKeyEvent(int nativeImeAdapterAndroid, KeyEvent event, - int action, int modifiers, long timestampMs, int keyCode, boolean isSystemKey, - int unicodeChar); - - private native void nativeSetComposingText(int nativeImeAdapterAndroid, String text, - int newCursorPosition); - - private native void nativeCommitText(int nativeImeAdapterAndroid, String text); - - private native void nativeAttachImeAdapter(int nativeImeAdapterAndroid); - - private native void nativeSetEditableSelectionOffsets(int nativeImeAdapterAndroid, - int start, int end); - - private native void nativeSetComposingRegion(int nativeImeAdapterAndroid, int start, int end); - - private native void nativeDeleteSurroundingText(int nativeImeAdapterAndroid, - int before, int after); - - private native void nativeImeBatchStateChanged(int nativeImeAdapterAndroid, boolean isBegin); - - private native void nativeUnselect(int nativeImeAdapterAndroid); - private native void nativeSelectAll(int nativeImeAdapterAndroid); - private native void nativeCut(int nativeImeAdapterAndroid); - private native void nativeCopy(int nativeImeAdapterAndroid); - private native void nativePaste(int nativeImeAdapterAndroid); -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java new file mode 100644 index 0000000..045a542 --- /dev/null +++ b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java @@ -0,0 +1,410 @@ +// Copyright (c) 2013 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 com.google.common.annotations.VisibleForTesting; + +import android.text.Editable; +import android.text.InputType; +import android.text.Selection; +import android.util.Log; +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; + +/** + * InputConnection is created by ContentView.onCreateInputConnection. + * It then adapts android's IME to chrome's RenderWidgetHostView using the + * native ImeAdapterAndroid via the class ImeAdapter. + */ +public class AdapterInputConnection extends BaseInputConnection { + private static final String TAG = + "org.chromium.content.browser.input.AdapterInputConnection"; + private static final boolean DEBUG = false; + /** + * Selection value should be -1 if not known. See EditorInfo.java for details. + */ + public static final int INVALID_SELECTION = -1; + public static final int INVALID_COMPOSITION = -1; + + 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; + + @VisibleForTesting + 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.getTextInputType() == ImeAdapter.sTextInputTypeText) { + // Normal text field + outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; + } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeTextArea || + imeAdapter.getTextInputType() == 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.getTextInputType() == ImeAdapter.sTextInputTypePassword) { + // Password + outAttrs.inputType = InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD; + outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; + } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeSearch) { + // Search + outAttrs.imeOptions |= EditorInfo.IME_ACTION_SEARCH; + } else if (imeAdapter.getTextInputType() == 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.getTextInputType() == 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.getTextInputType() == 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.getTextInputType() == ImeAdapter.sTextInputTypeNumber) { + // Number + outAttrs.inputType = InputType.TYPE_CLASS_NUMBER + | InputType.TYPE_NUMBER_VARIATION_NORMAL; + outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT; + } + outAttrs.initialSelStart = imeAdapter.getInitialSelectionStart(); + outAttrs.initialSelEnd = imeAdapter.getInitialSelectionStart(); + } + + /** + * 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 + "]"); + } + // Non-breaking spaces can cause the IME to get confused. Replace with normal spaces. + text = text.replace('\u00A0', ' '); + + 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; + } + + /** + * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) + */ + @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); + } + + /** + * @see BaseInputConnection#commitText(java.lang.CharSequence, int) + */ + @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); + } + + /** + * @see BaseInputConnection#performEditorAction(int) + */ + @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( + ImeAdapter.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; + } + + /** + * @see BaseInputConnection#performContextMenuAction(int) + */ + @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; + } + } + + /** + * @see BaseInputConnection#getExtractedText(android.view.inputmethod.ExtractedTextRequest, + * int) + */ + @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; + } + + /** + * @see BaseInputConnection#beginBatchEdit() + */ + @Override + public boolean beginBatchEdit() { + if (DEBUG) Log.w(TAG, "beginBatchEdit [" + (mNumNestedBatchEdits == 0) + "]"); + if (mNumNestedBatchEdits == 0) mImeAdapter.batchStateChanged(true); + + mNumNestedBatchEdits++; + return false; + } + + /** + * @see BaseInputConnection#endBatchEdit() + */ + @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; + } + + /** + * @see BaseInputConnection#deleteSurroundingText(int, int) + */ + @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); + } + + /** + * @see BaseInputConnection#sendKeyEvent(android.view.KeyEvent) + */ + @Override + public boolean sendKeyEvent(KeyEvent event) { + if (DEBUG) Log.w(TAG, "sendKeyEvent [" + event.getAction() + "]"); + mImeAdapter.hideSelectionAndInsertionHandleControllers(); + + // 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; + } + + /** + * @see BaseInputConnection#finishComposingText() + */ + @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); + } + + /** + * @see BaseInputConnection#setSelection(int, int) + */ + @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; + } + + /** + * @see BaseInputConnection#setComposingRegion(int, int) + */ + @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); + } + + public void setIgnoreTextInputStateUpdates(boolean shouldIgnore) { + mIgnoreTextInputStateUpdates = shouldIgnore; + if (shouldIgnore) return; + + Editable editable = getEditable(); + updateSelection(Selection.getSelectionStart(editable), + Selection.getSelectionEnd(editable), + getComposingSpanStart(editable), + getComposingSpanEnd(editable)); + } + + @VisibleForTesting + protected boolean isIgnoringTextInputStateUpdates() { + return mIgnoreTextInputStateUpdates; + } + + private InputMethodManagerWrapper getInputMethodManagerWrapper() { + return mImeAdapter.getInputMethodManagerWrapper(); + } +} diff --git a/content/public/android/java/src/org/chromium/content/browser/CursorController.java b/content/public/android/java/src/org/chromium/content/browser/input/CursorController.java index 6e572c8..10e3064 100644 --- a/content/public/android/java/src/org/chromium/content/browser/CursorController.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/CursorController.java @@ -2,7 +2,7 @@ // 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.view.ViewTreeObserver; diff --git a/content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java b/content/public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java index 7fc146b..f8fca54 100644 --- a/content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java @@ -2,10 +2,11 @@ // 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 org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; +import org.chromium.content.browser.ContentViewCore; import android.content.Context; diff --git a/content/public/android/java/src/org/chromium/content/browser/DateTimePickerDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/DateTimePickerDialog.java index c122c29..29bca66 100644 --- a/content/public/android/java/src/org/chromium/content/browser/DateTimePickerDialog.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/DateTimePickerDialog.java @@ -2,7 +2,7 @@ // 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.app.AlertDialog; import android.content.Context; diff --git a/content/public/android/java/src/org/chromium/content/browser/HandleView.java b/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java index a772d5d..a220894 100644 --- a/content/public/android/java/src/org/chromium/content/browser/HandleView.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java @@ -2,7 +2,7 @@ // 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.content.res.TypedArray; @@ -27,7 +27,7 @@ import android.widget.TextView; /** * View that displays a selection or insertion handle for text editing. */ -class HandleView extends View { +public class HandleView extends View { private static final float FADE_DURATION = 200.f; private Drawable mDrawable; @@ -113,7 +113,7 @@ class HandleView extends View { } mDrawable = mSelectHandleLeft; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = (handleWidth * 3) / 4; + mHotspotX = (handleWidth * 3) / 4f; break; } @@ -124,7 +124,7 @@ class HandleView extends View { } mDrawable = mSelectHandleRight; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = handleWidth / 4; + mHotspotX = handleWidth / 4f; break; } @@ -136,7 +136,7 @@ class HandleView extends View { } mDrawable = mSelectHandleCenter; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = handleWidth / 2; + mHotspotX = handleWidth / 2f; mIsInsertionHandle = true; break; } @@ -306,6 +306,9 @@ class HandleView extends View { case MotionEvent.ACTION_CANCEL: mIsDragging = false; break; + + default: + return false; } return true; } 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 new file mode 100644 index 0000000..1ea57b4 --- /dev/null +++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java @@ -0,0 +1,572 @@ +// Copyright (c) 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.content.Context; +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 { + public interface ViewEmbedder { + /** + * @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 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; + 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 sTextInputTypeWeek; + static int sTextInputTypeContentEditable; + static int sModifierShift; + static int sModifierAlt; + static int sModifierCtrl; + static int sModifierCapsLockOn; + static int sModifierNumLockOn; + + private int mNativeImeAdapterAndroid; + private final Context mContext; + private InputMethodManagerWrapper mInputMethodManagerWrapper; + private AdapterInputConnection mInputConnection; + private final ViewEmbedder mViewEmbedder; + private final Handler mHandler; + private DelayedDismissInput mDismissInput = null; + private final SelectionHandleController mSelectionHandleController; + private final InsertionHandleController mInsertionHandleController; + private int mTextInputType; + private int mInitialSelectionStart; + private int mInitialSelectionEnd; + + @VisibleForTesting + boolean mIsShowWithoutHideOutstanding = false; + + /** + * @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); + mSelectionHandleController = selectionHandleController; + mInsertionHandleController = insertionHandleController; + mViewEmbedder = embedder; + mHandler = new Handler(); + } + + public static class AdapterInputConnectionFactory { + public AdapterInputConnection get(View view, ImeAdapter imeAdapter, + EditorInfo outAttrs) { + return new AdapterInputConnection(view, imeAdapter, outAttrs); + } + } + + @VisibleForTesting + protected 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; + } + + 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); + + // 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; + } + + int previousType = mTextInputType; + attach(nativeImeAdapter, textInputType, selectionStart, selectionEnd); + + mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView()); + if (showIfNeeded) { + showKeyboard(); + } + } else if (hasInputType() && showIfNeeded) { + showKeyboard(); + } + } + + public void attach(int nativeImeAdapter, int textInputType, int selectionStart, + int selectionEnd) { + mNativeImeAdapterAndroid = nativeImeAdapter; + mTextInputType = textInputType; + mInitialSelectionStart = selectionStart; + mInitialSelectionEnd = selectionEnd; + 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(int nativeImeAdapter) { + mNativeImeAdapterAndroid = nativeImeAdapter; + if (nativeImeAdapter != 0) { + nativeAttachImeAdapter(mNativeImeAdapterAndroid); + } + } + + /** + * 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. + */ + public boolean isNativeImeAdapterAttached() { + return mNativeImeAdapterAndroid != 0; + } + + private void showKeyboard() { + mIsShowWithoutHideOutstanding = true; + mInputMethodManagerWrapper.showSoftInput(mViewEmbedder.getAttachedView(), 0, + mViewEmbedder.getNewShowKeyboardReceiver()); + } + + private void dismissInput(boolean unzoomIfNeeded) { + hideKeyboard(unzoomIfNeeded); + mViewEmbedder.onDismissInput(); + } + + private void hideKeyboard(boolean unzoomIfNeeded) { + mIsShowWithoutHideOutstanding = false; + View view = mViewEmbedder.getAttachedView(); + if (mInputMethodManagerWrapper.isActive(view)) { + mInputMethodManagerWrapper.hideSoftInputFromWindow(view.getWindowToken(), 0, + unzoomIfNeeded ? mViewEmbedder.getNewShowKeyboardReceiver() : null); + } + } + + private boolean hasInputType() { + return mTextInputType != sTextInputTypeNone; + } + + 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++ + + @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) { + mSelectionHandleController.hideAndDisallowAutomaticShowing(); + mInsertionHandleController.hideAndDisallowAutomaticShowing(); + } + 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; + } + + 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 leftLength, int rightLength) { + if (mNativeImeAdapterAndroid == 0) return false; + nativeDeleteSurroundingText(mNativeImeAdapterAndroid, leftLength, rightLength); + return true; + } + + @VisibleForTesting + protected boolean setEditableSelectionOffsets(int start, int end) { + if (mNativeImeAdapterAndroid == 0) return false; + nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); + return true; + } + + void batchStateChanged(boolean isBegin) { + if (mNativeImeAdapterAndroid == 0) return; + nativeImeBatchStateChanged(mNativeImeAdapterAndroid, isBegin); + } + + 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; + } + + /** + * 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 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; + } + + @CalledByNative + private void cancelComposition() { + if (mInputConnection != null) { + mInputConnection.restartInput(); + } + } + + @CalledByNative + void detach() { + mNativeImeAdapterAndroid = 0; + mTextInputType = 0; + } + + private native boolean nativeSendSyntheticKeyEvent(int nativeImeAdapterAndroid, + int eventType, long timestampMs, int keyCode, int unicodeChar); + + private native boolean nativeSendKeyEvent(int nativeImeAdapterAndroid, KeyEvent event, + int action, int modifiers, long timestampMs, int keyCode, boolean isSystemKey, + int unicodeChar); + + private native void nativeSetComposingText(int nativeImeAdapterAndroid, String text, + int newCursorPosition); + + private native void nativeCommitText(int nativeImeAdapterAndroid, String text); + + private native void nativeAttachImeAdapter(int nativeImeAdapterAndroid); + + private native void nativeSetEditableSelectionOffsets(int nativeImeAdapterAndroid, + int start, int end); + + private native void nativeSetComposingRegion(int nativeImeAdapterAndroid, int start, int end); + + private native void nativeDeleteSurroundingText(int nativeImeAdapterAndroid, + int before, int after); + + private native void nativeImeBatchStateChanged(int nativeImeAdapterAndroid, boolean isBegin); + + private native void nativeUnselect(int nativeImeAdapterAndroid); + private native void nativeSelectAll(int nativeImeAdapterAndroid); + private native void nativeCut(int nativeImeAdapterAndroid); + private native void nativeCopy(int nativeImeAdapterAndroid); + private native void nativePaste(int nativeImeAdapterAndroid); +} diff --git a/content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java b/content/public/android/java/src/org/chromium/content/browser/input/InputDialogContainer.java index 466665d..11189c5 100644 --- a/content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/InputDialogContainer.java @@ -2,7 +2,7 @@ // 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.app.AlertDialog; import android.app.DatePickerDialog; @@ -18,8 +18,8 @@ import android.text.format.Time; import android.widget.DatePicker; import android.widget.TimePicker; -import org.chromium.content.browser.DateTimePickerDialog.OnDateTimeSetListener; -import org.chromium.content.browser.MonthPickerDialog.OnMonthSetListener; +import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetListener; +import org.chromium.content.browser.input.MonthPickerDialog.OnMonthSetListener; import org.chromium.content.R; import java.text.ParseException; @@ -27,7 +27,7 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; -class InputDialogContainer { +public class InputDialogContainer { interface InputActionDelegate { void cancelDateTimeDialog(); diff --git a/content/public/android/java/src/org/chromium/content/browser/InputMethodManagerWrapper.java b/content/public/android/java/src/org/chromium/content/browser/input/InputMethodManagerWrapper.java index 572582e..34b8b4e 100644 --- a/content/public/android/java/src/org/chromium/content/browser/InputMethodManagerWrapper.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/InputMethodManagerWrapper.java @@ -2,7 +2,7 @@ // 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.IBinder; @@ -13,7 +13,7 @@ import android.view.inputmethod.InputMethodManager; /** * Wrapper around Android's InputMethodManager */ -public class InputMethodManagerWrapper { +class InputMethodManagerWrapper { private final Context mContext; public InputMethodManagerWrapper(Context context) { diff --git a/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java b/content/public/android/java/src/org/chromium/content/browser/input/InsertionHandleController.java index 18fb7a4..e3ab838 100644 --- a/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/InsertionHandleController.java @@ -2,13 +2,12 @@ // 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.ClipboardManager; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; -import android.graphics.PointF; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -20,7 +19,7 @@ import android.widget.PopupWindow; /** * CursorController for inserting text at the cursor position. */ -abstract class InsertionHandleController implements CursorController { +public abstract class InsertionHandleController implements CursorController { /** The handle view, lazily created when first shown */ private HandleView mHandle; @@ -36,18 +35,18 @@ abstract class InsertionHandleController implements CursorController { private Context mContext; - InsertionHandleController(View parent) { + public InsertionHandleController(View parent) { mParent = parent; mContext = parent.getContext(); } /** Allows the handle to be shown automatically when cursor position changes */ - void allowAutomaticShowing() { + public void allowAutomaticShowing() { mAllowAutomaticShowing = true; } /** Disallows the handle from being shown automatically when cursor position changes */ - void hideAndDisallowAutomaticShowing() { + public void hideAndDisallowAutomaticShowing() { hide(); mAllowAutomaticShowing = false; } @@ -55,7 +54,7 @@ abstract class InsertionHandleController implements CursorController { /** * Shows the handle. */ - void showHandle() { + public void showHandle() { createHandleIfNeeded(); showHandleIfNeeded(); } @@ -66,13 +65,13 @@ abstract class InsertionHandleController implements CursorController { } } - void showHandleWithPastePopup() { + public void showHandleWithPastePopup() { showHandle(); showPastePopup(); } /** Shows the handle at the given coordinates, as long as automatic showing is allowed */ - void onCursorPositionChanged() { + public void onCursorPositionChanged() { if (mAllowAutomaticShowing) { showHandle(); } @@ -83,25 +82,21 @@ abstract class InsertionHandleController implements CursorController { * @param x Handle x in physical pixels. * @param y Handle y in physical pixels. */ - void setHandlePosition(int x, int y) { - mHandle.positionAt(x, y); - } - - void setHandlePosition(float x, float y) { - setHandlePosition((int) x, (int) y); + public void setHandlePosition(float x, float y) { + mHandle.positionAt((int) x, (int) y); } /** * If the handle is not visible, sets its visibility to View.VISIBLE and begins fading it in. */ - void beginHandleFadeIn() { + public void beginHandleFadeIn() { mHandle.beginFadeIn(); } /** * Sets the handle to the given visibility. */ - void setHandleVisibility(int visibility) { + public void setHandleVisibility(int visibility) { mHandle.setVisibility(visibility); } diff --git a/content/public/android/java/src/org/chromium/content/browser/MonthPicker.java b/content/public/android/java/src/org/chromium/content/browser/input/MonthPicker.java index 5c425d7..01e636e 100644 --- a/content/public/android/java/src/org/chromium/content/browser/MonthPicker.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/MonthPicker.java @@ -2,7 +2,7 @@ // 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.content.res.Configuration; diff --git a/content/public/android/java/src/org/chromium/content/browser/MonthPickerDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/MonthPickerDialog.java index cf8c770..354810e 100644 --- a/content/public/android/java/src/org/chromium/content/browser/MonthPickerDialog.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/MonthPickerDialog.java @@ -2,7 +2,7 @@ // 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.app.AlertDialog; import android.content.Context; @@ -12,7 +12,7 @@ import android.os.Build; import android.os.Bundle; import android.view.View; -import org.chromium.content.browser.MonthPicker.OnMonthChangedListener; +import org.chromium.content.browser.input.MonthPicker.OnMonthChangedListener; import org.chromium.content.R; public class MonthPickerDialog extends AlertDialog implements OnClickListener, diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectPopupDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/SelectPopupDialog.java index b5d00a5..d32266b 100644 --- a/content/public/android/java/src/org/chromium/content/browser/SelectPopupDialog.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/SelectPopupDialog.java @@ -2,7 +2,9 @@ // 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 org.chromium.content.browser.ContentViewCore; import android.app.AlertDialog; import android.content.DialogInterface; @@ -18,7 +20,7 @@ import android.widget.ListView; /** * Handles the popup dialog for the <select> HTML tag support. */ -class SelectPopupDialog { +public class SelectPopupDialog { // The currently showing popup dialog, null if none is showing. private static SelectPopupDialog sShownDialog; @@ -211,7 +213,7 @@ class SelectPopupDialog { } // The methods below are used by tests. - static SelectPopupDialog getCurrent() { + public static SelectPopupDialog getCurrent() { return sShownDialog; } } diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectionHandleController.java b/content/public/android/java/src/org/chromium/content/browser/input/SelectionHandleController.java index 8c5a969..f65c6a5 100644 --- a/content/public/android/java/src/org/chromium/content/browser/SelectionHandleController.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/SelectionHandleController.java @@ -2,15 +2,14 @@ // 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.graphics.PointF; import android.view.View; /** * CursorController for selecting a range of text. */ -abstract class SelectionHandleController implements CursorController { +public abstract class SelectionHandleController implements CursorController { // The following constants match the ones in // third_party/WebKit/Source/WebKit/chromium/public/WebTextDirection.h @@ -32,17 +31,17 @@ abstract class SelectionHandleController implements CursorController { private int mFixedHandleX; private int mFixedHandleY; - SelectionHandleController(View parent) { + public SelectionHandleController(View parent) { mParent = parent; } /** Automatically show selection anchors when text is selected. */ - void allowAutomaticShowing() { + public void allowAutomaticShowing() { mAllowAutomaticShowing = true; } /** Hide selection anchors, and don't automatically show them. */ - void hideAndDisallowAutomaticShowing() { + public void hideAndDisallowAutomaticShowing() { hide(); mAllowAutomaticShowing = false; } @@ -101,7 +100,7 @@ abstract class SelectionHandleController implements CursorController { /** * @return true iff this controller is being used to drag either the selection start or end. */ - boolean isDragging() { + public boolean isDragging() { return (mStartHandle != null && mStartHandle.isDragging()) || (mEndHandle != null && mEndHandle.isDragging()); } @@ -121,26 +120,8 @@ abstract class SelectionHandleController implements CursorController { * @param x The start handle position X in physical pixels. * @param y The start handle position Y in physical pixels. */ - void setStartHandlePosition(int x, int y) { - mStartHandle.positionAt(x, y); - } - - /** - * Moves the start handle so that it points at the given coordinates. - * @param x The start handle position X in physical pixels. - * @param y The start handle position Y in physical pixels. - */ - void setStartHandlePosition(float x, float y) { - setStartHandlePosition((int) x, (int) y); - } - - /** - * Moves the end handle so that it points at the given coordinates. - * @param x The end handle position X in physical pixels. - * @param y The end handle position Y in physical pixels. - */ - void setEndHandlePosition(int x, int y) { - mEndHandle.positionAt(x, y); + public void setStartHandlePosition(float x, float y) { + mStartHandle.positionAt((int) x, (int) y); } /** @@ -148,15 +129,15 @@ abstract class SelectionHandleController implements CursorController { * @param x The end handle position X in physical pixels. * @param y The end handle position Y in physical pixels. */ - void setEndHandlePosition(float x, float y) { - setEndHandlePosition((int) x, (int) y); + public void setEndHandlePosition(float x, float y) { + mEndHandle.positionAt((int) x, (int) y); } /** * If the handles are not visible, sets their visibility to View.VISIBLE and begins fading them * in. */ - void beginHandleFadeIn() { + public void beginHandleFadeIn() { mStartHandle.beginFadeIn(); mEndHandle.beginFadeIn(); } @@ -164,7 +145,7 @@ abstract class SelectionHandleController implements CursorController { /** * Sets the start and end handles to the given visibility. */ - void setHandleVisibility(int visibility) { + public void setHandleVisibility(int visibility) { mStartHandle.setVisibility(visibility); mEndHandle.setVisibility(visibility); } @@ -175,7 +156,7 @@ abstract class SelectionHandleController implements CursorController { * @param startDir Direction (left/right) of start handle. * @param endDir Direction (left/right) of end handle. */ - void onSelectionChanged(int startDir, int endDir) { + public void onSelectionChanged(int startDir, int endDir) { if (mAllowAutomaticShowing) { showHandles(startDir, endDir); } @@ -189,7 +170,7 @@ abstract class SelectionHandleController implements CursorController { * @param startDir Direction (left/right) of start handle. * @param endDir Direction (left/right) of end handle. */ - void showHandles(int startDir, int endDir) { + public void showHandles(int startDir, int endDir) { createHandlesIfNeeded(startDir, endDir); showHandlesIfNeeded(); } diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java index 76d23e7..a49cddb 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ImeTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java @@ -2,7 +2,7 @@ // 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.app.Activity; import android.content.ClipData; @@ -20,7 +20,8 @@ import android.view.inputmethod.InputConnection; import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.UrlUtils; -import org.chromium.content.browser.ImeAdapter.AdapterInputConnection; +import org.chromium.content.browser.ContentView; +import org.chromium.content.browser.ContentViewCore; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.DOMUtils; @@ -31,8 +32,6 @@ import java.util.concurrent.Callable; public class ImeTest extends ContentShellTestBase { - private static final int INVALID_SELECTION = -2; - private static final int INVALID_COMPOSITION = -2; private static final String DATA_URL = UrlUtils.encodeHtmlDataUri( "<html><head><meta name=\"viewport\"" + "content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\" /></head>" + @@ -406,7 +405,7 @@ public class ImeTest extends ContentShellTestBase { return getContentViewCore().getImeAdapterForTest(); } - private ImeAdapter.AdapterInputConnection getAdapterInputConnection() { + private AdapterInputConnection getAdapterInputConnection() { return getContentViewCore().getInputConnectionForTest(); } @@ -419,7 +418,7 @@ public class ImeTest extends ContentShellTestBase { } } - private static class TestAdapterInputConnection extends ImeAdapter.AdapterInputConnection { + private static class TestAdapterInputConnection extends AdapterInputConnection { private int mSetEditableTextCallCounter = 0; private int mUpdateSelectionCounter = 0; private String mText; diff --git a/content/public/android/javatests/src/org/chromium/content/browser/InsertionHandleTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/InsertionHandleTest.java index 4f519f0..42ee232 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/InsertionHandleTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/InsertionHandleTest.java @@ -2,7 +2,7 @@ // 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.ClipData; import android.content.ClipboardManager; @@ -14,6 +14,8 @@ import android.text.Selection; import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; +import org.chromium.content.browser.ContentView; +import org.chromium.content.browser.RenderCoordinates; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.DOMUtils; diff --git a/content/public/android/javatests/src/org/chromium/content/browser/SelectPopupTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java index 81e03c1..7e65a25 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/SelectPopupTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java @@ -2,13 +2,14 @@ // 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.test.suitebuilder.annotation.LargeTest; import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.UrlUtils; +import org.chromium.content.browser.ContentView; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.DOMUtils; |