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 |