Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java

Issue 1278593004: Introduce ThreadedInputConnection behind a switch (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: added comment for call stack check Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 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.input; 5 package org.chromium.content.browser.input;
6 6
7 import android.content.res.Configuration; 7 import android.content.res.Configuration;
8 import android.os.ResultReceiver; 8 import android.os.ResultReceiver;
9 import android.os.SystemClock; 9 import android.os.SystemClock;
10 import android.text.Editable;
11 import android.text.Selection;
12 import android.text.SpannableString; 10 import android.text.SpannableString;
13 import android.text.TextUtils; 11 import android.text.TextUtils;
14 import android.text.style.BackgroundColorSpan; 12 import android.text.style.BackgroundColorSpan;
15 import android.text.style.CharacterStyle; 13 import android.text.style.CharacterStyle;
16 import android.text.style.UnderlineSpan; 14 import android.text.style.UnderlineSpan;
17 import android.view.KeyCharacterMap; 15 import android.view.KeyCharacterMap;
18 import android.view.KeyEvent; 16 import android.view.KeyEvent;
19 import android.view.View; 17 import android.view.View;
20 import android.view.inputmethod.BaseInputConnection; 18 import android.view.inputmethod.BaseInputConnection;
21 import android.view.inputmethod.EditorInfo; 19 import android.view.inputmethod.EditorInfo;
22 20
21 import org.chromium.base.CommandLine;
23 import org.chromium.base.Log; 22 import org.chromium.base.Log;
24 import org.chromium.base.VisibleForTesting; 23 import org.chromium.base.VisibleForTesting;
25 import org.chromium.base.annotations.CalledByNative; 24 import org.chromium.base.annotations.CalledByNative;
26 import org.chromium.base.annotations.JNINamespace; 25 import org.chromium.base.annotations.JNINamespace;
27 import org.chromium.blink_public.web.WebInputEventModifier; 26 import org.chromium.blink_public.web.WebInputEventModifier;
28 import org.chromium.blink_public.web.WebInputEventType; 27 import org.chromium.blink_public.web.WebInputEventType;
28 import org.chromium.content.common.ContentSwitches;
29 import org.chromium.ui.base.ime.TextInputType; 29 import org.chromium.ui.base.ime.TextInputType;
30 import org.chromium.ui.picker.InputDialogContainer; 30 import org.chromium.ui.picker.InputDialogContainer;
31 31
32 /** 32 /**
33 * Adapts and plumbs android IME service onto the chrome text input API. 33 * Adapts and plumbs android IME service onto the chrome text input API.
34 * ImeAdapter provides an interface in both ways native <-> java: 34 * ImeAdapter provides an interface in both ways native <-> java:
35 * 1. InputConnectionAdapter notifies native code of text composition state and 35 * 1. InputConnectionAdapter notifies native code of text composition state and
36 * dispatch key events from java -> WebKit. 36 * dispatch key events from java -> WebKit.
37 * 2. Native ImeAdapter notifies java side to clear composition text. 37 * 2. Native ImeAdapter notifies java side to clear composition text.
38 * 38 *
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 * @return Object that should be called for all keyboard show and hide r equests. 85 * @return Object that should be called for all keyboard show and hide r equests.
86 */ 86 */
87 ResultReceiver getNewShowKeyboardReceiver(); 87 ResultReceiver getNewShowKeyboardReceiver();
88 } 88 }
89 89
90 static char[] sSingleCharArray = new char[1]; 90 static char[] sSingleCharArray = new char[1];
91 static KeyCharacterMap sKeyCharacterMap; 91 static KeyCharacterMap sKeyCharacterMap;
92 92
93 private long mNativeImeAdapterAndroid; 93 private long mNativeImeAdapterAndroid;
94 private InputMethodManagerWrapper mInputMethodManagerWrapper; 94 private InputMethodManagerWrapper mInputMethodManagerWrapper;
95 private AdapterInputConnection mInputConnection; 95 private ChromiumBaseInputConnection mInputConnection;
96 private AdapterInputConnectionFactory mInputConnectionFactory; 96 private ChromiumBaseInputConnection.Factory mInputConnectionFactory;
97
97 private final ImeAdapterDelegate mViewEmbedder; 98 private final ImeAdapterDelegate mViewEmbedder;
98 99
99 // This holds the state of editable text (e.g. contents of <input>, contente ditable) of
100 // a focused element.
101 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
102 // state must be reflected to this to keep consistency.
103 private final Editable mEditable;
104
105 private int mTextInputType = TextInputType.NONE; 100 private int mTextInputType = TextInputType.NONE;
106 private int mTextInputFlags; 101 private int mTextInputFlags;
107 102
108 // Keep the current configuration to detect the change when onConfigurationC hanged() is called. 103 // Keep the current configuration to detect the change when onConfigurationC hanged() is called.
109 private Configuration mCurrentConfig; 104 private Configuration mCurrentConfig;
110 105
106 private int mLastSelectionStart;
107 private int mLastSelectionEnd;
108
111 /** 109 /**
112 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to 110 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to
113 * InputMethodManager. 111 * InputMethodManager.
114 * @param embedder The view that is used for callbacks from ImeAdapter. 112 * @param embedder The view that is used for callbacks from ImeAdapter.
115 */ 113 */
116 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { 114 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) {
117 mInputMethodManagerWrapper = wrapper; 115 mInputMethodManagerWrapper = wrapper;
118 mViewEmbedder = embedder; 116 mViewEmbedder = embedder;
119 mInputConnectionFactory = new AdapterInputConnectionFactory(); 117 resetInputConnectionFactory();
120 mEditable = Editable.Factory.getInstance().newEditable("");
121 Selection.setSelection(mEditable, 0);
122 // Deep copy newConfig so that we can notice the difference. 118 // Deep copy newConfig so that we can notice the difference.
123 mCurrentConfig = new Configuration( 119 mCurrentConfig = new Configuration(
124 mViewEmbedder.getAttachedView().getResources().getConfiguration( )); 120 mViewEmbedder.getAttachedView().getResources().getConfiguration( ));
125 } 121 }
126 122
127 /** 123 void resetInputConnectionFactory() {
128 * Default factory for AdapterInputConnection classes. 124 if (shouldUseImeThread()) {
129 */ 125 mInputConnectionFactory =
130 static class AdapterInputConnectionFactory { 126 new ThreadedInputConnectionFactory(mInputMethodManagerWrappe r);
131 AdapterInputConnection get(View view, ImeAdapter imeAdapter, int initial SelStart, 127 } else {
132 int initialSelEnd, EditorInfo outAttrs) { 128 mInputConnectionFactory = new ReplicaInputConnection.Factory();
133 return new AdapterInputConnection(
134 view, imeAdapter, initialSelStart, initialSelEnd, outAttrs);
135 } 129 }
136 } 130 }
137 131
132 private boolean shouldUseImeThread() {
133 if (CommandLine.getInstance().hasSwitch(ContentSwitches.DISABLE_IME_THRE AD)) {
134 return false;
135 }
136 if (CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_IME_THREA D)) {
137 return true;
138 }
139 return false;
140 }
141
138 /** 142 /**
139 * @see View#onCreateInputConnection(EditorInfo) 143 * @see View#onCreateInputConnection(EditorInfo)
140 */ 144 */
141 public AdapterInputConnection onCreateInputConnection(EditorInfo outAttrs) { 145 public ChromiumBaseInputConnection onCreateInputConnection(EditorInfo outAtt rs) {
146 // InputMethodService evaluates fullscreen mode even when the new input connection is
147 // null. This makes sure IME doesn't enter fullscreen mode or open custo m UI.
148 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME _FLAG_NO_EXTRACT_UI;
142 // Without this line, some third-party IMEs will try to compose text eve n when 149 // Without this line, some third-party IMEs will try to compose text eve n when
143 // not on an editable node. Even when we return null here, key events ca n still go through 150 // not on an editable node. Even when we return null here, key events ca n still go
144 // ImeAdapter#dispatchKeyEvent(). 151 // through ImeAdapter#dispatchKeyEvent().
145 if (mTextInputType == TextInputType.NONE) { 152 if (mTextInputType == TextInputType.NONE) {
153 // Unblock if view loses focus, no input form or content editable is focused, or render
154 // crashes, or navigates to another page, etc.
no sievers 2016/02/24 20:08:24 So how is this triggered for the less obvious case
Changwan Ryu 2016/02/24 23:03:24 hideSoftInput or restartInput with null text input
155 // Even when InputConnection methods are blocked IMM can still call this.
156 if (mInputConnection != null) mInputConnection.unblockOnUiThread();
146 mInputConnection = null; 157 mInputConnection = null;
147 if (DEBUG_LOGS) Log.w(TAG, "onCreateInputConnection returns null."); 158 if (DEBUG_LOGS) Log.w(TAG, "onCreateInputConnection returns null.");
148 // InputMethodService evaluates fullscreen mode even when the new in put connection is
149 // null. This makes sure IME doesn't enter fullscreen mode or open c ustom UI.
150 outAttrs.imeOptions =
151 EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_NO_E XTRACT_UI;
152 return null; 159 return null;
153 } 160 }
154 161 mInputConnection = mInputConnectionFactory.initializeAndGet(
155 if (!isTextInputType(mTextInputType)) { 162 mViewEmbedder.getAttachedView(), this, mTextInputType, mTextInpu tFlags,
156 // Although onCheckIsTextEditor will return false in this case, the EditorInfo 163 mLastSelectionStart, mLastSelectionEnd, outAttrs);
157 // is still used by the InputMethodService. Need to make sure the IM E doesn't 164 if (DEBUG_LOGS) Log.w(TAG, "onCreateInputConnection: " + mInputConnectio n);
158 // enter fullscreen mode.
159 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
160 }
161 int initialSelStart = Selection.getSelectionStart(mEditable);
162 int initialSelEnd = outAttrs.initialSelEnd = Selection.getSelectionEnd(m Editable);
163 mInputConnection = mInputConnectionFactory.get(
164 mViewEmbedder.getAttachedView(), this, initialSelStart, initialS elEnd, outAttrs);
165 if (DEBUG_LOGS) Log.w(TAG, "onCreateInputConnection");
166 return mInputConnection; 165 return mInputConnection;
167 } 166 }
168 167
169 /** 168 /**
170 * Overrides the InputMethodManagerWrapper that ImeAdapter uses to make call s to 169 * Overrides the InputMethodManagerWrapper that ImeAdapter uses to make call s to
171 * InputMethodManager. 170 * InputMethodManager.
172 * @param immw InputMethodManagerWrapper that should be used to call InputMe thodManager. 171 * @param immw InputMethodManagerWrapper that should be used to call InputMe thodManager.
173 */ 172 */
174 @VisibleForTesting 173 @VisibleForTesting
175 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper im mw) { 174 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper im mw) {
176 mInputMethodManagerWrapper = immw; 175 mInputMethodManagerWrapper = immw;
177 } 176 }
178 177
179 @VisibleForTesting 178 @VisibleForTesting
180 void setInputConnectionFactory(AdapterInputConnectionFactory factory) { 179 void setInputConnectionFactory(ChromiumBaseInputConnection.Factory factory) {
181 mInputConnectionFactory = factory; 180 mInputConnectionFactory = factory;
182 } 181 }
183 182
184 /** 183 @VisibleForTesting
185 * Set the current active InputConnection when a new InputConnection is cons tructed. 184 ChromiumBaseInputConnection.Factory getInputConnectionFactoryForTest() {
186 * @param inputConnection The input connection that is currently used with I ME. 185 return mInputConnectionFactory;
187 */
188 void setInputConnection(AdapterInputConnection inputConnection) {
189 mInputConnection = inputConnection;
190 } 186 }
191 187
192 /** 188 /**
193 * Get the current input connection for testing purposes. 189 * Get the current input connection for testing purposes.
194 */ 190 */
195 @VisibleForTesting 191 @VisibleForTesting
196 public AdapterInputConnection getInputConnectionForTest() { 192 public ChromiumBaseInputConnection getInputConnectionForTest() {
197 return mInputConnection; 193 return mInputConnection;
198 } 194 }
199 195
200 /**
201 * @return The Editable instance that will be shared across AdapterInputConn ection instances.
202 */
203 Editable getEditable() {
204 return mEditable;
205 }
206
207 /**
208 * Should be used only by AdapterInputConnection.
209 * @return The input type of currently focused element.
210 */
211 int getTextInputType() {
212 return mTextInputType;
213 }
214
215 /**
216 * Should be used only by AdapterInputConnection.
217 * @return The input flags of the currently focused element.
218 */
219 int getTextInputFlags() {
220 return mTextInputFlags;
221 }
222
223 private static int getModifiers(int metaState) { 196 private static int getModifiers(int metaState) {
224 int modifiers = 0; 197 int modifiers = 0;
225 if ((metaState & KeyEvent.META_SHIFT_ON) != 0) { 198 if ((metaState & KeyEvent.META_SHIFT_ON) != 0) {
226 modifiers |= WebInputEventModifier.ShiftKey; 199 modifiers |= WebInputEventModifier.ShiftKey;
227 } 200 }
228 if ((metaState & KeyEvent.META_ALT_ON) != 0) { 201 if ((metaState & KeyEvent.META_ALT_ON) != 0) {
229 modifiers |= WebInputEventModifier.AltKey; 202 modifiers |= WebInputEventModifier.AltKey;
230 } 203 }
231 if ((metaState & KeyEvent.META_CTRL_ON) != 0) { 204 if ((metaState & KeyEvent.META_CTRL_ON) != 0) {
232 modifiers |= WebInputEventModifier.ControlKey; 205 modifiers |= WebInputEventModifier.ControlKey;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 * @param selectionEnd The character offset of the selection end, or the car et position if there 251 * @param selectionEnd The character offset of the selection end, or the car et position if there
279 * is no selection. 252 * is no selection.
280 * @param compositionStart The character offset of the composition start, or -1 if there is no 253 * @param compositionStart The character offset of the composition start, or -1 if there is no
281 * composition. 254 * composition.
282 * @param compositionEnd The character offset of the composition end, or -1 if there is no 255 * @param compositionEnd The character offset of the composition end, or -1 if there is no
283 * selection. 256 * selection.
284 * @param isNonImeChange True when the update was caused by non-IME (e.g. Ja vascript). 257 * @param isNonImeChange True when the update was caused by non-IME (e.g. Ja vascript).
285 */ 258 */
286 public void updateState(String text, int selectionStart, int selectionEnd, i nt compositionStart, 259 public void updateState(String text, int selectionStart, int selectionEnd, i nt compositionStart,
287 int compositionEnd, boolean isNonImeChange) { 260 int compositionEnd, boolean isNonImeChange) {
261 mLastSelectionStart = selectionStart;
262 mLastSelectionEnd = selectionEnd;
288 if (mInputConnection == null) return; 263 if (mInputConnection == null) return;
289 mInputConnection.updateState(text, selectionStart, selectionEnd, composi tionStart, 264 boolean singleLine = mTextInputType != TextInputType.TEXT_AREA
290 compositionEnd, isNonImeChange); 265 && mTextInputType != TextInputType.CONTENT_EDITABLE;
266 mInputConnection.updateStateOnUiThread(text, selectionStart, selectionEn d, compositionStart,
267 compositionEnd, singleLine, isNonImeChange);
291 } 268 }
292 269
293 /** 270 /**
294 * Attaches the imeAdapter to its native counterpart. This is needed to star t forwarding 271 * Attaches the imeAdapter to its native counterpart. This is needed to star t forwarding
295 * keyboard events to WebKit. 272 * keyboard events to WebKit.
296 * @param nativeImeAdapter The pointer to the native ImeAdapter object. 273 * @param nativeImeAdapter The pointer to the native ImeAdapter object.
297 */ 274 */
298 public void attach(long nativeImeAdapter) { 275 public void attach(long nativeImeAdapter) {
299 if (mNativeImeAdapterAndroid == nativeImeAdapter) return; 276 if (mNativeImeAdapterAndroid == nativeImeAdapter) return;
300 if (mNativeImeAdapterAndroid != 0) { 277 if (mNativeImeAdapterAndroid != 0) {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
360 showSoftKeyboard(); 337 showSoftKeyboard();
361 } 338 }
362 } 339 }
363 340
364 /** 341 /**
365 * Call this when view's focus has changed. 342 * Call this when view's focus has changed.
366 * @param gainFocus True if we're gaining focus. 343 * @param gainFocus True if we're gaining focus.
367 */ 344 */
368 public void onViewFocusChanged(boolean gainFocus) { 345 public void onViewFocusChanged(boolean gainFocus) {
369 if (DEBUG_LOGS) Log.w(TAG, "onViewFocusChanged: gainFocus [%b]", gainFoc us); 346 if (DEBUG_LOGS) Log.w(TAG, "onViewFocusChanged: gainFocus [%b]", gainFoc us);
370 if (!gainFocus) hideKeyboard(); 347 if (!gainFocus) reset();
371 } 348 }
372 349
373 /** 350 /**
374 * Move cursor to the end of the current selection. 351 * Move cursor to the end of the current selection.
375 */ 352 */
376 public void moveCursorToSelectionEnd() { 353 public void moveCursorToSelectionEnd() {
377 if (DEBUG_LOGS) Log.w(TAG, "movecursorToEnd"); 354 if (DEBUG_LOGS) Log.w(TAG, "movecursorToEnd");
378 if (mInputConnection != null) { 355 if (mInputConnection != null) {
379 int selectionEnd = Selection.getSelectionEnd(mEditable); 356 mInputConnection.moveCursorToSelectionEndOnUiThread();
380 mInputConnection.setSelection(selectionEnd, selectionEnd);
381 } 357 }
382 } 358 }
383 359
384 @VisibleForTesting 360 @VisibleForTesting
385 void setInputTypeForTest(int textInputType) { 361 void setInputTypeForTest(int textInputType) {
386 mTextInputType = textInputType; 362 mTextInputType = textInputType;
387 } 363 }
388 364
389 private static boolean isTextInputType(int type) { 365 private static boolean isTextInputType(int type) {
390 return type != TextInputType.NONE && !InputDialogContainer.isDialogInput Type(type); 366 return type != TextInputType.NONE && !InputDialogContainer.isDialogInput Type(type);
391 } 367 }
392 368
393 public boolean hasTextInputType() { 369 public boolean hasTextInputType() {
394 return isTextInputType(mTextInputType); 370 return isTextInputType(mTextInputType);
395 } 371 }
396 372
373 /**
374 * See {@link View#dispatchKeyEvent(KeyEvent)}
375 */
397 public boolean dispatchKeyEvent(KeyEvent event) { 376 public boolean dispatchKeyEvent(KeyEvent event) {
398 if (DEBUG_LOGS) Log.w(TAG, "dispatchKeyEvent: action [%d], keycode [%d]" , event.getAction(), 377 if (DEBUG_LOGS) Log.w(TAG, "dispatchKeyEvent: action [%d], keycode [%d]" , event.getAction(),
399 event.getKeyCode()); 378 event.getKeyCode());
400 if (mInputConnection != null) { 379 if (mInputConnection != null) return mInputConnection.sendKeyEventOnUiTh read(event);
401 return mInputConnection.sendKeyEvent(event);
402 }
403 return sendKeyEvent(event); 380 return sendKeyEvent(event);
404 } 381 }
405 382
406 /** 383 /**
384 * Resets IME adapter and hides keyboard. Note that this will also unblock i nput connection.
385 */
386 public void reset() {
387 if (DEBUG_LOGS) Log.w(TAG, "reset");
388 mTextInputType = TextInputType.NONE;
389 mTextInputFlags = 0;
390 // This will trigger unblocking if necessary.
391 hideKeyboard();
392 }
393
394 /**
407 * Update selection to input method manager. 395 * Update selection to input method manager.
408 * 396 *
409 * @param selectionStart The selection start. 397 * @param selectionStart The selection start.
410 * @param selectionEnd The selection end. 398 * @param selectionEnd The selection end.
411 * @param compositionStart The composition start. 399 * @param compositionStart The composition start.
412 * @param compositionEnd The composition end. 400 * @param compositionEnd The composition end.
413 */ 401 */
414 void updateSelection( 402 void updateSelection(
415 int selectionStart, int selectionEnd, int compositionStart, int comp ositionEnd) { 403 int selectionStart, int selectionEnd, int compositionStart, int comp ositionEnd) {
416 mInputMethodManagerWrapper.updateSelection(mViewEmbedder.getAttachedView (), selectionStart, 404 mInputMethodManagerWrapper.updateSelection(mViewEmbedder.getAttachedView (),
417 selectionEnd, compositionStart, compositionEnd); 405 selectionStart, selectionEnd, compositionStart, compositionEnd);
418 } 406 }
419 407
420 /** 408 /**
421 * Restart input (finish composition and change EditorInfo, such as input ty pe). 409 * Restart input (finish composition and change EditorInfo, such as input ty pe).
422 */ 410 */
423 void restartInput() { 411 void restartInput() {
412 // This will eventually cause input method manager to call View#onCreate InputConnection().
424 mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView()) ; 413 mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView()) ;
425 if (mInputConnection != null) mInputConnection.onRestartInput(); 414 if (mInputConnection != null) mInputConnection.onRestartInputOnUiThread( );
426 } 415 }
427 416
428 /** 417 /**
429 * @see BaseInputConnection#performContextMenuAction(int) 418 * @see BaseInputConnection#performContextMenuAction(int)
430 */ 419 */
431 boolean performContextMenuAction(int id) { 420 boolean performContextMenuAction(int id) {
432 if (DEBUG_LOGS) Log.w(TAG, "performContextMenuAction: id [%d]", id); 421 if (DEBUG_LOGS) Log.w(TAG, "performContextMenuAction: id [%d]", id);
433 return mViewEmbedder.performContextMenuAction(id); 422 return mViewEmbedder.performContextMenuAction(id);
434 } 423 }
435 424
436 boolean performEditorAction(int actionCode) { 425 boolean performEditorAction(int actionCode) {
437 if (mNativeImeAdapterAndroid == 0) return false; 426 if (mNativeImeAdapterAndroid == 0) return false;
438 if (actionCode == EditorInfo.IME_ACTION_NEXT) { 427 if (actionCode == EditorInfo.IME_ACTION_NEXT) {
439 sendSyntheticKeyPress(KeyEvent.KEYCODE_TAB, 428 sendSyntheticKeyPress(KeyEvent.KEYCODE_TAB,
440 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE 429 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
441 | KeyEvent.FLAG_EDITOR_ACTION); 430 | KeyEvent.FLAG_EDITOR_ACTION);
442 } else { 431 } else {
443 sendSyntheticKeyPress(KeyEvent.KEYCODE_ENTER, 432 sendSyntheticKeyPress(KeyEvent.KEYCODE_ENTER,
444 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE 433 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
445 | KeyEvent.FLAG_EDITOR_ACTION); 434 | KeyEvent.FLAG_EDITOR_ACTION);
446 } 435 }
447 return true; 436 return true;
448 } 437 }
449 438
439 void notifyUserAction() {
440 mInputMethodManagerWrapper.notifyUserAction();
441 }
442
450 @VisibleForTesting 443 @VisibleForTesting
451 protected void sendSyntheticKeyPress(int keyCode, int flags) { 444 protected void sendSyntheticKeyPress(int keyCode, int flags) {
452 long eventTime = SystemClock.uptimeMillis(); 445 long eventTime = SystemClock.uptimeMillis();
453 sendKeyEvent(new KeyEvent(eventTime, eventTime, 446 sendKeyEvent(new KeyEvent(eventTime, eventTime,
454 KeyEvent.ACTION_DOWN, keyCode, 0, 0, 447 KeyEvent.ACTION_DOWN, keyCode, 0, 0,
455 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 448 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
456 flags)); 449 flags));
457 sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, 450 sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
458 KeyEvent.ACTION_UP, keyCode, 0, 0, 451 KeyEvent.ACTION_UP, keyCode, 0, 0,
459 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 452 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
(...skipping 21 matching lines...) Expand all
481 nativeSetComposingText( 474 nativeSetComposingText(
482 mNativeImeAdapterAndroid, text, text.toString(), newCursorPo sition); 475 mNativeImeAdapterAndroid, text, text.toString(), newCursorPo sition);
483 } 476 }
484 477
485 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, WebInputEventType. KeyUp, 478 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, WebInputEventType. KeyUp,
486 timestampMs, COMPOSITION_KEY_CODE, 0, 0); 479 timestampMs, COMPOSITION_KEY_CODE, 0, 0);
487 return true; 480 return true;
488 } 481 }
489 482
490 @VisibleForTesting 483 @VisibleForTesting
491 void finishComposingText() { 484 boolean finishComposingText() {
492 if (mNativeImeAdapterAndroid == 0) return; 485 if (mNativeImeAdapterAndroid == 0) return false;
493 nativeFinishComposingText(mNativeImeAdapterAndroid); 486 nativeFinishComposingText(mNativeImeAdapterAndroid);
487 return true;
494 } 488 }
495 489
496 boolean sendKeyEvent(KeyEvent event) { 490 boolean sendKeyEvent(KeyEvent event) {
497 if (mNativeImeAdapterAndroid == 0) return false; 491 if (mNativeImeAdapterAndroid == 0) return false;
498 492
499 int action = event.getAction(); 493 int action = event.getAction();
500 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) { 494 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) {
501 // action == KeyEvent.ACTION_MULTIPLE 495 // action == KeyEvent.ACTION_MULTIPLE
502 // TODO(bulach): confirm the actual behavior. Apparently: 496 // TODO(bulach): confirm the actual behavior. Apparently:
503 // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a 497 // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); 540 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end);
547 return true; 541 return true;
548 } 542 }
549 543
550 /** 544 /**
551 * Send a request to the native counterpart to set composing region to given indices. 545 * Send a request to the native counterpart to set composing region to given indices.
552 * @param start The start of the composition. 546 * @param start The start of the composition.
553 * @param end The end of the composition. 547 * @param end The end of the composition.
554 * @return Whether the native counterpart of ImeAdapter received the call. 548 * @return Whether the native counterpart of ImeAdapter received the call.
555 */ 549 */
556 boolean setComposingRegion(CharSequence text, int start, int end) { 550 boolean setComposingRegion(int start, int end) {
557 if (mNativeImeAdapterAndroid == 0) return false; 551 if (mNativeImeAdapterAndroid == 0) return false;
558 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); 552 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end);
559 return true; 553 return true;
560 } 554 }
561 555
562 @CalledByNative 556 @CalledByNative
563 private void focusedNodeChanged(boolean isEditable) { 557 private void focusedNodeChanged(boolean isEditable) {
564 if (DEBUG_LOGS) Log.w(TAG, "focusedNodeChanged: isEditable [%b]", isEdit able); 558 if (DEBUG_LOGS) Log.w(TAG, "focusedNodeChanged: isEditable [%b]", isEdit able);
565 if (mTextInputType != TextInputType.NONE && mInputConnection != null && isEditable) { 559 if (mTextInputType != TextInputType.NONE && mInputConnection != null && isEditable) {
566 restartInput(); 560 restartInput();
567 } 561 }
568 } 562 }
569 563
564 /**
565 * Send a request to the native counterpart to give the latest text input st ate update.
566 */
567 boolean requestTextInputStateUpdate() {
568 if (mNativeImeAdapterAndroid == 0) return false;
569 // You won't get state update anyways.
570 if (mInputConnection == null) return false;
571 return nativeRequestTextInputStateUpdate(mNativeImeAdapterAndroid);
572 }
573
570 @CalledByNative 574 @CalledByNative
571 private void populateUnderlinesFromSpans(CharSequence text, long underlines) { 575 private void populateUnderlinesFromSpans(CharSequence text, long underlines) {
572 if (DEBUG_LOGS) { 576 if (DEBUG_LOGS) {
573 Log.w(TAG, "populateUnderlinesFromSpans: text [%s], underlines [%d]" , text, underlines); 577 Log.w(TAG, "populateUnderlinesFromSpans: text [%s], underlines [%d]" , text, underlines);
574 } 578 }
575 if (!(text instanceof SpannableString)) return; 579 if (!(text instanceof SpannableString)) return;
576 580
577 SpannableString spannableString = ((SpannableString) text); 581 SpannableString spannableString = ((SpannableString) text);
578 CharacterStyle spans[] = 582 CharacterStyle spans[] =
579 spannableString.getSpans(0, text.length(), CharacterStyle.class) ; 583 spannableString.getSpans(0, text.length(), CharacterStyle.class) ;
(...skipping 16 matching lines...) Expand all
596 } 600 }
597 601
598 @CalledByNative 602 @CalledByNative
599 private void detach() { 603 private void detach() {
600 if (DEBUG_LOGS) Log.w(TAG, "detach"); 604 if (DEBUG_LOGS) Log.w(TAG, "detach");
601 mNativeImeAdapterAndroid = 0; 605 mNativeImeAdapterAndroid = 0;
602 } 606 }
603 607
604 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, 608 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid,
605 int eventType, long timestampMs, int keyCode, int modifiers, int uni codeChar); 609 int eventType, long timestampMs, int keyCode, int modifiers, int uni codeChar);
606
607 private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyE vent event, 610 private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyE vent event,
608 int action, int modifiers, long timestampMs, int keyCode, int scanCo de, 611 int action, int modifiers, long timestampMs, int keyCode, int scanCo de,
609 boolean isSystemKey, int unicodeChar); 612 boolean isSystemKey, int unicodeChar);
610
611 private static native void nativeAppendUnderlineSpan(long underlinePtr, int start, int end); 613 private static native void nativeAppendUnderlineSpan(long underlinePtr, int start, int end);
612
613 private static native void nativeAppendBackgroundColorSpan(long underlinePtr , int start, 614 private static native void nativeAppendBackgroundColorSpan(long underlinePtr , int start,
614 int end, int backgroundColor); 615 int end, int backgroundColor);
615
616 private native void nativeSetComposingText(long nativeImeAdapterAndroid, Cha rSequence text, 616 private native void nativeSetComposingText(long nativeImeAdapterAndroid, Cha rSequence text,
617 String textStr, int newCursorPosition); 617 String textStr, int newCursorPosition);
618
619 private native void nativeCommitText(long nativeImeAdapterAndroid, String te xtStr); 618 private native void nativeCommitText(long nativeImeAdapterAndroid, String te xtStr);
620
621 private native void nativeFinishComposingText(long nativeImeAdapterAndroid); 619 private native void nativeFinishComposingText(long nativeImeAdapterAndroid);
622
623 private native void nativeAttachImeAdapter(long nativeImeAdapterAndroid); 620 private native void nativeAttachImeAdapter(long nativeImeAdapterAndroid);
624
625 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA ndroid, 621 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA ndroid,
626 int start, int end); 622 int start, int end);
627
628 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i nt start, int end); 623 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i nt start, int end);
629
630 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , 624 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid ,
631 int before, int after); 625 int before, int after);
632
633 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); 626 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid);
627 private native boolean nativeRequestTextInputStateUpdate(long nativeImeAdapt erAndroid);
634 } 628 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698