Chromium Code Reviews| 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 mPendingAccent = 0; | |
| 361 finishComposingText(); | |
| 362 } | |
| 363 | |
| 354 int originalBeforeLength = beforeLength; | 364 int originalBeforeLength = beforeLength; |
| 355 int originalAfterLength = afterLength; | 365 int originalAfterLength = afterLength; |
| 356 int availableBefore = Selection.getSelectionStart(mEditable); | 366 int availableBefore = Selection.getSelectionStart(mEditable); |
| 357 int availableAfter = mEditable.length() - Selection.getSelectionEnd(mEdi table); | 367 int availableAfter = mEditable.length() - Selection.getSelectionEnd(mEdi table); |
| 358 beforeLength = Math.min(beforeLength, availableBefore); | 368 beforeLength = Math.min(beforeLength, availableBefore); |
| 359 afterLength = Math.min(afterLength, availableAfter); | 369 afterLength = Math.min(afterLength, availableAfter); |
| 360 super.deleteSurroundingText(beforeLength, afterLength); | 370 super.deleteSurroundingText(beforeLength, afterLength); |
| 361 updateSelectionIfRequired(); | 371 updateSelectionIfRequired(); |
| 362 | 372 |
| 363 // If this was called due to a physical key, no need to generate a key e vent here as | 373 // 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 | 406 @Override |
| 397 public boolean sendKeyEvent(KeyEvent event) { | 407 public boolean sendKeyEvent(KeyEvent event) { |
| 398 if (DEBUG) { | 408 if (DEBUG) { |
| 399 Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getK eyCode() + "]"); | 409 Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getK eyCode() + "]"); |
| 400 } | 410 } |
| 401 | 411 |
| 402 int action = event.getAction(); | 412 int action = event.getAction(); |
| 403 int keycode = event.getKeyCode(); | 413 int keycode = event.getKeyCode(); |
| 404 int unicodeChar = event.getUnicodeChar(); | 414 int unicodeChar = event.getUnicodeChar(); |
| 405 | 415 |
| 416 // If we're not focused in a textbox or if this isn't a KeyDown event, n o need to update | |
| 417 // composition state; just pass the key event through and return. | |
| 418 if (mImeAdapter.getTextInputType() == TextInputType.NONE | |
| 419 || action != KeyEvent.ACTION_DOWN) { | |
|
aurimas (slooooooooow)
2015/06/03 00:44:20
Would this result in us sending KeyUp events witho
aelias_OOO_until_Jul13
2015/06/03 01:15:01
Yes, it does and I spent some time looking into if
bcwhite
2015/06/03 15:28:58
I've been trying this patch with a physical keyboa
aelias_OOO_until_Jul13
2015/06/03 21:10:29
Good catch, thanks. It looks like this is a side
| |
| 420 mImeAdapter.translateAndSendNativeEvents(event); | |
| 421 return true; | |
| 422 } | |
| 423 | |
| 406 // If this is backspace/del or if the key has a character representation , | 424 // 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 | 425 // 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 | 426 // being edited). Some IMEs like Jellybean stock IME and Samsung IME mi x in delete |
| 409 // KeyPress events instead of calling deleteSurroundingText. | 427 // KeyPress events instead of calling deleteSurroundingText. |
| 410 if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE_DEL) { | 428 if (keycode == KeyEvent.KEYCODE_DEL) { |
| 411 deleteSurroundingTextImpl(1, 0, true); | 429 deleteSurroundingTextImpl(1, 0, true); |
| 412 } else if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE _FORWARD_DEL) { | 430 } else if (keycode == KeyEvent.KEYCODE_FORWARD_DEL) { |
|
aurimas (slooooooooow)
2015/06/03 00:44:20
What would happen if we have DEL, FORWARD_DEL or E
aelias_OOO_until_Jul13
2015/06/03 01:15:01
I did try out those scenarios earlier today and ha
| |
| 413 deleteSurroundingTextImpl(0, 1, true); | 431 deleteSurroundingTextImpl(0, 1, true); |
| 414 } else if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE _ENTER) { | 432 } else if (keycode == KeyEvent.KEYCODE_ENTER) { |
| 415 // Finish text composition when pressing enter, as that may submit a form field. | 433 // 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. | 434 // TODO(aurimas): remove this workaround when crbug.com/278584 is fi xed. |
| 417 beginBatchEdit(); | 435 beginBatchEdit(); |
| 418 finishComposingText(); | 436 finishComposingText(); |
| 419 mImeAdapter.translateAndSendNativeEvents(event); | 437 mImeAdapter.translateAndSendNativeEvents(event); |
| 420 endBatchEdit(); | 438 endBatchEdit(); |
| 421 return true; | 439 return true; |
| 422 } else if (action == KeyEvent.ACTION_UP && unicodeChar != 0) { | 440 } else if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) { |
| 441 // Store a pending accent character and make it the current composit ion. | |
| 442 int pendingAccent = unicodeChar & KeyCharacterMap.COMBINING_ACCENT_M ASK; | |
| 443 StringBuilder builder = new StringBuilder(); | |
| 444 builder.appendCodePoint(pendingAccent); | |
| 445 setComposingText(builder.toString(), 1); | |
| 446 mPendingAccent = pendingAccent; | |
| 447 return true; | |
| 448 } | |
| 449 | |
| 450 if (unicodeChar != 0) { | |
| 451 if (mPendingAccent != 0) { | |
| 452 int combined = KeyEvent.getDeadChar(mPendingAccent, unicodeChar) ; | |
| 453 if (combined != 0) { | |
| 454 StringBuilder builder = new StringBuilder(); | |
| 455 builder.appendCodePoint(combined); | |
| 456 commitText(builder.toString(), 1); | |
| 457 return true; | |
| 458 } | |
| 459 // Noncombinable character; commit the accent character and fall through to sending | |
| 460 // the key event for the character afterwards. | |
| 461 finishComposingText(); | |
| 462 } | |
| 463 | |
| 464 // Update the mEditable state to reflect what Blink will do in respo nse to the KeyDown | |
| 465 // for a unicode-mapped key event. | |
| 423 int selectionStart = Selection.getSelectionStart(mEditable); | 466 int selectionStart = Selection.getSelectionStart(mEditable); |
| 424 int selectionEnd = Selection.getSelectionEnd(mEditable); | 467 int selectionEnd = Selection.getSelectionEnd(mEditable); |
| 425 if (selectionStart > selectionEnd) { | 468 if (selectionStart > selectionEnd) { |
| 426 int temp = selectionStart; | 469 int temp = selectionStart; |
| 427 selectionStart = selectionEnd; | 470 selectionStart = selectionEnd; |
| 428 selectionEnd = temp; | 471 selectionEnd = temp; |
| 429 } | 472 } |
| 430 mEditable.replace(selectionStart, selectionEnd, | 473 mEditable.replace(selectionStart, selectionEnd, |
| 431 Character.toString((char) unicodeChar)); | 474 Character.toString((char) unicodeChar)); |
| 432 } | 475 } |
| 476 | |
| 433 mImeAdapter.translateAndSendNativeEvents(event); | 477 mImeAdapter.translateAndSendNativeEvents(event); |
| 434 return true; | 478 return true; |
| 435 } | 479 } |
| 436 | 480 |
| 437 /** | 481 /** |
| 438 * @see BaseInputConnection#finishComposingText() | 482 * @see BaseInputConnection#finishComposingText() |
| 439 */ | 483 */ |
| 440 @Override | 484 @Override |
| 441 public boolean finishComposingText() { | 485 public boolean finishComposingText() { |
| 442 if (DEBUG) Log.w(TAG, "finishComposingText"); | 486 if (DEBUG) Log.w(TAG, "finishComposingText"); |
| 443 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable)) { | 487 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable)) { |
| 444 return true; | 488 return true; |
| 445 } | 489 } |
| 446 | 490 |
| 491 mPendingAccent = 0; | |
| 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 } |
| 453 | 498 |
| 454 /** | 499 /** |
| 455 * @see BaseInputConnection#setSelection(int, int) | 500 * @see BaseInputConnection#setSelection(int, int) |
| 456 */ | 501 */ |
| 457 @Override | 502 @Override |
| 458 public boolean setSelection(int start, int end) { | 503 public boolean setSelection(int start, int end) { |
| 459 if (DEBUG) Log.w(TAG, "setSelection [" + start + " " + end + "]"); | 504 if (DEBUG) Log.w(TAG, "setSelection [" + start + " " + end + "]"); |
| 460 int textLength = mEditable.length(); | 505 int textLength = mEditable.length(); |
| 461 if (start < 0 || end < 0 || start > textLength || end > textLength) retu rn true; | 506 if (start < 0 || end < 0 || start > textLength || end > textLength) retu rn true; |
| 462 super.setSelection(start, end); | 507 super.setSelection(start, end); |
| 463 updateSelectionIfRequired(); | 508 updateSelectionIfRequired(); |
| 464 return mImeAdapter.setEditableSelectionOffsets(start, end); | 509 return mImeAdapter.setEditableSelectionOffsets(start, end); |
| 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 |