OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.content.browser.input; | |
6 | |
7 import android.os.Bundle; | |
8 import android.os.Handler; | |
9 import android.os.SystemClock; | |
10 import android.text.InputType; | |
11 import android.view.KeyEvent; | |
12 import android.view.View; | |
13 import android.view.inputmethod.CompletionInfo; | |
14 import android.view.inputmethod.CorrectionInfo; | |
15 import android.view.inputmethod.EditorInfo; | |
16 import android.view.inputmethod.ExtractedText; | |
17 import android.view.inputmethod.ExtractedTextRequest; | |
18 import android.view.inputmethod.InputConnection; | |
19 | |
20 import org.chromium.base.Log; | |
21 import org.chromium.base.ThreadUtils; | |
22 import org.chromium.base.VisibleForTesting; | |
23 import org.chromium.blink_public.web.WebInputEventType; | |
24 import org.chromium.blink_public.web.WebTextInputFlags; | |
25 import org.chromium.ui.base.ime.TextInputType; | |
26 | |
27 import java.util.concurrent.BlockingQueue; | |
28 import java.util.concurrent.Callable; | |
29 import java.util.concurrent.ExecutionException; | |
30 import java.util.concurrent.FutureTask; | |
31 import java.util.concurrent.LinkedBlockingQueue; | |
32 import java.util.concurrent.TimeUnit; | |
33 import java.util.concurrent.atomic.AtomicBoolean; | |
34 | |
35 /** | |
36 * TODO(changwan): add a description | |
37 */ | |
38 public class ChromiumInputConnection implements ChromiumBaseInputConnection { | |
39 private static final String TAG = "cr.Ime"; | |
40 | |
41 // Android will raise TimeoutException to IME if bound call takes 2 seconds. | |
42 private static final long MAX_TIMEOUT = 1900; | |
43 private static final long MIN_TIMEOUT = 15; | |
44 | |
45 private final Handler mHandler; | |
46 private final View mInternalView; | |
47 private final ImeAdapter mImeAdapter; | |
48 | |
49 private int mNumNestedBatchEdits = 0; | |
50 | |
51 // TODO(changwan): this value is used in two threads. | |
52 private final AtomicBoolean mSingleLine = new AtomicBoolean(true); | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
How about making this one of the TextInputState fi
Changwan Ryu
2016/01/19 07:31:53
Good idea! Done.
| |
53 private final BlockingQueue<TextInputState> mQueue = new LinkedBlockingQueue <>(); | |
54 private TextInputState mLastTextInputState; | |
55 | |
56 @VisibleForTesting | |
57 ChromiumInputConnection(View view, ImeAdapter imeAdapter) { | |
58 ImeUtils.assertOnUiThread(); | |
59 | |
60 mHandler = InputConnectionHandlerFactory.getInputConnectionHandler(); | |
61 mInternalView = view; | |
62 mImeAdapter = imeAdapter; | |
63 mImeAdapter.setInputConnection(ChromiumInputConnection.this); | |
64 | |
65 // Make sure the previous composition is finished. There is no proper wa y to | |
66 // tell IME about the previous composition by the time this call ends, a nyways. | |
67 // finishComposingText(); | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
Can this be deleted? Was this useful for anything
Changwan Ryu
2016/01/19 07:31:53
Done.
| |
68 | |
69 Log.d(TAG, "%d Constructor called", System.identityHashCode(this)); | |
70 } | |
71 | |
72 private void updateToInputMethodManager(TextInputState textInputState) { | |
73 ImeUtils.assertNotOnUiThread(); | |
74 if (mNumNestedBatchEdits != 0) { | |
75 mLastTextInputState = textInputState; | |
76 return; | |
77 } | |
78 ImeUtils.assertReally(textInputState != null); | |
79 if (textInputState.equals(mLastTextInputState)) return; | |
80 Range selection = textInputState.selection(); | |
81 Range composition = textInputState.composition(); | |
82 getInputMethodManagerWrapper().updateSelection(mInternalView, selection. start(), | |
83 selection.end(), composition.start(), composition.end()); | |
84 mLastTextInputState = textInputState; | |
85 } | |
86 | |
87 public void updateEditorInfo(EditorInfo outAttrs, int inputType, int inputFl ags) { | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
Please rename this to updateEditorInfoOnUiThread a
Changwan Ryu
2016/01/19 07:31:53
Done.
| |
88 Log.d(TAG, "updateEditorInfo"); | |
89 mSingleLine.set(true); | |
90 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME _FLAG_NO_EXTRACT_UI; | |
91 outAttrs.inputType = | |
92 EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_ EDIT_TEXT; | |
93 | |
94 if ((inputFlags & WebTextInputFlags.AutocompleteOff) != 0) { | |
95 outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS; | |
96 } | |
97 | |
98 if (inputType == TextInputType.TEXT) { | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
Please move most of this logic into a private stat
Changwan Ryu
2016/01/19 07:31:53
Done.
| |
99 // Normal text field | |
100 outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; | |
101 if ((inputFlags & WebTextInputFlags.AutocorrectOff) == 0) { | |
102 outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; | |
103 } | |
104 } else if (inputType == TextInputType.TEXT_AREA | |
105 || inputType == TextInputType.CONTENT_EDITABLE) { | |
106 outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; | |
107 mSingleLine.set(false); | |
108 if ((inputFlags & WebTextInputFlags.AutocorrectOff) == 0) { | |
109 outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; | |
110 } | |
111 outAttrs.imeOptions |= EditorInfo.IME_ACTION_NONE; | |
112 } else if (inputType == TextInputType.PASSWORD) { | |
113 // Password | |
114 outAttrs.inputType = | |
115 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WE B_PASSWORD; | |
116 outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; | |
117 } else if (inputType == TextInputType.SEARCH) { | |
118 // Search | |
119 outAttrs.imeOptions |= EditorInfo.IME_ACTION_SEARCH; | |
120 } else if (inputType == TextInputType.URL) { | |
121 // Url | |
122 outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT _VARIATION_URI; | |
123 outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; | |
124 } else if (inputType == TextInputType.EMAIL) { | |
125 // Email | |
126 outAttrs.inputType = | |
127 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WE B_EMAIL_ADDRESS; | |
128 outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; | |
129 } else if (inputType == TextInputType.TELEPHONE) { | |
130 // Telephone | |
131 // Number and telephone do not have both a Tab key and an | |
132 // action in default OSK, so set the action to NEXT | |
133 outAttrs.inputType = InputType.TYPE_CLASS_PHONE; | |
134 outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT; | |
135 } else if (inputType == TextInputType.NUMBER) { | |
136 // Number | |
137 outAttrs.inputType = InputType.TYPE_CLASS_NUMBER | |
138 | InputType.TYPE_NUMBER_VARIATION_NORMAL | InputType.TYPE_NU MBER_FLAG_DECIMAL; | |
139 outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT; | |
140 } | |
141 | |
142 // Handling of autocapitalize. Blink will send the flag taking into acco unt the element's | |
143 // type. This is not using AutocapitalizeNone because Android does not a utocapitalize by | |
144 // default and there is no way to express no capitalization. | |
145 // Autocapitalize is meant as a hint to the virtual keyboard. | |
146 if ((inputFlags & WebTextInputFlags.AutocapitalizeCharacters) != 0) { | |
147 outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; | |
148 } else if ((inputFlags & WebTextInputFlags.AutocapitalizeWords) != 0) { | |
149 outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS; | |
150 } else if ((inputFlags & WebTextInputFlags.AutocapitalizeSentences) != 0 ) { | |
151 outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; | |
152 } | |
153 // Content editable doesn't use autocapitalize so we need to set it manu ally. | |
154 if (inputType == TextInputType.CONTENT_EDITABLE) { | |
155 outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; | |
156 } | |
157 | |
158 Range selection = getSelection(); | |
159 outAttrs.initialSelStart = selection.start(); | |
160 outAttrs.initialSelEnd = selection.end(); | |
161 } | |
162 | |
163 @Override | |
164 public void updateState(final String text, final int selectionStart, final i nt selectionEnd, | |
165 final int compositionStart, final int compositionEnd, final boolean isNonImeChange) { | |
166 Log.d(TAG, "updateState [%s] [%s %s] [%s %s] [%b]", text, selectionStart , selectionEnd, | |
167 compositionStart, compositionEnd, isNonImeChange); | |
168 ImeUtils.assertOnUiThread(); | |
169 | |
170 // TODO(changwan): this should be called at the end of each call, not af ter it. | |
171 final TextInputState newState = | |
172 new TextInputState(text, new Range(selectionStart, selectionEnd) , | |
173 new Range(compositionStart, compositionEnd)); | |
174 | |
175 if (isNonImeChange) { | |
176 mHandler.post(new Runnable() { | |
177 @Override | |
178 public void run() { | |
179 updateToInputMethodManager(newState); | |
180 } | |
181 }); | |
182 } else { | |
183 onStateUpdateOriginatingFromIme(newState); | |
184 } | |
185 Log.d(TAG, "updateState finished"); | |
186 } | |
187 | |
188 private TextInputState updateTextInputState() { | |
189 ThreadUtils.postOnUiThread(new Runnable() { | |
190 @Override | |
191 public void run() { | |
192 boolean result = mImeAdapter.requestTextInputStateUpdate(); | |
193 if (!result) unblock(); | |
194 } | |
195 }); | |
196 return blockAndGetStateUpdate(); | |
197 } | |
198 | |
199 private Range getSelection() { | |
200 Log.d(TAG, "getSelection"); | |
201 return new Range(0, 0); | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
Looks like this needs to be fixed to work properly
Changwan Ryu
2016/01/19 07:31:53
Applied the logic from AdapterInputConnection.
BT
| |
202 // TextInputState textInputState = updateTextInputState(); | |
203 // if (textInputState == null) return new Range(0, 0); | |
204 // return textInputState.selection(); | |
205 } | |
206 | |
207 private void onStateUpdateOriginatingFromIme(TextInputState textInputState) { | |
208 ImeUtils.assertOnUiThread(); | |
209 try { | |
210 mQueue.put(textInputState); | |
211 } catch (InterruptedException e) { | |
212 Log.w(TAG, "onStateUpdateOriginatingFromIme interrupted", e); | |
213 } | |
214 Log.d(TAG, "%d onStateUpdateOriginatingFromIme finished: %d", System.ide ntityHashCode(this), | |
215 mQueue.size()); | |
216 } | |
217 | |
218 /** | |
219 * TODO(changwan): simplify calls using this. | |
220 * @param c | |
221 * @return | |
222 */ | |
223 private TextInputState runAndGetStateUpdate(final Callable<Boolean> c) { | |
224 ImeUtils.assertNotOnUiThread(); | |
225 mQueue.clear(); | |
226 ThreadUtils.postOnUiThread(new Runnable() { | |
227 @Override | |
228 public void run() { | |
229 // TODO(changwan): remove this. | |
230 Boolean result = null; | |
231 try { | |
232 result = c.call(); | |
233 } catch (Exception e) { | |
234 e.printStackTrace(); | |
235 } | |
236 if (result == null || !result.booleanValue()) { | |
237 // Now we know that updateState() will never be called. | |
238 unblock(); | |
239 } | |
240 } | |
241 }); | |
242 | |
243 return blockAndGetStateUpdate(); | |
244 } | |
245 | |
246 /** | |
247 * TODO(changwan): simplify calls using this. | |
248 * @param c | |
249 * @return | |
250 */ | |
251 private TextInputState runAndGetStateUpdate2(final Callable<Boolean> c) { | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
This has no callers, can it be deleted?
Changwan Ryu
2016/01/19 07:31:53
Done.
| |
252 mQueue.clear(); | |
253 ThreadUtils.postOnUiThread(new Runnable() { | |
254 @Override | |
255 public void run() { | |
256 Boolean result = null; | |
257 try { | |
258 result = c.call(); | |
259 } catch (Exception e) { | |
260 e.printStackTrace(); | |
261 } | |
262 if (result == null || !result.booleanValue()) { | |
263 // Now we know that updateState() will never be called. | |
264 unblock(); | |
265 } | |
266 } | |
267 }); | |
268 FutureTask<TextInputState> task = new FutureTask<>(new Callable<TextInpu tState>() { | |
269 @Override | |
270 public TextInputState call() throws Exception { | |
271 TextInputState result = blockAndGetStateUpdate(); | |
272 // if (result != null) updateToInputMethodManager(result); | |
273 return result; | |
274 } | |
275 }); | |
276 mHandler.post(task); | |
277 if (!ThreadUtils.runningOnUiThread()) { | |
278 try { | |
279 return task.get(); | |
280 } catch (InterruptedException | ExecutionException e) { | |
281 // TODO(changwan): Auto-generated catch block | |
282 e.printStackTrace(); | |
283 } | |
284 } | |
285 return null; | |
286 } | |
287 | |
288 /** | |
289 * Unblock the blockAndGetStateUpdate() function if we found that we will | |
290 * never get state update. | |
291 */ | |
292 private void unblock() { | |
293 ImeUtils.assertOnUiThread(); | |
294 onStateUpdateOriginatingFromIme(new TextInputState.Dummy()); | |
295 } | |
296 | |
297 private void postConsumeStateUpdate() { | |
298 mHandler.post(new Runnable() { | |
299 @Override | |
300 public void run() { | |
301 blockAndGetStateUpdate(); | |
302 } | |
303 }); | |
304 } | |
305 | |
306 /** | |
307 * Block until we get the expected state update. | |
308 * @return TextInputState if we get it successfully. null otherwise. | |
309 */ | |
310 private TextInputState blockAndGetStateUpdate() { | |
311 ImeUtils.assertNotOnUiThread(); | |
312 long timeout = MAX_TIMEOUT; | |
313 try { | |
314 while (true) { | |
315 long startTime = System.currentTimeMillis(); | |
316 TextInputState result = mQueue.poll(timeout, TimeUnit.MILLISECON DS); | |
317 | |
318 if (result != null) { | |
319 if (result.isDummy()) { | |
320 Log.d(TAG, "blockAndGetStateUpdate: failed, got dummy"); | |
321 return null; | |
322 } | |
323 updateToInputMethodManager(result); | |
324 return result; | |
325 } | |
326 | |
327 long polledTime = System.currentTimeMillis() - startTime; | |
328 timeout -= polledTime + 10; | |
329 if (timeout < MIN_TIMEOUT) { | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
This doesn't seem useful, how about simply having
Changwan Ryu
2016/01/19 07:31:53
Done.
| |
330 Log.e(TAG, "%d blockAndGetStateUpdate: failed %d", | |
331 System.identityHashCode(this), mQueue.size()); | |
332 ImeUtils.assertReally(false); | |
333 return null; | |
334 } | |
335 } | |
336 } catch (InterruptedException e) { | |
337 e.printStackTrace(); | |
338 Log.d(TAG, "blockAndGetStateUpdate: failed due to interruption"); | |
339 return null; | |
340 } | |
341 } | |
342 | |
343 /** | |
344 * @see InputConnection#setComposingText(java.lang.CharSequence, int) | |
345 */ | |
346 @Override | |
347 public boolean setComposingText(final CharSequence text, final int newCursor Position) { | |
348 Log.d(TAG, "setComposingText [%s] [%d]", text, newCursorPosition); | |
349 ImeUtils.assertNotOnUiThread(); | |
350 mQueue.clear(); // TODO(changwan): remove | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
Can all these be removed yet?
Changwan Ryu
2016/01/19 07:31:53
Done.
| |
351 ThreadUtils.postOnUiThread(new Runnable() { | |
352 @Override | |
353 public void run() { | |
354 boolean result = mImeAdapter.checkCompositionQueueAndCallNative( | |
355 text, newCursorPosition, false); | |
356 if (!result) unblock(); | |
357 } | |
358 }); | |
359 return blockAndGetStateUpdate() != null; | |
360 } | |
361 | |
362 /** | |
363 * @see InputConnection#commitText(java.lang.CharSequence, int) | |
364 */ | |
365 @Override | |
366 public boolean commitText(final CharSequence text, final int newCursorPositi on) { | |
367 Log.d(TAG, "commitText [%s] [%d]", text, newCursorPosition); | |
368 ImeUtils.assertNotOnUiThread(); | |
369 mQueue.clear(); // TODO(changwan): remove | |
370 ThreadUtils.postOnUiThread(new Runnable() { | |
371 @Override | |
372 public void run() { | |
373 boolean result = mImeAdapter.checkCompositionQueueAndCallNative( | |
374 text, newCursorPosition, text.length() > 0); | |
375 if (!result) unblock(); | |
376 } | |
377 }); | |
378 return blockAndGetStateUpdate() != null; | |
379 } | |
380 | |
381 @Override | |
382 public void restartInput() { | |
383 Log.d(TAG, "restartInput"); | |
384 if (ThreadUtils.runningOnUiThread()) { | |
aelias_OOO_until_Jul13
2015/09/30 00:10:03
After this is renamed restartInputOnUiThread, this
Changwan Ryu
2016/01/19 07:31:53
The logic here has been refactored into ImeAdapter
| |
385 unblock(); | |
386 mHandler.post(new Runnable() { | |
387 @Override | |
388 public void run() { | |
389 getInputMethodManagerWrapper().restartInput(mInternalView); | |
390 } | |
391 }); | |
392 } else { | |
393 getInputMethodManagerWrapper().restartInput(mInternalView); | |
394 } | |
395 } | |
396 | |
397 /** | |
398 * @see InputConnection#performEditorAction(int) | |
399 */ | |
400 @Override | |
401 public boolean performEditorAction(int actionCode) { | |
402 Log.d(TAG, "performEditorAction [%d]", actionCode); | |
403 ImeUtils.assertNotOnUiThread(); | |
404 if (actionCode == EditorInfo.IME_ACTION_NEXT) { | |
405 restartInput(); | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
This is the only IME-thread caller of restartInput
Changwan Ryu
2016/01/19 07:31:53
Thanks for pointing that out. It's already been mo
| |
406 mQueue.clear(); // TODO(changwan): remove | |
407 // Send TAB key event | |
408 ThreadUtils.postOnUiThread(new Runnable() { | |
409 @Override | |
410 public void run() { | |
411 long timeStampMs = SystemClock.uptimeMillis(); | |
412 boolean result = mImeAdapter.sendSyntheticKeyEvent( | |
413 WebInputEventType.RawKeyDown, timeStampMs, KeyEvent. KEYCODE_TAB, 0, 0); | |
414 if (!result) unblock(); | |
415 } | |
416 }); | |
417 return blockAndGetStateUpdate() != null; | |
418 } else { | |
419 mQueue.clear(); // TODO(changwan): remove | |
420 ThreadUtils.postOnUiThread(new Runnable() { | |
421 @Override | |
422 public void run() { | |
423 boolean result = mImeAdapter.sendKeyEventWithKeyCode( | |
424 KeyEvent.KEYCODE_ENTER, KeyEvent.FLAG_SOFT_KEYBOARD | |
425 | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.F LAG_EDITOR_ACTION); | |
426 if (!result) unblock(); | |
427 } | |
428 }); | |
429 return blockAndGetStateUpdate() != null; | |
430 } | |
431 } | |
432 | |
433 /** | |
434 * @see InputConnection#performContextMenuAction(int) | |
435 */ | |
436 @Override | |
437 public boolean performContextMenuAction(final int id) { | |
438 Log.d(TAG, "performContextMenuAction [%d]", id); | |
439 ImeUtils.assertNotOnUiThread(); | |
440 ThreadUtils.postOnUiThread(new Runnable() { | |
441 @Override | |
442 public void run() { | |
443 mImeAdapter.performContextMenuAction(id); | |
444 } | |
445 }); | |
446 // TODO(changwan): return correct value | |
447 return true; | |
448 } | |
449 | |
450 /** | |
451 * @see InputConnection#getExtractedText(android.view.inputmethod.ExtractedT extRequest, | |
452 * int) | |
453 */ | |
454 @Override | |
455 public ExtractedText getExtractedText(ExtractedTextRequest request, int flag s) { | |
456 Log.d(TAG, "getExtractedText"); | |
457 ImeUtils.assertNotOnUiThread(); | |
458 mQueue.clear(); // TODO(changwan): remove | |
459 TextInputState textInputState = updateTextInputState(); | |
460 if (textInputState == null) return null; | |
461 ExtractedText extractedText = new ExtractedText(); | |
462 extractedText.text = textInputState.text(); | |
463 extractedText.partialEndOffset = textInputState.text().length(); | |
464 extractedText.selectionStart = textInputState.selection().start(); | |
465 extractedText.selectionEnd = textInputState.selection().end(); | |
466 extractedText.flags = mSingleLine.get() ? ExtractedText.FLAG_SINGLE_LINE : 0; | |
467 return extractedText; | |
468 } | |
469 | |
470 /** | |
471 * @see InputConnection#beginBatchEdit() | |
472 */ | |
473 @Override | |
474 public boolean beginBatchEdit() { | |
475 Log.d(TAG, "beginBatchEdit [%b]", (mNumNestedBatchEdits == 0)); | |
476 ImeUtils.assertNotOnUiThread(); | |
477 mNumNestedBatchEdits++; | |
478 return true; | |
479 } | |
480 | |
481 /** | |
482 * @see InputConnection#endBatchEdit() | |
483 */ | |
484 @Override | |
485 public boolean endBatchEdit() { | |
486 ImeUtils.assertNotOnUiThread(); | |
487 if (mNumNestedBatchEdits == 0) return false; | |
488 --mNumNestedBatchEdits; | |
489 Log.d(TAG, "endBatchEdit [%b]", (mNumNestedBatchEdits == 0)); | |
490 if (mNumNestedBatchEdits == 0 && mLastTextInputState != null) { | |
491 updateToInputMethodManager(mLastTextInputState); | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
I don't think this works as you intended, this is
Changwan Ryu
2016/01/19 07:31:53
You're correct. Now we have mLastUpdatedTextInputS
| |
492 } | |
493 return mNumNestedBatchEdits != 0; | |
494 } | |
495 | |
496 /** | |
497 * @see InputConnection#deleteSurroundingText(int, int) | |
498 */ | |
499 @Override | |
500 public boolean deleteSurroundingText(final int beforeLength, final int after Length) { | |
501 Log.d(TAG, "deleteSurroundingText [%d %d]", beforeLength, afterLength); | |
502 ImeUtils.assertNotOnUiThread(); | |
503 mQueue.clear(); // TODO(changwan): remove | |
504 ThreadUtils.postOnUiThread(new Runnable() { | |
505 @Override | |
506 public void run() { | |
507 boolean result = mImeAdapter.deleteSurroundingText(beforeLength, afterLength); | |
508 if (!result) unblock(); | |
509 } | |
510 }); | |
511 return blockAndGetStateUpdate() != null; | |
512 } | |
513 | |
514 /** | |
515 * @see ChromiumBaseInputConnection#dispatchKeyEvent(KeyEvent) | |
516 */ | |
517 @Override | |
518 public boolean dispatchKeyEvent(final KeyEvent event) { | |
519 // TODO(changwan): check if we need to post to IME handler first. | |
520 ImeUtils.assertOnUiThread(); | |
521 // This should not happen. | |
522 if (!mImeAdapter.translateAndSendNativeEvents(event)) return false; | |
523 postConsumeStateUpdate(); | |
524 return true; | |
525 } | |
526 | |
527 /** | |
528 * @see InputConnection#sendKeyEvent(android.view.KeyEvent) | |
529 */ | |
530 @Override | |
531 public boolean sendKeyEvent(final KeyEvent event) { | |
532 Log.d(TAG, "sendKeyEvent [%d %d]", event.getAction(), event.getKeyCode() ); | |
533 ImeUtils.assertNotOnUiThread(); | |
534 mQueue.clear(); // TODO(changwan): remove | |
535 ThreadUtils.postOnUiThread(new Runnable() { | |
536 @Override | |
537 public void run() { | |
538 boolean result = mImeAdapter.translateAndSendNativeEvents(event) ; | |
539 if (!result) unblock(); | |
540 } | |
541 }); | |
542 return blockAndGetStateUpdate() != null; | |
543 } | |
544 | |
545 /** | |
546 * @see InputConnection#finishComposingText() | |
547 */ | |
548 @Override | |
549 public boolean finishComposingText() { | |
550 Log.d(TAG, "%d finishComposingText", System.identityHashCode(this)); | |
551 mQueue.clear(); // TODO(changwan): remove | |
552 // This is the only function that may be called on UI thread because | |
553 // of direct calls from InputMethodManager. | |
554 | |
555 ThreadUtils.postOnUiThread(new Runnable() { | |
556 @Override | |
557 public void run() { | |
558 boolean result = mImeAdapter.finishComposingText(); | |
559 if (!result) unblock(); | |
560 } | |
561 }); | |
562 | |
563 if (ThreadUtils.runningOnUiThread()) { | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
There doesn't appear to be any (non-unit-test) cod
Changwan Ryu
2016/01/19 07:31:53
Hmm.. How about InputMethodManager#reportFinishInp
| |
564 postConsumeStateUpdate(); | |
565 return true; | |
566 } else { | |
567 return blockAndGetStateUpdate() != null; | |
568 } | |
569 } | |
570 | |
571 /** | |
572 * @see InputConnection#setSelection(int, int) | |
573 */ | |
574 @Override | |
575 public boolean setSelection(final int start, final int end) { | |
576 Log.d(TAG, "setSelection [%d %d]", start, end); | |
577 ImeUtils.assertNotOnUiThread(); | |
578 mQueue.clear(); // TODO(changwan): remove | |
579 ThreadUtils.postOnUiThread(new Runnable() { | |
580 @Override | |
581 public void run() { | |
582 boolean result = mImeAdapter.setEditableSelectionOffsets(start, end); | |
583 if (!result) unblock(); | |
584 } | |
585 }); | |
586 return blockAndGetStateUpdate() != null; | |
587 } | |
588 | |
589 /** | |
590 * @see InputConnection#setComposingRegion(int, int) | |
591 */ | |
592 @Override | |
593 public boolean setComposingRegion(final int start, final int end) { | |
594 Log.d(TAG, "setComposingRegion [%d %d]", start, end); | |
595 ImeUtils.assertNotOnUiThread(); | |
596 mQueue.clear(); // TODO(changwan): remove | |
597 ThreadUtils.postOnUiThread(new Runnable() { | |
598 @Override | |
599 public void run() { | |
600 boolean result = mImeAdapter.setComposingRegion("", start, end); | |
601 if (!result) unblock(); | |
602 } | |
603 }); | |
604 return blockAndGetStateUpdate() != null; | |
605 } | |
606 | |
607 private InputMethodManagerWrapper getInputMethodManagerWrapper() { | |
608 return mImeAdapter.getInputMethodManagerWrapper(); | |
609 } | |
610 | |
611 /** | |
612 * @see InputConnection#getTextBeforeCursor(int, int) | |
613 */ | |
614 @Override | |
615 public CharSequence getTextBeforeCursor(int maxChars, int flags) { | |
616 Log.d(TAG, "getTextBeforeCursor [%d %x]", maxChars, flags); | |
617 ImeUtils.assertNotOnUiThread(); | |
618 mQueue.clear(); // TODO(changwan): remove | |
619 TextInputState textInputState = updateTextInputState(); | |
620 if (textInputState == null) return null; | |
621 return textInputState.getTextBeforeSelection(maxChars); | |
622 } | |
623 | |
624 /** | |
625 * @see InputConnection#getTextAfterCursor(int, int) | |
626 */ | |
627 @Override | |
628 public CharSequence getTextAfterCursor(int maxChars, int flags) { | |
629 Log.d(TAG, "getTextAfterCursor [%d %x]", maxChars, flags); | |
630 mQueue.clear(); // TODO(changwan): remove | |
631 ImeUtils.assertNotOnUiThread(); | |
632 TextInputState textInputState = updateTextInputState(); | |
633 if (textInputState == null) return null; | |
634 return textInputState.getTextAfterSelection(maxChars); | |
635 } | |
636 | |
637 /** | |
638 * @see InputConnection#getSelectedText(int) | |
639 */ | |
640 @Override | |
641 public CharSequence getSelectedText(int flags) { | |
642 Log.d(TAG, "getSelectedText [%x]", flags); | |
643 ImeUtils.assertNotOnUiThread(); | |
644 mQueue.clear(); // TODO(changwan): remove | |
645 TextInputState textInputState = updateTextInputState(); | |
646 if (textInputState == null) return null; | |
647 return textInputState.getSelectedText(); | |
648 } | |
649 | |
650 /** | |
651 * @see InputConnection#getCursorCapsMode(int) | |
652 */ | |
653 @Override | |
654 public int getCursorCapsMode(int reqModes) { | |
655 Log.d(TAG, "getCursorCapsMode [%x]", reqModes); | |
656 ImeUtils.assertNotOnUiThread(); | |
657 // TODO(changwan): Auto-generated method stub | |
aelias_OOO_until_Jul13
2015/09/30 00:10:04
Looks like you have several rarely called methods
Changwan Ryu
2016/01/19 07:31:53
Since InputConnection is an interface, we still ne
| |
658 return 0; | |
659 } | |
660 | |
661 /** | |
662 * @see InputConnection#commitCompletion(android.view.inputmethod.Completion Info) | |
663 */ | |
664 @Override | |
665 public boolean commitCompletion(CompletionInfo text) { | |
666 Log.d(TAG, "commitCompletion [%s]", text); | |
667 ImeUtils.assertNotOnUiThread(); | |
668 // TODO(changwan): Auto-generated method stub | |
669 return false; | |
670 } | |
671 | |
672 /** | |
673 * @see InputConnection#commitCorrection(android.view.inputmethod.Correction Info) | |
674 */ | |
675 @Override | |
676 public boolean commitCorrection(CorrectionInfo correctionInfo) { | |
677 Log.d(TAG, "commitCorrection [%s]", ImeUtils.dumpCorrectionInfo(correcti onInfo)); | |
678 ImeUtils.assertNotOnUiThread(); | |
679 // TODO(changwan): Auto-generated method stub | |
680 return false; | |
681 } | |
682 | |
683 /** | |
684 * @see InputConnection#clearMetaKeyStates(int) | |
685 */ | |
686 @Override | |
687 public boolean clearMetaKeyStates(int states) { | |
688 Log.d(TAG, "clearMetaKeyStates [%x]", states); | |
689 ImeUtils.assertNotOnUiThread(); | |
690 // TODO(changwan): Auto-generated method stub | |
691 return false; | |
692 } | |
693 | |
694 /** | |
695 * @see InputConnection#reportFullscreenMode(boolean) | |
696 */ | |
697 @Override | |
698 public boolean reportFullscreenMode(boolean enabled) { | |
699 Log.d(TAG, "reportFullscreenMode [%b]", enabled); | |
700 ImeUtils.assertNotOnUiThread(); | |
701 // We ignore fullscreen mode for now. That's why we set | |
702 // EditorInfo.IME_FLAG_NO_FULLSCREEN in constructor. | |
703 return false; | |
704 } | |
705 | |
706 /** | |
707 * @see InputConnection#performPrivateCommand(java.lang.String, android.os.B undle) | |
708 */ | |
709 @Override | |
710 public boolean performPrivateCommand(String action, Bundle data) { | |
711 Log.d(TAG, "performPrivateCommand [%s]", action); | |
712 ImeUtils.assertNotOnUiThread(); | |
713 // TODO(changwan): Auto-generated method stub | |
714 return false; | |
715 } | |
716 | |
717 /** | |
718 * @see InputConnection#requestCursorUpdates(int) | |
719 */ | |
720 @Override | |
721 public boolean requestCursorUpdates(int cursorUpdateMode) { | |
722 Log.d(TAG, "requestCursorUpdates [%x]", cursorUpdateMode); | |
723 ImeUtils.assertNotOnUiThread(); | |
724 // TODO(changwan): Auto-generated method stub | |
725 return false; | |
726 } | |
727 | |
728 /** | |
729 * TODO(changwan): add description | |
730 */ | |
731 public void reset() { | |
732 ImeUtils.assertOnUiThread(); | |
733 finishComposingText(); | |
734 } | |
735 } | |
OLD | NEW |