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

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

Issue 1278593004: Introduce ThreadedInputConnection behind a switch (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: add all the files Created 4 years, 11 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
(Empty)
1 // Copyright 2016 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.Looper;
10 import android.view.KeyEvent;
11 import android.view.inputmethod.CompletionInfo;
12 import android.view.inputmethod.CorrectionInfo;
13 import android.view.inputmethod.EditorInfo;
14 import android.view.inputmethod.ExtractedText;
15 import android.view.inputmethod.ExtractedTextRequest;
16 import android.view.inputmethod.InputConnection;
17
18 import org.chromium.base.Log;
19 import org.chromium.base.ThreadUtils;
20
21 import java.util.concurrent.BlockingQueue;
22 import java.util.concurrent.Callable;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.LinkedBlockingQueue;
25
26 /**
27 * Chromium's implementation of {@link InputConnection} to communicate with exte rnal input method
28 * apps.
29 */
30 public class ChromiumInputConnection implements ChromiumBaseInputConnection {
31 private static final String TAG = "cr_Ime";
32
33 static class ThreadManager implements ChromiumBaseInputConnection.ThreadMana ger {
34 private final Handler mHandler;
35
36 public ThreadManager(Handler handler) {
37 mHandler = handler;
38 }
39
40 public Handler getHandler() {
41 return mHandler;
42 }
43
44 @Override
45 public boolean runningOnThisThread() {
46 return getHandler().getLooper() == Looper.myLooper();
47 }
48
49 @Override
50 public void post(Runnable runnable) {
51 getHandler().post(runnable);
52 }
53 }
54
55 private final ImeAdapter mImeAdapter;
56 private final ThreadManager mThreadManager;
57
58 private int mNumNestedBatchEdits = 0;
59
60 private final BlockingQueue<TextInputState> mQueue = new LinkedBlockingQueue <>();
61 private TextInputState mBatchEditPendingState;
62 // Only accessible on UI thread.
63 private TextInputState mLastUpdatedTextInputState;
64
65 ChromiumInputConnection(ImeAdapter imeAdapter, ThreadManager threadManager) {
66 Log.d(TAG, "constructor");
67 ImeUtils.assertOnUiThread();
68 mImeAdapter = imeAdapter;
69 mThreadManager = threadManager;
70 }
71
72 void initializeOutAttrsOnUiThread(int inputType, int inputFlags, EditorInfo outAttrs) {
73 Log.d(TAG, "initialize");
74 ImeUtils.assertOnUiThread();
75 int initialSelStart = 0;
76 int initialSelEnd = 0;
77 if (mLastUpdatedTextInputState != null) {
78 initialSelStart = mLastUpdatedTextInputState.selection().start();
79 initialSelEnd = mLastUpdatedTextInputState.selection().end();
80 }
81 ImeUtils.computeEditorInfo(inputType, inputFlags, initialSelStart, initi alSelEnd, outAttrs);
82 }
83
84 @Override
85 public void updateStateOnUiThread(final String text, final int selectionStar t,
86 final int selectionEnd, final int compositionStart, final int compos itionEnd,
87 boolean singleLine, final boolean isNonImeChange) {
88 Log.d(TAG, "updateState [%s] [%s %s] [%s %s] [%b] [%b]", text, selection Start, selectionEnd,
89 compositionStart, compositionEnd, singleLine, isNonImeChange);
90 ImeUtils.assertOnUiThread();
91
92 final TextInputState newState =
93 new TextInputState(text, new Range(selectionStart, selectionEnd) ,
94 new Range(compositionStart, compositionEnd), singleLine) ;
95 mLastUpdatedTextInputState = newState;
96
97 if (!isNonImeChange) {
98 addToQueueOnUiThread(newState);
99 }
100
101 mThreadManager.post(new Runnable() {
102 @Override
103 public void run() {
104 updateSelection(newState);
105 }
106 });
107 Log.d(TAG, "updateState finished");
108 }
109
110 @Override
111 public ThreadManager getThreadManager() {
112 return mThreadManager;
113 }
114
115 @Override
116 public void onRestartInputOnUiThread() {}
117
118 private void updateSelection(TextInputState textInputState) {
119 assertOnImeThread();
120 if (mNumNestedBatchEdits != 0) {
121 mBatchEditPendingState = textInputState;
122 return;
123 }
124 ImeUtils.assertReally(textInputState != null);
aelias_OOO_until_Jul13 2016/01/22 23:34:49 I applied the patch locally on ToT r371021 on Nexu
aelias_OOO_until_Jul13 2016/01/23 03:21:39 Thanks, it works fine now.
125 Range selection = textInputState.selection();
126 Range composition = textInputState.composition();
127 mImeAdapter.updateSelection(
128 selection.start(), selection.end(), composition.start(), composi tion.end());
129 }
130
131 private TextInputState requestTextInputState() {
132 Log.d(TAG, "requestTextInputState");
133 ThreadUtils.postOnUiThread(new Runnable() {
134 @Override
135 public void run() {
136 boolean result = mImeAdapter.requestTextInputStateUpdate();
137 if (!result) unblock();
138 }
139 });
140 return blockAndGetStateUpdate();
141 }
142
143 private void addToQueueOnUiThread(TextInputState textInputState) {
144 ImeUtils.assertOnUiThread();
145 try {
146 mQueue.put(textInputState);
147 } catch (InterruptedException e) {
148 Log.e(TAG, "onImeOriginatingStateUpdateOnUiThread interrupted", e);
149 }
150 Log.d(TAG, "onImeOriginatingStateUpdateOnUiThread finished: %d", mQueue. size());
151 }
152
153 /**
154 * Unblock the blockAndGetStateUpdate() function if we found that we will
155 * never get state update.
156 */
157 private void unblock() {
158 ImeUtils.assertOnUiThread();
159 addToQueueOnUiThread(new TextInputState.Unblocker());
160 }
161
162 private void assertOnImeThread() {
163 ImeUtils.assertReally(mThreadManager.runningOnThisThread());
164 }
165
166 /**
167 * Block until we get the expected state update.
168 * @return TextInputState if we get it successfully. null otherwise.
169 */
170 private TextInputState blockAndGetStateUpdate() {
171 assertOnImeThread();
172 while (true) {
173 TextInputState result = mQueue.poll();
174 if (result != null) {
175 if (result.shouldUnblock()) {
176 Log.d(TAG, "blockAndGetStateUpdate: unblocked");
177 return null;
178 }
179 Log.d(TAG, "blockAndGetStateUpdate finished: %s", result.toStrin g());
180 return result;
181 }
182 }
183 }
184
185 /**
186 * @see InputConnection#setComposingText(java.lang.CharSequence, int)
187 */
188 @Override
189 public boolean setComposingText(final CharSequence text, final int newCursor Position) {
190 Log.d(TAG, "setComposingText [%s] [%d]", text, newCursorPosition);
191 assertOnImeThread();
192 ThreadUtils.postOnUiThread(new Runnable() {
193 @Override
194 public void run() {
195 mImeAdapter.sendCompositionToNative(text, newCursorPosition, fal se);
196 }
197 });
198
199 return requestTextInputState() != null;
200 }
201
202 /**
203 * @see InputConnection#commitText(java.lang.CharSequence, int)
204 */
205 @Override
206 public boolean commitText(final CharSequence text, final int newCursorPositi on) {
207 Log.d(TAG, "commitText [%s] [%d]", text, newCursorPosition);
208 assertOnImeThread();
209 ThreadUtils.postOnUiThread(new Runnable() {
210 @Override
211 public void run() {
212 mImeAdapter.sendCompositionToNative(text, newCursorPosition, tex t.length() > 0);
213 }
214 });
215 return requestTextInputState() != null;
216 }
217
218 /**
219 * @see InputConnection#performEditorAction(int)
220 */
221 @Override
222 public boolean performEditorAction(final int actionCode) {
223 Log.d(TAG, "performEditorAction [%d]", actionCode);
224 assertOnImeThread();
225 try {
226 return ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
227 @Override
228 public Boolean call() {
229 return mImeAdapter.performEditorAction(actionCode);
230 }
231 });
232 } catch (ExecutionException e) {
233 e.printStackTrace();
234 return false;
235 }
236 }
237
238 /**
239 * @see InputConnection#performContextMenuAction(int)
240 */
241 @Override
242 public boolean performContextMenuAction(final int id) {
243 Log.d(TAG, "performContextMenuAction [%d]", id);
244 assertOnImeThread();
245 try {
246 return ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
247 @Override
248 public Boolean call() {
249 return mImeAdapter.performContextMenuAction(id);
250 }
251 });
252 } catch (ExecutionException e) {
253 e.printStackTrace();
254 return false;
255 }
256 }
257
258 /**
259 * @see InputConnection#getExtractedText(android.view.inputmethod.ExtractedT extRequest,
260 * int)
261 */
262 @Override
263 public ExtractedText getExtractedText(ExtractedTextRequest request, int flag s) {
264 Log.d(TAG, "getExtractedText");
265 assertOnImeThread();
266 TextInputState textInputState = requestTextInputState();
267 if (textInputState == null) return null;
268 ExtractedText extractedText = new ExtractedText();
269 extractedText.text = textInputState.text();
270 extractedText.partialEndOffset = textInputState.text().length();
271 extractedText.selectionStart = textInputState.selection().start();
272 extractedText.selectionEnd = textInputState.selection().end();
273 extractedText.flags = textInputState.singleLine() ? ExtractedText.FLAG_S INGLE_LINE : 0;
274 return extractedText;
275 }
276
277 /**
278 * @see InputConnection#beginBatchEdit()
279 */
280 @Override
281 public boolean beginBatchEdit() {
282 Log.d(TAG, "beginBatchEdit [%b]", (mNumNestedBatchEdits == 0));
283 assertOnImeThread();
284 mNumNestedBatchEdits++;
285 return true;
286 }
287
288 /**
289 * @see InputConnection#endBatchEdit()
290 */
291 @Override
292 public boolean endBatchEdit() {
293 assertOnImeThread();
294 if (mNumNestedBatchEdits == 0) return false;
295 --mNumNestedBatchEdits;
296 Log.d(TAG, "endBatchEdit [%b]", (mNumNestedBatchEdits == 0));
297 if (mNumNestedBatchEdits == 0) {
298 if (mBatchEditPendingState != null) {
299 // Make sure that this gets called after any other state update.
300 mThreadManager.post(new Runnable() {
301 @Override
302 public void run() {
303 updateSelection(mBatchEditPendingState);
304 }
305 });
306 mBatchEditPendingState = null;
307 }
308 }
309 return mNumNestedBatchEdits != 0;
310 }
311
312 /**
313 * @see InputConnection#deleteSurroundingText(int, int)
314 */
315 @Override
316 public boolean deleteSurroundingText(final int beforeLength, final int after Length) {
317 Log.d(TAG, "deleteSurroundingText [%d %d]", beforeLength, afterLength);
318 assertOnImeThread();
319 ThreadUtils.postOnUiThread(new Runnable() {
320 @Override
321 public void run() {
322 mImeAdapter.deleteSurroundingText(beforeLength, afterLength);
323 }
324 });
325 return requestTextInputState() != null;
326 }
327
328 /**
329 * @see ChromiumBaseInputConnection#sendKeyEventOnUiThread(KeyEvent)
330 */
331 @Override
332 public boolean sendKeyEventOnUiThread(final KeyEvent event) {
333 ImeUtils.assertOnUiThread();
334 mThreadManager.post(new Runnable() {
335 @Override
336 public void run() {
337 sendKeyEvent(event);
338 }
339 });
340 return true;
341 }
342
343 /**
344 * @see InputConnection#sendKeyEvent(android.view.KeyEvent)
345 */
346 @Override
347 public boolean sendKeyEvent(final KeyEvent event) {
348 Log.d(TAG, "sendKeyEvent [%d %d]", event.getAction(), event.getKeyCode() );
349 assertOnImeThread();
350 ThreadUtils.postOnUiThread(new Runnable() {
351 @Override
352 public void run() {
353 mImeAdapter.sendKeyEvent(event);
354 }
355 });
356 return requestTextInputState() != null;
357 }
358
359 /**
360 * @see InputConnection#finishComposingText()
361 */
362 @Override
363 public boolean finishComposingText() {
364 Log.d(TAG, "finishComposingText");
365 // This is the only function that may be called on UI thread because
366 // of direct calls from InputMethodManager.
367
368 ThreadUtils.postOnUiThread(new Runnable() {
369 @Override
370 public void run() {
371 mImeAdapter.finishComposingText();
372 }
373 });
374
375 if (ThreadUtils.runningOnUiThread()) {
376 Log.d(TAG, "finishComposingText was called on UI thread.");
377 return true;
378 } else {
379 return requestTextInputState() != null;
380 }
381 }
382
383 /**
384 * @see InputConnection#setSelection(int, int)
385 */
386 @Override
387 public boolean setSelection(final int start, final int end) {
388 Log.d(TAG, "setSelection [%d %d]", start, end);
389 assertOnImeThread();
390 ThreadUtils.postOnUiThread(new Runnable() {
391 @Override
392 public void run() {
393 mImeAdapter.setEditableSelectionOffsets(start, end);
394 }
395 });
396 return requestTextInputState() != null;
397 }
398
399 /**
400 * @see InputConnection#setComposingRegion(int, int)
401 */
402 @Override
403 public boolean setComposingRegion(final int start, final int end) {
404 Log.d(TAG, "setComposingRegion [%d %d]", start, end);
405 assertOnImeThread();
406 ThreadUtils.postOnUiThread(new Runnable() {
407 @Override
408 public void run() {
409 mImeAdapter.setComposingRegion(start, end);
410 }
411 });
412 return requestTextInputState() != null;
413 }
414
415 /**
416 * @see InputConnection#getTextBeforeCursor(int, int)
417 */
418 @Override
419 public CharSequence getTextBeforeCursor(int maxChars, int flags) {
420 Log.d(TAG, "getTextBeforeCursor [%d %x]", maxChars, flags);
421 assertOnImeThread();
422 TextInputState textInputState = requestTextInputState();
423 if (textInputState == null) return null;
424 return textInputState.getTextBeforeSelection(maxChars);
425 }
426
427 /**
428 * @see InputConnection#getTextAfterCursor(int, int)
429 */
430 @Override
431 public CharSequence getTextAfterCursor(int maxChars, int flags) {
432 Log.d(TAG, "getTextAfterCursor [%d %x]", maxChars, flags);
433 assertOnImeThread();
434 TextInputState textInputState = requestTextInputState();
435 if (textInputState == null) return null;
436 return textInputState.getTextAfterSelection(maxChars);
437 }
438
439 /**
440 * @see InputConnection#getSelectedText(int)
441 */
442 @Override
443 public CharSequence getSelectedText(int flags) {
444 Log.d(TAG, "getSelectedText [%x]", flags);
445 assertOnImeThread();
446 TextInputState textInputState = requestTextInputState();
447 if (textInputState == null) return null;
448 return textInputState.getSelectedText();
449 }
450
451 @Override
452 public void moveCursorToSelectionEndOnUiThread() {
453 mThreadManager.post(new Runnable() {
454 @Override
455 public void run() {
456 TextInputState textInputState = requestTextInputState();
457 if (textInputState == null) return;
458 Range selection = textInputState.selection();
459 setSelection(selection.end(), selection.end());
460 }
461 });
462 }
463
464 /**
465 * @see InputConnection#getCursorCapsMode(int)
466 */
467 @Override
468 public int getCursorCapsMode(int reqModes) {
469 Log.d(TAG, "getCursorCapsMode [%x]", reqModes);
470 assertOnImeThread();
471 // TODO(changwan): Auto-generated method stub
472 return 0;
473 }
474
475 /**
476 * @see InputConnection#commitCompletion(android.view.inputmethod.Completion Info)
477 */
478 @Override
479 public boolean commitCompletion(CompletionInfo text) {
480 Log.d(TAG, "commitCompletion [%s]", text);
481 assertOnImeThread();
482 // TODO(changwan): Auto-generated method stub
483 return false;
484 }
485
486 /**
487 * @see InputConnection#commitCorrection(android.view.inputmethod.Correction Info)
488 */
489 @Override
490 public boolean commitCorrection(CorrectionInfo correctionInfo) {
491 Log.d(TAG, "commitCorrection [%s]", ImeUtils.dumpCorrectionInfo(correcti onInfo));
492 assertOnImeThread();
493 // TODO(changwan): Auto-generated method stub
494 return false;
495 }
496
497 /**
498 * @see InputConnection#clearMetaKeyStates(int)
499 */
500 @Override
501 public boolean clearMetaKeyStates(int states) {
502 Log.d(TAG, "clearMetaKeyStates [%x]", states);
503 assertOnImeThread();
504 // TODO(changwan): Auto-generated method stub
505 return false;
506 }
507
508 /**
509 * @see InputConnection#reportFullscreenMode(boolean)
510 */
511 @Override
512 public boolean reportFullscreenMode(boolean enabled) {
513 Log.d(TAG, "reportFullscreenMode [%b]", enabled);
514 // We ignore fullscreen mode for now. That's why we set
515 // EditorInfo.IME_FLAG_NO_FULLSCREEN in constructor.
516 // Note that this may be called on UI thread.
517 return false;
518 }
519
520 /**
521 * @see InputConnection#performPrivateCommand(java.lang.String, android.os.B undle)
522 */
523 @Override
524 public boolean performPrivateCommand(String action, Bundle data) {
525 Log.d(TAG, "performPrivateCommand [%s]", action);
526 assertOnImeThread();
527 // TODO(changwan): Auto-generated method stub
528 return false;
529 }
530
531 /**
532 * @see InputConnection#requestCursorUpdates(int)
533 */
534 @Override
535 public boolean requestCursorUpdates(int cursorUpdateMode) {
536 Log.d(TAG, "requestCursorUpdates [%x]", cursorUpdateMode);
537 assertOnImeThread();
538 // TODO(changwan): Auto-generated method stub
539 return false;
540 }
541 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698