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; | 7 import android.os.SystemClock; |
8 import android.text.Editable; | 8 import android.text.Editable; |
9 import android.text.InputType; | 9 import android.text.InputType; |
10 import android.text.Selection; | 10 import android.text.Selection; |
11 import android.text.TextUtils; | 11 import android.text.TextUtils; |
12 import android.util.Log; | 12 import android.util.Log; |
| 13 import android.view.KeyCharacterMap; |
13 import android.view.KeyEvent; | 14 import android.view.KeyEvent; |
14 import android.view.View; | 15 import android.view.View; |
15 import android.view.inputmethod.BaseInputConnection; | 16 import android.view.inputmethod.BaseInputConnection; |
16 import android.view.inputmethod.EditorInfo; | 17 import android.view.inputmethod.EditorInfo; |
17 import android.view.inputmethod.ExtractedText; | 18 import android.view.inputmethod.ExtractedText; |
18 import android.view.inputmethod.ExtractedTextRequest; | 19 import android.view.inputmethod.ExtractedTextRequest; |
19 | 20 |
20 import org.chromium.base.VisibleForTesting; | 21 import org.chromium.base.VisibleForTesting; |
21 import org.chromium.blink_public.web.WebInputEventType; | 22 import org.chromium.blink_public.web.WebInputEventType; |
22 import org.chromium.blink_public.web.WebTextInputFlags; | 23 import org.chromium.blink_public.web.WebTextInputFlags; |
(...skipping 12 matching lines...) Expand all Loading... |
35 */ | 36 */ |
36 public static final int INVALID_SELECTION = -1; | 37 public static final int INVALID_SELECTION = -1; |
37 public static final int INVALID_COMPOSITION = -1; | 38 public static final int INVALID_COMPOSITION = -1; |
38 | 39 |
39 private final View mInternalView; | 40 private final View mInternalView; |
40 private final ImeAdapter mImeAdapter; | 41 private final ImeAdapter mImeAdapter; |
41 private final Editable mEditable; | 42 private final Editable mEditable; |
42 | 43 |
43 private boolean mSingleLine; | 44 private boolean mSingleLine; |
44 private int mNumNestedBatchEdits = 0; | 45 private int mNumNestedBatchEdits = 0; |
| 46 private int mPendingAccent; |
45 | 47 |
46 private int mLastUpdateSelectionStart = INVALID_SELECTION; | 48 private int mLastUpdateSelectionStart = INVALID_SELECTION; |
47 private int mLastUpdateSelectionEnd = INVALID_SELECTION; | 49 private int mLastUpdateSelectionEnd = INVALID_SELECTION; |
48 private int mLastUpdateCompositionStart = INVALID_COMPOSITION; | 50 private int mLastUpdateCompositionStart = INVALID_COMPOSITION; |
49 private int mLastUpdateCompositionEnd = INVALID_COMPOSITION; | 51 private int mLastUpdateCompositionEnd = INVALID_COMPOSITION; |
50 | 52 |
51 @VisibleForTesting | 53 @VisibleForTesting |
52 AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable, | 54 AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable, |
53 EditorInfo outAttrs) { | 55 EditorInfo outAttrs) { |
54 super(view, true); | 56 super(view, true); |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 mLastUpdateCompositionEnd = compositionEnd; | 237 mLastUpdateCompositionEnd = compositionEnd; |
236 } | 238 } |
237 | 239 |
238 /** | 240 /** |
239 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) | 241 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) |
240 */ | 242 */ |
241 @Override | 243 @Override |
242 public boolean setComposingText(CharSequence text, int newCursorPosition) { | 244 public boolean setComposingText(CharSequence text, int newCursorPosition) { |
243 if (DEBUG) Log.w(TAG, "setComposingText [" + text + "] [" + newCursorPos
ition + "]"); | 245 if (DEBUG) Log.w(TAG, "setComposingText [" + text + "] [" + newCursorPos
ition + "]"); |
244 if (maybePerformEmptyCompositionWorkaround(text)) return true; | 246 if (maybePerformEmptyCompositionWorkaround(text)) return true; |
| 247 mPendingAccent = 0; |
245 super.setComposingText(text, newCursorPosition); | 248 super.setComposingText(text, newCursorPosition); |
246 updateSelectionIfRequired(); | 249 updateSelectionIfRequired(); |
247 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos
ition, false); | 250 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos
ition, false); |
248 } | 251 } |
249 | 252 |
250 /** | 253 /** |
251 * @see BaseInputConnection#commitText(java.lang.CharSequence, int) | 254 * @see BaseInputConnection#commitText(java.lang.CharSequence, int) |
252 */ | 255 */ |
253 @Override | 256 @Override |
254 public boolean commitText(CharSequence text, int newCursorPosition) { | 257 public boolean commitText(CharSequence text, int newCursorPosition) { |
255 if (DEBUG) Log.w(TAG, "commitText [" + text + "] [" + newCursorPosition
+ "]"); | 258 if (DEBUG) Log.w(TAG, "commitText [" + text + "] [" + newCursorPosition
+ "]"); |
256 if (maybePerformEmptyCompositionWorkaround(text)) return true; | 259 if (maybePerformEmptyCompositionWorkaround(text)) return true; |
| 260 mPendingAccent = 0; |
257 super.commitText(text, newCursorPosition); | 261 super.commitText(text, newCursorPosition); |
258 updateSelectionIfRequired(); | 262 updateSelectionIfRequired(); |
259 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos
ition, | 263 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos
ition, |
260 text.length() > 0); | 264 text.length() > 0); |
261 } | 265 } |
262 | 266 |
263 /** | 267 /** |
264 * @see BaseInputConnection#performEditorAction(int) | 268 * @see BaseInputConnection#performEditorAction(int) |
265 */ | 269 */ |
266 @Override | 270 @Override |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
344 @Override | 348 @Override |
345 public boolean deleteSurroundingText(int beforeLength, int afterLength) { | 349 public boolean deleteSurroundingText(int beforeLength, int afterLength) { |
346 return deleteSurroundingTextImpl(beforeLength, afterLength, false); | 350 return deleteSurroundingTextImpl(beforeLength, afterLength, false); |
347 } | 351 } |
348 | 352 |
349 private boolean deleteSurroundingTextImpl( | 353 private boolean deleteSurroundingTextImpl( |
350 int beforeLength, int afterLength, boolean fromPhysicalKey) { | 354 int beforeLength, int afterLength, boolean fromPhysicalKey) { |
351 if (DEBUG) { | 355 if (DEBUG) { |
352 Log.w(TAG, "deleteSurroundingText [" + beforeLength + " " + afterLen
gth + "]"); | 356 Log.w(TAG, "deleteSurroundingText [" + beforeLength + " " + afterLen
gth + "]"); |
353 } | 357 } |
| 358 |
| 359 if (mPendingAccent != 0) { |
| 360 finishComposingText(); |
| 361 } |
| 362 |
354 int originalBeforeLength = beforeLength; | 363 int originalBeforeLength = beforeLength; |
355 int originalAfterLength = afterLength; | 364 int originalAfterLength = afterLength; |
356 int availableBefore = Selection.getSelectionStart(mEditable); | 365 int availableBefore = Selection.getSelectionStart(mEditable); |
357 int availableAfter = mEditable.length() - Selection.getSelectionEnd(mEdi
table); | 366 int availableAfter = mEditable.length() - Selection.getSelectionEnd(mEdi
table); |
358 beforeLength = Math.min(beforeLength, availableBefore); | 367 beforeLength = Math.min(beforeLength, availableBefore); |
359 afterLength = Math.min(afterLength, availableAfter); | 368 afterLength = Math.min(afterLength, availableAfter); |
360 super.deleteSurroundingText(beforeLength, afterLength); | 369 super.deleteSurroundingText(beforeLength, afterLength); |
361 updateSelectionIfRequired(); | 370 updateSelectionIfRequired(); |
362 | 371 |
363 // If this was called due to a physical key, no need to generate a key e
vent here as | 372 // If this was called due to a physical key, no need to generate a key e
vent here as |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 @Override | 405 @Override |
397 public boolean sendKeyEvent(KeyEvent event) { | 406 public boolean sendKeyEvent(KeyEvent event) { |
398 if (DEBUG) { | 407 if (DEBUG) { |
399 Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getK
eyCode() + "]"); | 408 Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getK
eyCode() + "]"); |
400 } | 409 } |
401 | 410 |
402 int action = event.getAction(); | 411 int action = event.getAction(); |
403 int keycode = event.getKeyCode(); | 412 int keycode = event.getKeyCode(); |
404 int unicodeChar = event.getUnicodeChar(); | 413 int unicodeChar = event.getUnicodeChar(); |
405 | 414 |
| 415 // If this isn't a KeyDown event, no need to update composition state; j
ust pass the key |
| 416 // event through and return. |
| 417 if (action != KeyEvent.ACTION_DOWN) { |
| 418 mImeAdapter.translateAndSendNativeEvents(event); |
| 419 return true; |
| 420 } |
| 421 |
406 // If this is backspace/del or if the key has a character representation
, | 422 // If this is backspace/del or if the key has a character representation
, |
407 // need to update the underlying Editable (i.e. the local representation
of the text | 423 // need to update the underlying Editable (i.e. the local representation
of the text |
408 // being edited). Some IMEs like Jellybean stock IME and Samsung IME mi
x in delete | 424 // being edited). Some IMEs like Jellybean stock IME and Samsung IME mi
x in delete |
409 // KeyPress events instead of calling deleteSurroundingText. | 425 // KeyPress events instead of calling deleteSurroundingText. |
410 if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE_DEL) { | 426 if (keycode == KeyEvent.KEYCODE_DEL) { |
411 deleteSurroundingTextImpl(1, 0, true); | 427 deleteSurroundingTextImpl(1, 0, true); |
412 } else if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE
_FORWARD_DEL) { | 428 } else if (keycode == KeyEvent.KEYCODE_FORWARD_DEL) { |
413 deleteSurroundingTextImpl(0, 1, true); | 429 deleteSurroundingTextImpl(0, 1, true); |
414 } else if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE
_ENTER) { | 430 } else if (keycode == KeyEvent.KEYCODE_ENTER) { |
415 // Finish text composition when pressing enter, as that may submit a
form field. | 431 // Finish text composition when pressing enter, as that may submit a
form field. |
416 // TODO(aurimas): remove this workaround when crbug.com/278584 is fi
xed. | 432 // TODO(aurimas): remove this workaround when crbug.com/278584 is fi
xed. |
417 beginBatchEdit(); | 433 beginBatchEdit(); |
418 finishComposingText(); | 434 finishComposingText(); |
419 mImeAdapter.translateAndSendNativeEvents(event); | 435 mImeAdapter.translateAndSendNativeEvents(event); |
420 endBatchEdit(); | 436 endBatchEdit(); |
421 return true; | 437 return true; |
422 } else if (action == KeyEvent.ACTION_UP && unicodeChar != 0) { | 438 } else if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) { |
| 439 // Store a pending accent character and make it the current composit
ion. |
| 440 int pendingAccent = unicodeChar & KeyCharacterMap.COMBINING_ACCENT_M
ASK; |
| 441 StringBuilder builder = new StringBuilder(); |
| 442 builder.appendCodePoint(pendingAccent); |
| 443 setComposingText(builder.toString(), 1); |
| 444 mPendingAccent = pendingAccent; |
| 445 return true; |
| 446 } |
| 447 |
| 448 if (unicodeChar != 0) { |
| 449 if (mPendingAccent != 0) { |
| 450 int combined = KeyEvent.getDeadChar(mPendingAccent, unicodeChar)
; |
| 451 if (combined != 0) { |
| 452 StringBuilder builder = new StringBuilder(); |
| 453 builder.appendCodePoint(combined); |
| 454 commitText(builder.toString(), 1); |
| 455 return true; |
| 456 } |
| 457 // Noncombinable character; commit the accent character and fall
through to sending |
| 458 // the key event for the character afterwards. |
| 459 finishComposingText(); |
| 460 } |
| 461 |
| 462 // Update the mEditable state to reflect what Blink will do in respo
nse to the KeyDown |
| 463 // for a unicode-mapped key event. |
423 int selectionStart = Selection.getSelectionStart(mEditable); | 464 int selectionStart = Selection.getSelectionStart(mEditable); |
424 int selectionEnd = Selection.getSelectionEnd(mEditable); | 465 int selectionEnd = Selection.getSelectionEnd(mEditable); |
425 if (selectionStart > selectionEnd) { | 466 if (selectionStart > selectionEnd) { |
426 int temp = selectionStart; | 467 int temp = selectionStart; |
427 selectionStart = selectionEnd; | 468 selectionStart = selectionEnd; |
428 selectionEnd = temp; | 469 selectionEnd = temp; |
429 } | 470 } |
430 mEditable.replace(selectionStart, selectionEnd, | 471 mEditable.replace(selectionStart, selectionEnd, |
431 Character.toString((char) unicodeChar)); | 472 Character.toString((char) unicodeChar)); |
432 } | 473 } |
| 474 |
433 mImeAdapter.translateAndSendNativeEvents(event); | 475 mImeAdapter.translateAndSendNativeEvents(event); |
434 return true; | 476 return true; |
435 } | 477 } |
436 | 478 |
437 /** | 479 /** |
438 * @see BaseInputConnection#finishComposingText() | 480 * @see BaseInputConnection#finishComposingText() |
439 */ | 481 */ |
440 @Override | 482 @Override |
441 public boolean finishComposingText() { | 483 public boolean finishComposingText() { |
442 if (DEBUG) Log.w(TAG, "finishComposingText"); | 484 if (DEBUG) Log.w(TAG, "finishComposingText"); |
| 485 |
| 486 mPendingAccent = 0; |
| 487 |
443 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable))
{ | 488 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable))
{ |
444 return true; | 489 return true; |
445 } | 490 } |
446 | 491 |
447 super.finishComposingText(); | 492 super.finishComposingText(); |
448 updateSelectionIfRequired(); | 493 updateSelectionIfRequired(); |
449 mImeAdapter.finishComposingText(); | 494 mImeAdapter.finishComposingText(); |
450 | 495 |
451 return true; | 496 return true; |
452 } | 497 } |
(...skipping 12 matching lines...) Expand all Loading... |
465 } | 510 } |
466 | 511 |
467 /** | 512 /** |
468 * Informs the InputMethodManager and InputMethodSession (i.e. the IME) that
the text | 513 * Informs the InputMethodManager and InputMethodSession (i.e. the IME) that
the text |
469 * state is no longer what the IME has and that it needs to be updated. | 514 * state is no longer what the IME has and that it needs to be updated. |
470 */ | 515 */ |
471 void restartInput() { | 516 void restartInput() { |
472 if (DEBUG) Log.w(TAG, "restartInput"); | 517 if (DEBUG) Log.w(TAG, "restartInput"); |
473 getInputMethodManagerWrapper().restartInput(mInternalView); | 518 getInputMethodManagerWrapper().restartInput(mInternalView); |
474 mNumNestedBatchEdits = 0; | 519 mNumNestedBatchEdits = 0; |
| 520 mPendingAccent = 0; |
475 } | 521 } |
476 | 522 |
477 /** | 523 /** |
478 * @see BaseInputConnection#setComposingRegion(int, int) | 524 * @see BaseInputConnection#setComposingRegion(int, int) |
479 */ | 525 */ |
480 @Override | 526 @Override |
481 public boolean setComposingRegion(int start, int end) { | 527 public boolean setComposingRegion(int start, int end) { |
482 if (DEBUG) Log.w(TAG, "setComposingRegion [" + start + " " + end + "]"); | 528 if (DEBUG) Log.w(TAG, "setComposingRegion [" + start + " " + end + "]"); |
483 int textLength = mEditable.length(); | 529 int textLength = mEditable.length(); |
484 int a = Math.min(start, end); | 530 int a = Math.min(start, end); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
558 @VisibleForTesting | 604 @VisibleForTesting |
559 ImeState getImeStateForTesting() { | 605 ImeState getImeStateForTesting() { |
560 String text = mEditable.toString(); | 606 String text = mEditable.toString(); |
561 int selectionStart = Selection.getSelectionStart(mEditable); | 607 int selectionStart = Selection.getSelectionStart(mEditable); |
562 int selectionEnd = Selection.getSelectionEnd(mEditable); | 608 int selectionEnd = Selection.getSelectionEnd(mEditable); |
563 int compositionStart = getComposingSpanStart(mEditable); | 609 int compositionStart = getComposingSpanStart(mEditable); |
564 int compositionEnd = getComposingSpanEnd(mEditable); | 610 int compositionEnd = getComposingSpanEnd(mEditable); |
565 return new ImeState(text, selectionStart, selectionEnd, compositionStart
, compositionEnd); | 611 return new ImeState(text, selectionStart, selectionEnd, compositionStart
, compositionEnd); |
566 } | 612 } |
567 } | 613 } |
OLD | NEW |