OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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.os.SystemClock; | |
8 import android.text.Editable; | 7 import android.text.Editable; |
9 import android.text.InputType; | 8 import android.text.InputType; |
10 import android.text.Selection; | 9 import android.text.Selection; |
11 import android.text.TextUtils; | 10 import android.text.TextUtils; |
12 import android.util.StringBuilderPrinter; | 11 import android.util.StringBuilderPrinter; |
13 import android.view.KeyCharacterMap; | 12 import android.view.KeyCharacterMap; |
14 import android.view.KeyEvent; | 13 import android.view.KeyEvent; |
15 import android.view.View; | 14 import android.view.View; |
16 import android.view.inputmethod.BaseInputConnection; | 15 import android.view.inputmethod.BaseInputConnection; |
17 import android.view.inputmethod.EditorInfo; | 16 import android.view.inputmethod.EditorInfo; |
18 import android.view.inputmethod.ExtractedText; | 17 import android.view.inputmethod.ExtractedText; |
19 import android.view.inputmethod.ExtractedTextRequest; | 18 import android.view.inputmethod.ExtractedTextRequest; |
20 | 19 |
21 import org.chromium.base.Log; | 20 import org.chromium.base.Log; |
22 import org.chromium.base.VisibleForTesting; | 21 import org.chromium.base.VisibleForTesting; |
23 import org.chromium.blink_public.web.WebInputEventType; | |
24 import org.chromium.blink_public.web.WebTextInputFlags; | 22 import org.chromium.blink_public.web.WebTextInputFlags; |
25 import org.chromium.ui.base.ime.TextInputType; | 23 import org.chromium.ui.base.ime.TextInputType; |
26 | 24 |
27 /** | 25 /** |
28 * InputConnection is created by ContentView.onCreateInputConnection. | 26 * InputConnection is created by ContentView.onCreateInputConnection. |
29 * It then adapts android's IME to chrome's RenderWidgetHostView using the | 27 * It then adapts android's IME to chrome's RenderWidgetHostView using the |
30 * native ImeAdapterAndroid via the class ImeAdapter. | 28 * native ImeAdapterAndroid via the class ImeAdapter. |
31 */ | 29 */ |
32 public class AdapterInputConnection extends BaseInputConnection { | 30 public class AdapterInputConnection extends BaseInputConnection { |
33 private static final String TAG = "cr.Ime"; | 31 private static final String TAG = "cr.Ime"; |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 /** | 244 /** |
247 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) | 245 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) |
248 */ | 246 */ |
249 @Override | 247 @Override |
250 public boolean setComposingText(CharSequence text, int newCursorPosition) { | 248 public boolean setComposingText(CharSequence text, int newCursorPosition) { |
251 Log.d(TAG, "setComposingText [%s] [%d]", text, newCursorPosition); | 249 Log.d(TAG, "setComposingText [%s] [%d]", text, newCursorPosition); |
252 if (maybePerformEmptyCompositionWorkaround(text)) return true; | 250 if (maybePerformEmptyCompositionWorkaround(text)) return true; |
253 mPendingAccent = 0; | 251 mPendingAccent = 0; |
254 super.setComposingText(text, newCursorPosition); | 252 super.setComposingText(text, newCursorPosition); |
255 updateSelectionIfRequired(); | 253 updateSelectionIfRequired(); |
256 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos
ition, false); | 254 return mImeAdapter.sendCompositionToNative(text, newCursorPosition, fals
e); |
257 } | 255 } |
258 | 256 |
259 /** | 257 /** |
260 * @see BaseInputConnection#commitText(java.lang.CharSequence, int) | 258 * @see BaseInputConnection#commitText(java.lang.CharSequence, int) |
261 */ | 259 */ |
262 @Override | 260 @Override |
263 public boolean commitText(CharSequence text, int newCursorPosition) { | 261 public boolean commitText(CharSequence text, int newCursorPosition) { |
264 Log.d(TAG, "commitText [%s] [%d]", text, newCursorPosition); | 262 Log.d(TAG, "commitText [%s] [%d]", text, newCursorPosition); |
265 if (maybePerformEmptyCompositionWorkaround(text)) return true; | 263 if (maybePerformEmptyCompositionWorkaround(text)) return true; |
266 mPendingAccent = 0; | 264 mPendingAccent = 0; |
267 super.commitText(text, newCursorPosition); | 265 super.commitText(text, newCursorPosition); |
268 updateSelectionIfRequired(); | 266 updateSelectionIfRequired(); |
269 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos
ition, | 267 return mImeAdapter.sendCompositionToNative(text, newCursorPosition, text
.length() > 0); |
270 text.length() > 0); | |
271 } | 268 } |
272 | 269 |
273 /** | 270 /** |
274 * @see BaseInputConnection#performEditorAction(int) | 271 * @see BaseInputConnection#performEditorAction(int) |
275 */ | 272 */ |
276 @Override | 273 @Override |
277 public boolean performEditorAction(int actionCode) { | 274 public boolean performEditorAction(int actionCode) { |
278 Log.d(TAG, "performEditorAction [%d]", actionCode); | 275 Log.d(TAG, "performEditorAction [%d]", actionCode); |
279 if (actionCode == EditorInfo.IME_ACTION_NEXT) { | 276 return mImeAdapter.performEditorAction(actionCode); |
280 restartInput(); | |
281 // Send TAB key event | |
282 long timeStampMs = SystemClock.uptimeMillis(); | |
283 mImeAdapter.sendSyntheticKeyEvent( | |
284 WebInputEventType.RawKeyDown, timeStampMs, KeyEvent.KEYCODE_
TAB, 0, 0); | |
285 } else { | |
286 mImeAdapter.sendKeyEventWithKeyCode(KeyEvent.KEYCODE_ENTER, | |
287 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE | |
288 | KeyEvent.FLAG_EDITOR_ACTION); | |
289 } | |
290 return true; | |
291 } | 277 } |
292 | 278 |
293 /** | 279 /** |
294 * @see BaseInputConnection#performContextMenuAction(int) | 280 * @see BaseInputConnection#performContextMenuAction(int) |
295 */ | 281 */ |
296 @Override | 282 @Override |
297 public boolean performContextMenuAction(int id) { | 283 public boolean performContextMenuAction(int id) { |
298 Log.d(TAG, "performContextMenuAction [%d]", id); | 284 Log.d(TAG, "performContextMenuAction [%d]", id); |
299 return mImeAdapter.performContextMenuAction(id); | 285 return mImeAdapter.performContextMenuAction(id); |
300 } | 286 } |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 | 371 |
386 super.deleteSurroundingText(beforeLength, afterLength); | 372 super.deleteSurroundingText(beforeLength, afterLength); |
387 updateSelectionIfRequired(); | 373 updateSelectionIfRequired(); |
388 | 374 |
389 // If this was called due to a physical key, no need to generate a key e
vent here as | 375 // If this was called due to a physical key, no need to generate a key e
vent here as |
390 // the caller will take care of forwarding the original. | 376 // the caller will take care of forwarding the original. |
391 if (fromPhysicalKey) { | 377 if (fromPhysicalKey) { |
392 return true; | 378 return true; |
393 } | 379 } |
394 | 380 |
395 // For single-char deletion calls |ImeAdapter.sendKeyEventWithKeyCode| w
ith the real key | 381 return mImeAdapter.deleteSurroundingText(beforeLength, afterLength); |
396 // code. For multi-character deletion, executes deletion by calling | |
397 // |ImeAdapter.deleteSurroundingText| and sends synthetic key events wit
h a dummy key code. | |
398 int keyCode = KeyEvent.KEYCODE_UNKNOWN; | |
399 if (originalBeforeLength == 1 && originalAfterLength == 0) { | |
400 keyCode = KeyEvent.KEYCODE_DEL; | |
401 } else if (originalBeforeLength == 0 && originalAfterLength == 1) { | |
402 keyCode = KeyEvent.KEYCODE_FORWARD_DEL; | |
403 } | |
404 | |
405 boolean result = true; | |
406 if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { | |
407 result = mImeAdapter.sendSyntheticKeyEvent( | |
408 WebInputEventType.RawKeyDown, SystemClock.uptimeMillis(), ke
yCode, 0, 0); | |
409 result &= mImeAdapter.deleteSurroundingText(beforeLength, afterLengt
h); | |
410 result &= mImeAdapter.sendSyntheticKeyEvent( | |
411 WebInputEventType.KeyUp, SystemClock.uptimeMillis(), keyCode
, 0, 0); | |
412 } else { | |
413 mImeAdapter.sendKeyEventWithKeyCode( | |
414 keyCode, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TO
UCH_MODE); | |
415 } | |
416 return result; | |
417 } | 382 } |
418 | 383 |
419 /** | 384 /** |
420 * @see BaseInputConnection#sendKeyEvent(android.view.KeyEvent) | 385 * @see BaseInputConnection#sendKeyEvent(android.view.KeyEvent) |
421 */ | 386 */ |
422 @Override | 387 @Override |
423 public boolean sendKeyEvent(KeyEvent event) { | 388 public boolean sendKeyEvent(KeyEvent event) { |
424 Log.d(TAG, "sendKeyEvent [%d] [%d] [%d]", event.getAction(), event.getKe
yCode(), | 389 Log.d(TAG, "sendKeyEvent [%d] [%d] [%d]", event.getAction(), event.getKe
yCode(), |
425 event.getUnicodeChar()); | 390 event.getUnicodeChar()); |
426 | 391 |
427 int action = event.getAction(); | 392 int action = event.getAction(); |
428 int keycode = event.getKeyCode(); | 393 int keycode = event.getKeyCode(); |
429 int unicodeChar = event.getUnicodeChar(); | 394 int unicodeChar = event.getUnicodeChar(); |
430 | 395 |
431 // If this isn't a KeyDown event, no need to update composition state; j
ust pass the key | 396 // If this isn't a KeyDown event, no need to update composition state; j
ust pass the key |
432 // event through and return. But note that some keys, such as enter, may
actually be | 397 // event through and return. But note that some keys, such as enter, may
actually be |
433 // handled on ACTION_UP in Blink. | 398 // handled on ACTION_UP in Blink. |
434 if (action != KeyEvent.ACTION_DOWN) { | 399 if (action != KeyEvent.ACTION_DOWN) { |
435 mImeAdapter.translateAndSendNativeEvents(event); | 400 mImeAdapter.sendKeyEvent(event); |
436 return true; | 401 return true; |
437 } | 402 } |
438 | 403 |
439 // If this is backspace/del or if the key has a character representation
, | 404 // If this is backspace/del or if the key has a character representation
, |
440 // need to update the underlying Editable (i.e. the local representation
of the text | 405 // need to update the underlying Editable (i.e. the local representation
of the text |
441 // being edited). Some IMEs like Jellybean stock IME and Samsung IME mi
x in delete | 406 // being edited). Some IMEs like Jellybean stock IME and Samsung IME mi
x in delete |
442 // KeyPress events instead of calling deleteSurroundingText. | 407 // KeyPress events instead of calling deleteSurroundingText. |
443 if (keycode == KeyEvent.KEYCODE_DEL) { | 408 if (keycode == KeyEvent.KEYCODE_DEL) { |
444 deleteSurroundingTextImpl(1, 0, true); | 409 deleteSurroundingTextImpl(1, 0, true); |
445 } else if (keycode == KeyEvent.KEYCODE_FORWARD_DEL) { | 410 } else if (keycode == KeyEvent.KEYCODE_FORWARD_DEL) { |
(...skipping 16 matching lines...) Expand all Loading... |
462 StringBuilder builder = new StringBuilder(); | 427 StringBuilder builder = new StringBuilder(); |
463 builder.appendCodePoint(combined); | 428 builder.appendCodePoint(combined); |
464 commitText(builder.toString(), 1); | 429 commitText(builder.toString(), 1); |
465 return true; | 430 return true; |
466 } | 431 } |
467 // Noncombinable character; commit the accent character and fall thr
ough to sending | 432 // Noncombinable character; commit the accent character and fall thr
ough to sending |
468 // the key event for the character afterwards. | 433 // the key event for the character afterwards. |
469 finishComposingText(); | 434 finishComposingText(); |
470 } | 435 } |
471 replaceSelectionWithUnicodeChar(unicodeChar); | 436 replaceSelectionWithUnicodeChar(unicodeChar); |
472 mImeAdapter.translateAndSendNativeEvents(event); | 437 mImeAdapter.sendKeyEvent(event); |
473 return true; | 438 return true; |
474 } | 439 } |
475 | 440 |
476 /** | 441 /** |
477 * Update the mEditable state to reflect what Blink will do in response to t
he KeyDown | 442 * Update the mEditable state to reflect what Blink will do in response to t
he KeyDown |
478 * for a unicode-mapped key event. | 443 * for a unicode-mapped key event. |
479 * @param unicodeChar The Unicode character to update selection with. | 444 * @param unicodeChar The Unicode character to update selection with. |
480 */ | 445 */ |
481 private void replaceSelectionWithUnicodeChar(int unicodeChar) { | 446 private void replaceSelectionWithUnicodeChar(int unicodeChar) { |
482 if (unicodeChar == 0) return; | 447 if (unicodeChar == 0) return; |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
618 @VisibleForTesting | 583 @VisibleForTesting |
619 ImeState getImeStateForTesting() { | 584 ImeState getImeStateForTesting() { |
620 String text = mEditable.toString(); | 585 String text = mEditable.toString(); |
621 int selectionStart = Selection.getSelectionStart(mEditable); | 586 int selectionStart = Selection.getSelectionStart(mEditable); |
622 int selectionEnd = Selection.getSelectionEnd(mEditable); | 587 int selectionEnd = Selection.getSelectionEnd(mEditable); |
623 int compositionStart = getComposingSpanStart(mEditable); | 588 int compositionStart = getComposingSpanStart(mEditable); |
624 int compositionEnd = getComposingSpanEnd(mEditable); | 589 int compositionEnd = getComposingSpanEnd(mEditable); |
625 return new ImeState(text, selectionStart, selectionEnd, compositionStart
, compositionEnd); | 590 return new ImeState(text, selectionStart, selectionEnd, compositionStart
, compositionEnd); |
626 } | 591 } |
627 } | 592 } |
OLD | NEW |