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