| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.content.browser; | 5 package org.chromium.content.browser; |
| 6 | 6 |
| 7 import android.content.Context; | 7 import android.content.Context; |
| 8 import android.os.Handler; | 8 import android.os.Handler; |
| 9 import android.os.ResultReceiver; | 9 import android.os.ResultReceiver; |
| 10 import android.text.Editable; | 10 import android.text.Editable; |
| 11 import android.text.InputType; | 11 import android.text.InputType; |
| 12 import android.text.Selection; | 12 import android.text.Selection; |
| 13 import android.view.KeyEvent; | 13 import android.view.KeyEvent; |
| 14 import android.view.View; | 14 import android.view.View; |
| 15 import android.view.inputmethod.BaseInputConnection; | 15 import android.view.inputmethod.BaseInputConnection; |
| 16 import android.view.inputmethod.EditorInfo; | 16 import android.view.inputmethod.EditorInfo; |
| 17 import android.view.inputmethod.ExtractedText; | 17 import android.view.inputmethod.ExtractedText; |
| 18 import android.view.inputmethod.ExtractedTextRequest; | 18 import android.view.inputmethod.ExtractedTextRequest; |
| 19 import android.view.inputmethod.InputMethodManager; | 19 import android.view.inputmethod.InputMethodManager; |
| 20 | 20 |
| 21 import org.chromium.base.CalledByNative; | 21 import org.chromium.base.CalledByNative; |
| 22 import org.chromium.base.JNINamespace; | 22 import org.chromium.base.JNINamespace; |
| 23 | 23 |
| 24 // We have to adapt and plumb android IME service and chrome text input API. | 24 /** |
| 25 // ImeAdapter provides an interface in both ways native <-> java: | 25 We have to adapt and plumb android IME service and chrome text input API. |
| 26 // 1. InputConnectionAdapter notifies native code of text composition state and | 26 ImeAdapter provides an interface in both ways native <-> java: |
| 27 // dispatch key events from java -> WebKit. | 27 1. InputConnectionAdapter notifies native code of text composition state and |
| 28 // 2. Native ImeAdapter notifies java side to clear composition text. | 28 dispatch key events from java -> WebKit. |
| 29 // | 29 2. Native ImeAdapter notifies java side to clear composition text. |
| 30 // The basic flow is: | 30 |
| 31 // 1. Intercept dispatchKeyEventPreIme() to record the current key event, but do | 31 The basic flow is: |
| 32 // nothing else. | 32 1. Intercept dispatchKeyEventPreIme() to record the current key event, but do |
| 33 // 2. When InputConnectionAdapter gets called with composition or result text: | 33 nothing else. |
| 34 // a) If a key event has been recorded in dispatchKeyEventPreIme() and we | 34 2. When InputConnectionAdapter gets called with composition or result text: |
| 35 // receive a result text with single character, then we probably need to | 35 a) If a key event has been recorded in dispatchKeyEventPreIme() and we |
| 36 // send the result text as a Char event rather than a ConfirmComposition | 36 receive a result text with single character, then we probably need to |
| 37 // event. So we need to dispatch the recorded key event followed by a | 37 send the result text as a Char event rather than a ConfirmComposition |
| 38 // synthetic Char event. | 38 event. So we need to dispatch the recorded key event followed by a |
| 39 // b) If we receive a composition text or a result text with more than one | 39 synthetic Char event. |
| 40 // characters, then no matter if we recorded a key event or not in | 40 b) If we receive a composition text or a result text with more than one |
| 41 // dispatchKeyEventPreIme(), we just need to dispatch a synthetic key | 41 characters, then no matter if we recorded a key event or not in |
| 42 // event with special keycode 229, and then dispatch the composition or | 42 dispatchKeyEventPreIme(), we just need to dispatch a synthetic key |
| 43 // result text. | 43 event with special keycode 229, and then dispatch the composition or |
| 44 // 3. Intercept dispatchKeyEvent() method for key events not handled by IME, we | 44 result text. |
| 45 // need to dispatch them to webkit and check webkit's reply. Then inject a | 45 3. Intercept dispatchKeyEvent() method for key events not handled by IME, we |
| 46 // new key event for further processing if webkit didn't handle it. | 46 need to dispatch them to WebKit and check webkit's reply. Then inject a |
| 47 new key event for further processing if WebKit didn't handle it. |
| 48 */ |
| 47 @JNINamespace("content") | 49 @JNINamespace("content") |
| 48 class ImeAdapter { | 50 class ImeAdapter { |
| 49 interface ViewEmbedder { | 51 interface ViewEmbedder { |
| 50 /** | 52 /** |
| 51 * @param isFinish whether the event is occuring because input is finish
ed. | 53 * @param isFinish whether the event is occurring because input is finis
hed. |
| 52 */ | 54 */ |
| 53 public void onImeEvent(boolean isFinish); | 55 public void onImeEvent(boolean isFinish); |
| 54 public void onSetFieldValue(); | 56 public void onSetFieldValue(); |
| 55 public void onDismissInput(); | 57 public void onDismissInput(); |
| 56 public View getAttachedView(); | 58 public View getAttachedView(); |
| 57 public ResultReceiver getNewShowKeyboardReceiver(); | 59 public ResultReceiver getNewShowKeyboardReceiver(); |
| 58 } | 60 } |
| 59 | 61 |
| 60 static final int COMPOSITION_KEY_CODE = 229; | 62 static final int COMPOSITION_KEY_CODE = 229; |
| 61 | 63 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 104 sTextInputTypeText = textInputTypeText; | 106 sTextInputTypeText = textInputTypeText; |
| 105 sTextInputTypeTextArea = textInputTypeTextArea; | 107 sTextInputTypeTextArea = textInputTypeTextArea; |
| 106 sTextInputTypePassword = textInputTypePassword; | 108 sTextInputTypePassword = textInputTypePassword; |
| 107 sTextInputTypeSearch = textInputTypeSearch; | 109 sTextInputTypeSearch = textInputTypeSearch; |
| 108 sTextInputTypeUrl = textInputTypeUrl; | 110 sTextInputTypeUrl = textInputTypeUrl; |
| 109 sTextInputTypeEmail = textInputTypeEmail; | 111 sTextInputTypeEmail = textInputTypeEmail; |
| 110 sTextInputTypeTel = textInputTypeTel; | 112 sTextInputTypeTel = textInputTypeTel; |
| 111 sTextInputTypeNumber = textInputTypeNumber; | 113 sTextInputTypeNumber = textInputTypeNumber; |
| 112 sTextInputTypeWeek = textInputTypeWeek; | 114 sTextInputTypeWeek = textInputTypeWeek; |
| 113 sTextInputTypeContentEditable = textInputTypeContentEditable; | 115 sTextInputTypeContentEditable = textInputTypeContentEditable; |
| 114 InputDialogContainer.initializeInputTypes(textInputTypeDate, textInputTy
peDateTime, | |
| 115 textInputTypeDateTimeLocal, textInputTypeMonth, textInputTypeTim
e); | |
| 116 } | 116 } |
| 117 | 117 |
| 118 private int mNativeImeAdapterAndroid; | 118 private int mNativeImeAdapterAndroid; |
| 119 private int mTextInputType; | 119 private int mTextInputType; |
| 120 private int mPreImeEventCount; | 120 private int mPreImeEventCount; |
| 121 | 121 |
| 122 private Context mContext; | 122 private Context mContext; |
| 123 private SelectionHandleController mSelectionHandleController; | 123 private SelectionHandleController mSelectionHandleController; |
| 124 private InsertionHandleController mInsertionHandleController; | 124 private InsertionHandleController mInsertionHandleController; |
| 125 private AdapterInputConnection mInputConnection; | 125 private AdapterInputConnection mInputConnection; |
| 126 private ViewEmbedder mViewEmbedder; | 126 private ViewEmbedder mViewEmbedder; |
| 127 private Handler mHandler; | 127 private Handler mHandler; |
| 128 private InputDialogContainer mInputDialogContainer; | |
| 129 | 128 |
| 130 private class DelayedDismissInput implements Runnable { | 129 private class DelayedDismissInput implements Runnable { |
| 131 private int mNativeImeAdapter; | 130 private int mNativeImeAdapter; |
| 132 | 131 |
| 133 DelayedDismissInput(int nativeImeAdapter) { | 132 DelayedDismissInput(int nativeImeAdapter) { |
| 134 mNativeImeAdapter = nativeImeAdapter; | 133 mNativeImeAdapter = nativeImeAdapter; |
| 135 } | 134 } |
| 136 | 135 |
| 137 @Override | 136 @Override |
| 138 public void run() { | 137 public void run() { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 151 private static final int INPUT_DISMISS_DELAY = 150; | 150 private static final int INPUT_DISMISS_DELAY = 150; |
| 152 | 151 |
| 153 ImeAdapter(Context context, SelectionHandleController selectionHandleControl
ler, | 152 ImeAdapter(Context context, SelectionHandleController selectionHandleControl
ler, |
| 154 InsertionHandleController insertionHandleController, ViewEmbedder em
bedder) { | 153 InsertionHandleController insertionHandleController, ViewEmbedder em
bedder) { |
| 155 mPreImeEventCount = 0; | 154 mPreImeEventCount = 0; |
| 156 mContext = context; | 155 mContext = context; |
| 157 mSelectionHandleController = selectionHandleController; | 156 mSelectionHandleController = selectionHandleController; |
| 158 mInsertionHandleController = insertionHandleController; | 157 mInsertionHandleController = insertionHandleController; |
| 159 mViewEmbedder = embedder; | 158 mViewEmbedder = embedder; |
| 160 mHandler = new Handler(); | 159 mHandler = new Handler(); |
| 161 mInputDialogContainer = new InputDialogContainer(context, | |
| 162 new InputDialogContainer.InputActionDelegate() { | |
| 163 | |
| 164 @Override | |
| 165 public void replaceDateTime(String text) { | |
| 166 mViewEmbedder.onSetFieldValue(); | |
| 167 nativeReplaceDateTime(mNativeImeAdapterAndroid, text); | |
| 168 } | |
| 169 | |
| 170 @Override | |
| 171 public void cancelDateTimeDialog() { | |
| 172 nativeCancelDialog(mNativeImeAdapterAndroid); | |
| 173 } | |
| 174 }); | |
| 175 } | 160 } |
| 176 | 161 |
| 177 boolean isFor(int nativeImeAdapter, int textInputType) { | 162 boolean isFor(int nativeImeAdapter, int textInputType) { |
| 178 return mNativeImeAdapterAndroid == nativeImeAdapter && | 163 return mNativeImeAdapterAndroid == nativeImeAdapter && |
| 179 mTextInputType == textInputType; | 164 mTextInputType == textInputType; |
| 180 } | 165 } |
| 181 | 166 |
| 182 void attachAndShowIfNeeded(int nativeImeAdapter, int textInputType, | 167 void attachAndShowIfNeeded(int nativeImeAdapter, int textInputType, |
| 183 String text, boolean showIfNeeded) { | 168 String text, boolean showIfNeeded) { |
| 184 mHandler.removeCallbacks(mDismissInput); | 169 mHandler.removeCallbacks(mDismissInput); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 197 mHandler.postDelayed(mDismissInput, INPUT_DISMISS_DELAY); | 182 mHandler.postDelayed(mDismissInput, INPUT_DISMISS_DELAY); |
| 198 return; | 183 return; |
| 199 } | 184 } |
| 200 | 185 |
| 201 int previousType = mTextInputType; | 186 int previousType = mTextInputType; |
| 202 attach(nativeImeAdapter, textInputType); | 187 attach(nativeImeAdapter, textInputType); |
| 203 | 188 |
| 204 InputMethodManager manager = (InputMethodManager) | 189 InputMethodManager manager = (InputMethodManager) |
| 205 mContext.getSystemService(Context.INPUT_METHOD_SERVICE); | 190 mContext.getSystemService(Context.INPUT_METHOD_SERVICE); |
| 206 | 191 |
| 207 if (hasTextInputType()) { | 192 manager.restartInput(mViewEmbedder.getAttachedView()); |
| 208 manager.restartInput(mViewEmbedder.getAttachedView()); | 193 if (showIfNeeded) { |
| 209 // If type has changed from dialog to text, show even if showIfN
eeded is not true. | 194 showKeyboard(); |
| 210 if (showIfNeeded || mInputDialogContainer.isDialogShowing()) { | |
| 211 showKeyboard(); | |
| 212 } | |
| 213 } else if (hasDialogInputType()) { | |
| 214 // If type has changed from text to dialog, show even if showIfN
eeded is not true. | |
| 215 if (showIfNeeded || isTextInputType(previousType)) { | |
| 216 // Make sure the keyboard is dismissed before displaying the
dialog. | |
| 217 dismissInput(false); | |
| 218 mInsertionHandleController.hideAndDisallowAutomaticShowing()
; | |
| 219 mInputDialogContainer.showDialog(text, textInputType); | |
| 220 } | |
| 221 } | 195 } |
| 222 } else if (hasInputType()) { | 196 } else if (hasInputType()) { |
| 223 if (!mInputDialogContainer.isDialogShowing() && showIfNeeded) { | 197 showKeyboard(); |
| 224 if (hasDialogInputType()) { | |
| 225 mInsertionHandleController.hideAndDisallowAutomaticShowing()
; | |
| 226 mInputDialogContainer.showDialog(text, textInputType); | |
| 227 } else { | |
| 228 showKeyboard(); | |
| 229 } | |
| 230 } | |
| 231 } | 198 } |
| 232 } | 199 } |
| 233 | 200 |
| 234 void attach(int nativeImeAdapter, int textInputType) { | 201 void attach(int nativeImeAdapter, int textInputType) { |
| 235 mNativeImeAdapterAndroid = nativeImeAdapter; | 202 mNativeImeAdapterAndroid = nativeImeAdapter; |
| 236 mTextInputType = textInputType; | 203 mTextInputType = textInputType; |
| 237 nativeAttachImeAdapter(mNativeImeAdapterAndroid); | 204 nativeAttachImeAdapter(mNativeImeAdapterAndroid); |
| 238 } | 205 } |
| 239 | 206 |
| 240 /** | 207 /** |
| (...skipping 10 matching lines...) Expand all Loading... |
| 251 | 218 |
| 252 /** | 219 /** |
| 253 * Used to check whether the native counterpart of the ImeAdapter has been a
ttached yet. | 220 * Used to check whether the native counterpart of the ImeAdapter has been a
ttached yet. |
| 254 * @return Whether native ImeAdapter has been attached and its pointer is cu
rrently nonzero. | 221 * @return Whether native ImeAdapter has been attached and its pointer is cu
rrently nonzero. |
| 255 */ | 222 */ |
| 256 boolean isNativeImeAdapterAttached() { | 223 boolean isNativeImeAdapterAttached() { |
| 257 return mNativeImeAdapterAndroid != 0; | 224 return mNativeImeAdapterAndroid != 0; |
| 258 } | 225 } |
| 259 | 226 |
| 260 private void showKeyboard() { | 227 private void showKeyboard() { |
| 261 mInputDialogContainer.dismissDialog(); | |
| 262 InputMethodManager manager = (InputMethodManager) | 228 InputMethodManager manager = (InputMethodManager) |
| 263 mContext.getSystemService(Context.INPUT_METHOD_SERVICE); | 229 mContext.getSystemService(Context.INPUT_METHOD_SERVICE); |
| 264 manager.showSoftInput(mViewEmbedder.getAttachedView(), 0, | 230 manager.showSoftInput(mViewEmbedder.getAttachedView(), 0, |
| 265 mViewEmbedder.getNewShowKeyboardReceiver()); | 231 mViewEmbedder.getNewShowKeyboardReceiver()); |
| 266 } | 232 } |
| 267 | 233 |
| 268 private void dismissInput(boolean unzoomIfNeeded) { | 234 private void dismissInput(boolean unzoomIfNeeded) { |
| 269 hideKeyboard(unzoomIfNeeded); | 235 hideKeyboard(unzoomIfNeeded); |
| 270 mViewEmbedder.onDismissInput(); | 236 mViewEmbedder.onDismissInput(); |
| 271 } | 237 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 291 } | 257 } |
| 292 | 258 |
| 293 static boolean isTextInputType(int type) { | 259 static boolean isTextInputType(int type) { |
| 294 return type != sTextInputTypeNone && !InputDialogContainer.isDialogInput
Type(type); | 260 return type != sTextInputTypeNone && !InputDialogContainer.isDialogInput
Type(type); |
| 295 } | 261 } |
| 296 | 262 |
| 297 boolean hasTextInputType() { | 263 boolean hasTextInputType() { |
| 298 return isTextInputType(mTextInputType); | 264 return isTextInputType(mTextInputType); |
| 299 } | 265 } |
| 300 | 266 |
| 301 boolean hasDialogInputType() { | |
| 302 return InputDialogContainer.isDialogInputType(mTextInputType); | |
| 303 } | |
| 304 | |
| 305 void dispatchKeyEventPreIme(KeyEvent event) { | 267 void dispatchKeyEventPreIme(KeyEvent event) { |
| 306 // We only register that a key was pressed, but we don't actually interc
ept | 268 // We only register that a key was pressed, but we don't actually interc
ept |
| 307 // it. | 269 // it. |
| 308 ++mPreImeEventCount; | 270 ++mPreImeEventCount; |
| 309 } | 271 } |
| 310 | 272 |
| 311 boolean dispatchKeyEvent(KeyEvent event) { | 273 boolean dispatchKeyEvent(KeyEvent event) { |
| 312 mPreImeEventCount = 0; | 274 mPreImeEventCount = 0; |
| 313 return translateAndSendNativeEvents(event); | 275 return translateAndSendNativeEvents(event); |
| 314 } | 276 } |
| (...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 771 int action, int modifiers, long timestampMs, int keyCode, boolean is
SystemKey, | 733 int action, int modifiers, long timestampMs, int keyCode, boolean is
SystemKey, |
| 772 int unicodeChar); | 734 int unicodeChar); |
| 773 | 735 |
| 774 private native void nativeSetComposingText(int nativeImeAdapterAndroid, Stri
ng text, | 736 private native void nativeSetComposingText(int nativeImeAdapterAndroid, Stri
ng text, |
| 775 int newCursorPosition); | 737 int newCursorPosition); |
| 776 | 738 |
| 777 private native void nativeCommitText(int nativeImeAdapterAndroid, String tex
t); | 739 private native void nativeCommitText(int nativeImeAdapterAndroid, String tex
t); |
| 778 | 740 |
| 779 private native void nativeAttachImeAdapter(int nativeImeAdapterAndroid); | 741 private native void nativeAttachImeAdapter(int nativeImeAdapterAndroid); |
| 780 | 742 |
| 781 private native void nativeReplaceDateTime(int nativeImeAdapterAndroid, Strin
g text); | |
| 782 | |
| 783 private native void nativeCancelDialog(int nativeImeAdapterAndroid); | |
| 784 | |
| 785 private native void nativeSetEditableSelectionOffsets(int nativeImeAdapterAn
droid, | 743 private native void nativeSetEditableSelectionOffsets(int nativeImeAdapterAn
droid, |
| 786 int start, int end); | 744 int start, int end); |
| 787 | 745 |
| 788 private native void nativeSetComposingRegion(int nativeImeAdapterAndroid, in
t start, int end); | 746 private native void nativeSetComposingRegion(int nativeImeAdapterAndroid, in
t start, int end); |
| 789 | 747 |
| 790 private native void nativeDeleteSurroundingText(int nativeImeAdapterAndroid, | 748 private native void nativeDeleteSurroundingText(int nativeImeAdapterAndroid, |
| 791 int before, int after); | 749 int before, int after); |
| 792 | 750 |
| 793 private native void nativeUnselect(int nativeImeAdapterAndroid); | 751 private native void nativeUnselect(int nativeImeAdapterAndroid); |
| 794 private native void nativeSelectAll(int nativeImeAdapterAndroid); | 752 private native void nativeSelectAll(int nativeImeAdapterAndroid); |
| 795 private native void nativeCut(int nativeImeAdapterAndroid); | 753 private native void nativeCut(int nativeImeAdapterAndroid); |
| 796 private native void nativeCopy(int nativeImeAdapterAndroid); | 754 private native void nativeCopy(int nativeImeAdapterAndroid); |
| 797 private native void nativePaste(int nativeImeAdapterAndroid); | 755 private native void nativePaste(int nativeImeAdapterAndroid); |
| 798 } | 756 } |
| OLD | NEW |