| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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.content.res.Configuration; | 7 import android.content.res.Configuration; |
| 8 import android.os.Handler; | 8 import android.os.Handler; |
| 9 import android.os.ResultReceiver; | 9 import android.os.ResultReceiver; |
| 10 import android.os.SystemClock; | 10 import android.os.SystemClock; |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 static char[] sSingleCharArray = new char[1]; | 102 static char[] sSingleCharArray = new char[1]; |
| 103 static KeyCharacterMap sKeyCharacterMap; | 103 static KeyCharacterMap sKeyCharacterMap; |
| 104 | 104 |
| 105 private long mNativeImeAdapterAndroid; | 105 private long mNativeImeAdapterAndroid; |
| 106 private InputMethodManagerWrapper mInputMethodManagerWrapper; | 106 private InputMethodManagerWrapper mInputMethodManagerWrapper; |
| 107 private AdapterInputConnection mInputConnection; | 107 private AdapterInputConnection mInputConnection; |
| 108 private final ImeAdapterDelegate mViewEmbedder; | 108 private final ImeAdapterDelegate mViewEmbedder; |
| 109 private final Handler mHandler; | 109 private final Handler mHandler; |
| 110 private int mTextInputType; | 110 private int mTextInputType; |
| 111 private int mTextInputFlags; | 111 private int mTextInputFlags; |
| 112 private String mLastComposeText; | |
| 113 | |
| 114 @VisibleForTesting | |
| 115 int mLastSyntheticKeyCode; | |
| 116 | 112 |
| 117 @VisibleForTesting | 113 @VisibleForTesting |
| 118 boolean mIsShowWithoutHideOutstanding = false; | 114 boolean mIsShowWithoutHideOutstanding = false; |
| 119 | 115 |
| 120 /** | 116 /** |
| 121 * @param wrapper InputMethodManagerWrapper that should receive all the call
directed to | 117 * @param wrapper InputMethodManagerWrapper that should receive all the call
directed to |
| 122 * InputMethodManager. | 118 * InputMethodManager. |
| 123 * @param embedder The view that is used for callbacks from ImeAdapter. | 119 * @param embedder The view that is used for callbacks from ImeAdapter. |
| 124 */ | 120 */ |
| 125 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe
dder) { | 121 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe
dder) { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 156 InputMethodManagerWrapper getInputMethodManagerWrapper() { | 152 InputMethodManagerWrapper getInputMethodManagerWrapper() { |
| 157 return mInputMethodManagerWrapper; | 153 return mInputMethodManagerWrapper; |
| 158 } | 154 } |
| 159 | 155 |
| 160 /** | 156 /** |
| 161 * Set the current active InputConnection when a new InputConnection is cons
tructed. | 157 * Set the current active InputConnection when a new InputConnection is cons
tructed. |
| 162 * @param inputConnection The input connection that is currently used with I
ME. | 158 * @param inputConnection The input connection that is currently used with I
ME. |
| 163 */ | 159 */ |
| 164 void setInputConnection(AdapterInputConnection inputConnection) { | 160 void setInputConnection(AdapterInputConnection inputConnection) { |
| 165 mInputConnection = inputConnection; | 161 mInputConnection = inputConnection; |
| 166 mLastComposeText = null; | |
| 167 } | 162 } |
| 168 | 163 |
| 169 /** | 164 /** |
| 170 * Should be used only by AdapterInputConnection. | 165 * Should be used only by AdapterInputConnection. |
| 171 * @return The input type of currently focused element. | 166 * @return The input type of currently focused element. |
| 172 */ | 167 */ |
| 173 int getTextInputType() { | 168 int getTextInputType() { |
| 174 return mTextInputType; | 169 return mTextInputType; |
| 175 } | 170 } |
| 176 | 171 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 239 private void attach(long nativeImeAdapter, int textInputType, int textInputF
lags, | 234 private void attach(long nativeImeAdapter, int textInputType, int textInputF
lags, |
| 240 boolean delayDismissInput) { | 235 boolean delayDismissInput) { |
| 241 Log.d(TAG, "attach"); | 236 Log.d(TAG, "attach"); |
| 242 if (mNativeImeAdapterAndroid != 0) { | 237 if (mNativeImeAdapterAndroid != 0) { |
| 243 nativeResetImeAdapter(mNativeImeAdapterAndroid); | 238 nativeResetImeAdapter(mNativeImeAdapterAndroid); |
| 244 } | 239 } |
| 245 if (nativeImeAdapter != 0) { | 240 if (nativeImeAdapter != 0) { |
| 246 nativeAttachImeAdapter(nativeImeAdapter); | 241 nativeAttachImeAdapter(nativeImeAdapter); |
| 247 } | 242 } |
| 248 mNativeImeAdapterAndroid = nativeImeAdapter; | 243 mNativeImeAdapterAndroid = nativeImeAdapter; |
| 249 mLastComposeText = null; | |
| 250 mTextInputFlags = textInputFlags; | 244 mTextInputFlags = textInputFlags; |
| 251 if (textInputType == mTextInputType) return; | 245 if (textInputType == mTextInputType) return; |
| 252 mTextInputType = textInputType; | 246 mTextInputType = textInputType; |
| 253 mHandler.removeCallbacks(mDismissInputRunnable); // okay if not found | 247 mHandler.removeCallbacks(mDismissInputRunnable); // okay if not found |
| 254 if (mTextInputType == TextInputType.NONE) { | 248 if (mTextInputType == TextInputType.NONE) { |
| 255 if (delayDismissInput) { | 249 if (delayDismissInput) { |
| 256 // Set a delayed task to do unfocus. This avoids hiding the keyb
oard when tabbing | 250 // Set a delayed task to do unfocus. This avoids hiding the keyb
oard when tabbing |
| 257 // through text inputs or when JS rapidly changes focus to anoth
er text element. | 251 // through text inputs or when JS rapidly changes focus to anoth
er text element. |
| 258 mHandler.postDelayed(mDismissInputRunnable, INPUT_DISMISS_DELAY)
; | 252 mHandler.postDelayed(mDismissInputRunnable, INPUT_DISMISS_DELAY)
; |
| 259 mIsShowWithoutHideOutstanding = false; | 253 mIsShowWithoutHideOutstanding = false; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 public boolean hasTextInputType() { | 302 public boolean hasTextInputType() { |
| 309 return isTextInputType(mTextInputType); | 303 return isTextInputType(mTextInputType); |
| 310 } | 304 } |
| 311 | 305 |
| 312 public boolean dispatchKeyEvent(KeyEvent event) { | 306 public boolean dispatchKeyEvent(KeyEvent event) { |
| 313 Log.d(TAG, "dispatchKeyEvent: action [%d], keycode [%d]", event.getActio
n(), | 307 Log.d(TAG, "dispatchKeyEvent: action [%d], keycode [%d]", event.getActio
n(), |
| 314 event.getKeyCode()); | 308 event.getKeyCode()); |
| 315 if (mInputConnection != null) { | 309 if (mInputConnection != null) { |
| 316 return mInputConnection.sendKeyEvent(event); | 310 return mInputConnection.sendKeyEvent(event); |
| 317 } | 311 } |
| 318 return translateAndSendNativeEvents(event); | 312 return sendKeyEvent(event); |
| 319 } | |
| 320 | |
| 321 private int shouldSendKeyEventWithKeyCode(String text) { | |
| 322 if (text.length() != 1) return COMPOSITION_KEY_CODE; | |
| 323 | |
| 324 if (text.equals("\n")) { | |
| 325 return KeyEvent.KEYCODE_ENTER; | |
| 326 } else if (text.equals("\t")) { | |
| 327 return KeyEvent.KEYCODE_TAB; | |
| 328 } else { | |
| 329 return COMPOSITION_KEY_CODE; | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 /** | |
| 334 * @return Android KeyEvent for a single unicode character. Only one KeyEve
nt is returned | |
| 335 * even if the system determined that various modifier keys (like Shift) wou
ld also have | |
| 336 * been pressed. | |
| 337 */ | |
| 338 private static KeyEvent androidKeyEventForCharacter(char chr) { | |
| 339 if (sKeyCharacterMap == null) { | |
| 340 sKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYB
OARD); | |
| 341 } | |
| 342 sSingleCharArray[0] = chr; | |
| 343 // TODO: Evaluate cost of this system call. | |
| 344 KeyEvent[] events = sKeyCharacterMap.getEvents(sSingleCharArray); | |
| 345 if (events == null) { // No known key sequence will create that charact
er. | |
| 346 return null; | |
| 347 } | |
| 348 | |
| 349 for (int i = 0; i < events.length; ++i) { | |
| 350 if (events[i].getAction() == KeyEvent.ACTION_DOWN | |
| 351 && !KeyEvent.isModifierKey(events[i].getKeyCode())) { | |
| 352 return events[i]; | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 return null; // No printing characters were found. | |
| 357 } | 313 } |
| 358 | 314 |
| 359 /** | 315 /** |
| 360 * @see BaseInputConnection#performContextMenuAction(int) | 316 * @see BaseInputConnection#performContextMenuAction(int) |
| 361 */ | 317 */ |
| 362 public boolean performContextMenuAction(int id) { | 318 boolean performContextMenuAction(int id) { |
| 363 Log.d(TAG, "performContextMenuAction: id [%d]", id); | 319 Log.d(TAG, "performContextMenuAction: id [%d]", id); |
| 364 return mViewEmbedder.performContextMenuAction(id); | 320 return mViewEmbedder.performContextMenuAction(id); |
| 365 } | 321 } |
| 366 | 322 |
| 367 @VisibleForTesting | 323 boolean performEditorAction(int actionCode) { |
| 368 public static KeyEvent getTypedKeyEventGuess(String oldtext, String newtext)
{ | 324 if (mNativeImeAdapterAndroid == 0) return false; |
| 369 // Starting typing a new composition should add only a single character.
Any composition | 325 if (actionCode == EditorInfo.IME_ACTION_NEXT) { |
| 370 // beginning with text longer than that must come from something other t
han typing so | 326 sendSyntheticKeyPress(KeyEvent.KEYCODE_TAB, |
| 371 // return 0. | 327 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE |
| 372 if (oldtext == null) { | 328 | KeyEvent.FLAG_EDITOR_ACTION); |
| 373 if (newtext.length() == 1) { | 329 } else { |
| 374 return androidKeyEventForCharacter(newtext.charAt(0)); | 330 sendSyntheticKeyPress(KeyEvent.KEYCODE_ENTER, |
| 375 } else { | 331 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE |
| 376 return null; | 332 | KeyEvent.FLAG_EDITOR_ACTION); |
| 377 } | |
| 378 } | 333 } |
| 379 | 334 return true; |
| 380 // The content has grown in length: assume the last character is the key
that caused it. | |
| 381 if (newtext.length() > oldtext.length() && newtext.startsWith(oldtext))
{ | |
| 382 return androidKeyEventForCharacter(newtext.charAt(newtext.length() -
1)); | |
| 383 } | |
| 384 | |
| 385 // The content has shrunk in length: assume that backspace was pressed. | |
| 386 if (oldtext.length() > newtext.length() && oldtext.startsWith(newtext))
{ | |
| 387 return new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL); | |
| 388 } | |
| 389 | |
| 390 // The content is unchanged or has undergone a complex change (i.e. not
a simple tail | |
| 391 // modification) so return an unknown key-code. | |
| 392 return null; | |
| 393 } | 335 } |
| 394 | 336 |
| 395 void sendKeyEventWithKeyCode(int keyCode, int flags) { | 337 @VisibleForTesting |
| 338 protected void sendSyntheticKeyPress(int keyCode, int flags) { |
| 396 long eventTime = SystemClock.uptimeMillis(); | 339 long eventTime = SystemClock.uptimeMillis(); |
| 397 mLastSyntheticKeyCode = keyCode; | 340 sendKeyEvent(new KeyEvent(eventTime, eventTime, |
| 398 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, | |
| 399 KeyEvent.ACTION_DOWN, keyCode, 0, 0, | 341 KeyEvent.ACTION_DOWN, keyCode, 0, 0, |
| 400 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 342 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| 401 flags)); | 343 flags)); |
| 402 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev
entTime, | 344 sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, |
| 403 KeyEvent.ACTION_UP, keyCode, 0, 0, | 345 KeyEvent.ACTION_UP, keyCode, 0, 0, |
| 404 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 346 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| 405 flags)); | 347 flags)); |
| 406 } | 348 } |
| 407 | 349 |
| 408 // Calls from Java to C++ | 350 boolean sendCompositionToNative(CharSequence text, int newCursorPosition, bo
olean isCommit) { |
| 409 // TODO: Add performance tracing to more complicated functions. | |
| 410 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP
osition, | |
| 411 boolean isCommit) { | |
| 412 if (mNativeImeAdapterAndroid == 0) return false; | 351 if (mNativeImeAdapterAndroid == 0) return false; |
| 413 mViewEmbedder.onImeEvent(); | 352 mViewEmbedder.onImeEvent(); |
| 414 | 353 |
| 415 String textStr = text.toString(); | 354 long timestampMs = SystemClock.uptimeMillis(); |
| 416 int keyCode = shouldSendKeyEventWithKeyCode(textStr); | 355 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, WebInputEventType.
RawKeyDown, |
| 417 long timeStampMs = SystemClock.uptimeMillis(); | 356 timestampMs, COMPOSITION_KEY_CODE, 0, 0); |
| 418 | 357 |
| 419 if (keyCode != COMPOSITION_KEY_CODE) { | 358 if (isCommit) { |
| 420 sendKeyEventWithKeyCode(keyCode, | 359 nativeCommitText(mNativeImeAdapterAndroid, text.toString()); |
| 421 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)
; | |
| 422 } else { | 360 } else { |
| 423 KeyEvent keyEvent = getTypedKeyEventGuess(mLastComposeText, textStr)
; | 361 nativeSetComposingText( |
| 424 int modifiers = 0; | 362 mNativeImeAdapterAndroid, text, text.toString(), newCursorPo
sition); |
| 425 if (keyEvent != null) { | |
| 426 keyCode = keyEvent.getKeyCode(); | |
| 427 modifiers = getModifiers(keyEvent.getMetaState()); | |
| 428 } else if (!textStr.equals(mLastComposeText)) { | |
| 429 keyCode = KeyEvent.KEYCODE_UNKNOWN; | |
| 430 } else { | |
| 431 keyCode = -1; | |
| 432 } | |
| 433 | |
| 434 // If this is a single-character commit with no previous composition
, then treat it as | |
| 435 // a native KeyDown/KeyUp pair with no composition rather than a syn
thetic pair with | |
| 436 // composition below. | |
| 437 if (keyCode > 0 && isCommit && mLastComposeText == null && textStr.l
ength() == 1) { | |
| 438 mLastSyntheticKeyCode = keyCode; | |
| 439 return translateAndSendNativeEvents(keyEvent) | |
| 440 && translateAndSendNativeEvents( | |
| 441 KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_
UP)); | |
| 442 } | |
| 443 | |
| 444 // Always send compose events. This is a quick fix for http://crbug.
com/476497. | |
| 445 keyCode = COMPOSITION_KEY_CODE; | |
| 446 modifiers = 0; | |
| 447 | |
| 448 // When typing, there is no issue sending KeyDown and KeyUp events a
round the | |
| 449 // composition event because those key events do nothing (other than
call JS | |
| 450 // handlers). Typing does not cause changes outside of a KeyPress e
vent which | |
| 451 // we don't call here. However, if the key-code is a control key su
ch as | |
| 452 // KEYCODE_DEL then there never is an associated KeyPress event and
the KeyDown | |
| 453 // event itself causes the action. The net result below is that the
Renderer calls | |
| 454 // cancelComposition() and then Android starts anew with setComposin
gRegion(). | |
| 455 // This stopping and restarting of composition could be a source of
problems | |
| 456 // with 3rd party keyboards. | |
| 457 // | |
| 458 // An alternative is to *not* call nativeSetComposingText() in the n
on-commit case | |
| 459 // below. This avoids the restart of composition described above bu
t fails to send | |
| 460 // an update to the composition while in composition which, strictly
speaking, | |
| 461 // does not match the spec. | |
| 462 // | |
| 463 // For now, the solution is to endure the restarting of composition
and only dive | |
| 464 // into the alternate solution should there be problems in the field
. --bcwhite | |
| 465 | |
| 466 if (keyCode >= 0) { | |
| 467 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, WebInputEv
entType.RawKeyDown, | |
| 468 timeStampMs, keyCode, modifiers, 0); | |
| 469 } | |
| 470 | |
| 471 if (isCommit) { | |
| 472 nativeCommitText(mNativeImeAdapterAndroid, textStr); | |
| 473 textStr = null; | |
| 474 } else { | |
| 475 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr,
newCursorPosition); | |
| 476 } | |
| 477 | |
| 478 if (keyCode >= 0) { | |
| 479 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, WebInputEv
entType.KeyUp, | |
| 480 timeStampMs, keyCode, modifiers, 0); | |
| 481 } | |
| 482 | |
| 483 mLastSyntheticKeyCode = keyCode; | |
| 484 } | 363 } |
| 485 | 364 |
| 486 mLastComposeText = textStr; | 365 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, WebInputEventType.
KeyUp, |
| 366 timestampMs, COMPOSITION_KEY_CODE, 0, 0); |
| 487 return true; | 367 return true; |
| 488 } | 368 } |
| 489 | 369 |
| 490 @VisibleForTesting | 370 @VisibleForTesting |
| 491 protected void finishComposingText() { | 371 protected void finishComposingText() { |
| 492 mLastComposeText = null; | |
| 493 if (mNativeImeAdapterAndroid == 0) return; | 372 if (mNativeImeAdapterAndroid == 0) return; |
| 494 nativeFinishComposingText(mNativeImeAdapterAndroid); | 373 nativeFinishComposingText(mNativeImeAdapterAndroid); |
| 495 } | 374 } |
| 496 | 375 |
| 497 boolean translateAndSendNativeEvents(KeyEvent event) { | 376 boolean sendKeyEvent(KeyEvent event) { |
| 498 if (mNativeImeAdapterAndroid == 0) return false; | 377 if (mNativeImeAdapterAndroid == 0) return false; |
| 499 | 378 |
| 500 int action = event.getAction(); | 379 int action = event.getAction(); |
| 501 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) { | 380 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) { |
| 502 // action == KeyEvent.ACTION_MULTIPLE | 381 // action == KeyEvent.ACTION_MULTIPLE |
| 503 // TODO(bulach): confirm the actual behavior. Apparently: | 382 // TODO(bulach): confirm the actual behavior. Apparently: |
| 504 // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a | 383 // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a |
| 505 // composition key down (229) followed by a commit text with the | 384 // composition key down (229) followed by a commit text with the |
| 506 // string from event.getUnicodeChars(). | 385 // string from event.getUnicodeChars(). |
| 507 // Otherwise, we'd need to send an event with a | 386 // Otherwise, we'd need to send an event with a |
| 508 // WebInputEvent::IsAutoRepeat modifier. We also need to verify when | 387 // WebInputEvent::IsAutoRepeat modifier. We also need to verify when |
| 509 // we receive ACTION_MULTIPLE: we may receive it after an ACTION_DOW
N, | 388 // we receive ACTION_MULTIPLE: we may receive it after an ACTION_DOW
N, |
| 510 // and if that's the case, we'll need to review when to send the Cha
r | 389 // and if that's the case, we'll need to review when to send the Cha
r |
| 511 // event. | 390 // event. |
| 512 return false; | 391 return false; |
| 513 } | 392 } |
| 514 mViewEmbedder.onImeEvent(); | 393 mViewEmbedder.onImeEvent(); |
| 515 return nativeSendKeyEvent(mNativeImeAdapterAndroid, event, event.getActi
on(), | 394 return nativeSendKeyEvent(mNativeImeAdapterAndroid, event, event.getActi
on(), |
| 516 getModifiers(event.getMetaState()), event.getEventTime(), event.
getKeyCode(), | 395 getModifiers(event.getMetaState()), event.getEventTime(), event.
getKeyCode(), |
| 517 event.getScanCode(), /*isSystemKey=*/false, event.g
etUnicodeChar()); | 396 event.getScanCode(), /*isSystemKey=*/false, event.g
etUnicodeChar()); |
| 518 } | 397 } |
| 519 | 398 |
| 520 boolean sendSyntheticKeyEvent(int eventType, long timestampMs, int keyCode,
int modifiers, | |
| 521 int unicodeChar) { | |
| 522 if (mNativeImeAdapterAndroid == 0) return false; | |
| 523 | |
| 524 nativeSendSyntheticKeyEvent( | |
| 525 mNativeImeAdapterAndroid, eventType, timestampMs, keyCode, modif
iers, unicodeChar); | |
| 526 return true; | |
| 527 } | |
| 528 | |
| 529 /** | 399 /** |
| 530 * Send a request to the native counterpart to delete a given range of chara
cters. | 400 * Send a request to the native counterpart to delete a given range of chara
cters. |
| 531 * @param beforeLength Number of characters to extend the selection by befor
e the existing | 401 * @param beforeLength Number of characters to extend the selection by befor
e the existing |
| 532 * selection. | 402 * selection. |
| 533 * @param afterLength Number of characters to extend the selection by after
the existing | 403 * @param afterLength Number of characters to extend the selection by after
the existing |
| 534 * selection. | 404 * selection. |
| 535 * @return Whether the native counterpart of ImeAdapter received the call. | 405 * @return Whether the native counterpart of ImeAdapter received the call. |
| 536 */ | 406 */ |
| 537 boolean deleteSurroundingText(int beforeLength, int afterLength) { | 407 boolean deleteSurroundingText(int beforeLength, int afterLength) { |
| 538 mViewEmbedder.onImeEvent(); | 408 mViewEmbedder.onImeEvent(); |
| 539 if (mNativeImeAdapterAndroid == 0) return false; | 409 if (mNativeImeAdapterAndroid == 0) return false; |
| 410 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, WebInputEventType.
RawKeyDown, |
| 411 SystemClock.uptimeMillis(), COMPOSITION_KEY_CODE, 0, 0); |
| 540 nativeDeleteSurroundingText(mNativeImeAdapterAndroid, beforeLength, afte
rLength); | 412 nativeDeleteSurroundingText(mNativeImeAdapterAndroid, beforeLength, afte
rLength); |
| 413 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, |
| 414 WebInputEventType.KeyUp, SystemClock.uptimeMillis(), COMPOSITION
_KEY_CODE, 0, 0); |
| 541 return true; | 415 return true; |
| 542 } | 416 } |
| 543 | 417 |
| 544 /** | 418 /** |
| 545 * Send a request to the native counterpart to set the selection to given ra
nge. | 419 * Send a request to the native counterpart to set the selection to given ra
nge. |
| 546 * @param start Selection start index. | 420 * @param start Selection start index. |
| 547 * @param end Selection end index. | 421 * @param end Selection end index. |
| 548 * @return Whether the native counterpart of ImeAdapter received the call. | 422 * @return Whether the native counterpart of ImeAdapter received the call. |
| 549 */ | 423 */ |
| 550 boolean setEditableSelectionOffsets(int start, int end) { | 424 boolean setEditableSelectionOffsets(int start, int end) { |
| 551 if (mNativeImeAdapterAndroid == 0) return false; | 425 if (mNativeImeAdapterAndroid == 0) return false; |
| 552 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); | 426 nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end); |
| 553 return true; | 427 return true; |
| 554 } | 428 } |
| 555 | 429 |
| 556 /** | 430 /** |
| 557 * Send a request to the native counterpart to set composing region to given
indices. | 431 * Send a request to the native counterpart to set composing region to given
indices. |
| 558 * @param start The start of the composition. | 432 * @param start The start of the composition. |
| 559 * @param end The end of the composition. | 433 * @param end The end of the composition. |
| 560 * @return Whether the native counterpart of ImeAdapter received the call. | 434 * @return Whether the native counterpart of ImeAdapter received the call. |
| 561 */ | 435 */ |
| 562 boolean setComposingRegion(CharSequence text, int start, int end) { | 436 boolean setComposingRegion(CharSequence text, int start, int end) { |
| 563 if (mNativeImeAdapterAndroid == 0) return false; | 437 if (mNativeImeAdapterAndroid == 0) return false; |
| 564 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); | 438 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); |
| 565 mLastComposeText = text != null ? text.toString() : null; | |
| 566 return true; | 439 return true; |
| 567 } | 440 } |
| 568 | 441 |
| 569 @CalledByNative | 442 @CalledByNative |
| 570 private void focusedNodeChanged(boolean isEditable) { | 443 private void focusedNodeChanged(boolean isEditable) { |
| 571 Log.d(TAG, "focusedNodeChanged"); | 444 Log.d(TAG, "focusedNodeChanged"); |
| 572 if (mInputConnection != null && isEditable) mInputConnection.restartInpu
t(); | 445 if (mInputConnection != null && isEditable) mInputConnection.restartInpu
t(); |
| 573 } | 446 } |
| 574 | 447 |
| 575 @CalledByNative | 448 @CalledByNative |
| (...skipping 13 matching lines...) Expand all Loading... |
| 589 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta
rt(span), | 462 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta
rt(span), |
| 590 spannableString.getSpanEnd(span)); | 463 spannableString.getSpanEnd(span)); |
| 591 } | 464 } |
| 592 } | 465 } |
| 593 } | 466 } |
| 594 | 467 |
| 595 @CalledByNative | 468 @CalledByNative |
| 596 private void cancelComposition() { | 469 private void cancelComposition() { |
| 597 Log.d(TAG, "cancelComposition"); | 470 Log.d(TAG, "cancelComposition"); |
| 598 if (mInputConnection != null) mInputConnection.restartInput(); | 471 if (mInputConnection != null) mInputConnection.restartInput(); |
| 599 mLastComposeText = null; | |
| 600 } | 472 } |
| 601 | 473 |
| 602 @CalledByNative | 474 @CalledByNative |
| 603 void detach() { | 475 void detach() { |
| 604 Log.d(TAG, "detach"); | 476 Log.d(TAG, "detach"); |
| 605 mHandler.removeCallbacks(mDismissInputRunnable); | 477 mHandler.removeCallbacks(mDismissInputRunnable); |
| 606 mNativeImeAdapterAndroid = 0; | 478 mNativeImeAdapterAndroid = 0; |
| 607 mTextInputType = 0; | 479 mTextInputType = 0; |
| 608 } | 480 } |
| 609 | 481 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 631 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA
ndroid, | 503 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA
ndroid, |
| 632 int start, int end); | 504 int start, int end); |
| 633 | 505 |
| 634 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i
nt start, int end); | 506 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i
nt start, int end); |
| 635 | 507 |
| 636 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid
, | 508 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid
, |
| 637 int before, int after); | 509 int before, int after); |
| 638 | 510 |
| 639 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); | 511 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); |
| 640 } | 512 } |
| OLD | NEW |