| 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 |