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; |
11 import android.text.Editable; | |
12 import android.text.SpannableString; | 11 import android.text.SpannableString; |
13 import android.text.style.BackgroundColorSpan; | 12 import android.text.style.BackgroundColorSpan; |
14 import android.text.style.CharacterStyle; | 13 import android.text.style.CharacterStyle; |
15 import android.text.style.UnderlineSpan; | 14 import android.text.style.UnderlineSpan; |
16 import android.view.KeyCharacterMap; | 15 import android.view.KeyCharacterMap; |
17 import android.view.KeyEvent; | 16 import android.view.KeyEvent; |
18 import android.view.View; | 17 import android.view.View; |
19 import android.view.inputmethod.EditorInfo; | 18 import android.view.inputmethod.BaseInputConnection; |
20 | 19 |
21 import org.chromium.base.VisibleForTesting; | 20 import org.chromium.base.VisibleForTesting; |
22 import org.chromium.base.annotations.CalledByNative; | 21 import org.chromium.base.annotations.CalledByNative; |
23 import org.chromium.base.annotations.JNINamespace; | 22 import org.chromium.base.annotations.JNINamespace; |
24 import org.chromium.blink_public.web.WebInputEventModifier; | 23 import org.chromium.blink_public.web.WebInputEventModifier; |
25 import org.chromium.blink_public.web.WebInputEventType; | 24 import org.chromium.blink_public.web.WebInputEventType; |
26 import org.chromium.blink_public.web.WebTextInputFlags; | 25 import org.chromium.blink_public.web.WebTextInputFlags; |
27 import org.chromium.ui.base.ime.TextInputType; | 26 import org.chromium.ui.base.ime.TextInputType; |
28 import org.chromium.ui.picker.InputDialogContainer; | 27 import org.chromium.ui.picker.InputDialogContainer; |
29 | 28 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 public void run() { | 93 public void run() { |
95 dismissInput(true); | 94 dismissInput(true); |
96 } | 95 } |
97 }; | 96 }; |
98 | 97 |
99 static char[] sSingleCharArray = new char[1]; | 98 static char[] sSingleCharArray = new char[1]; |
100 static KeyCharacterMap sKeyCharacterMap; | 99 static KeyCharacterMap sKeyCharacterMap; |
101 | 100 |
102 private long mNativeImeAdapterAndroid; | 101 private long mNativeImeAdapterAndroid; |
103 private InputMethodManagerWrapper mInputMethodManagerWrapper; | 102 private InputMethodManagerWrapper mInputMethodManagerWrapper; |
104 private AdapterInputConnection mInputConnection; | 103 public ChromiumBaseInputConnection mInputConnection; |
105 private final ImeAdapterDelegate mViewEmbedder; | 104 private final ImeAdapterDelegate mViewEmbedder; |
106 private final Handler mHandler; | 105 private final Handler mHandler; |
107 private int mTextInputType; | 106 private int mTextInputType; |
108 private int mTextInputFlags; | 107 private int mTextInputFlags; |
109 private String mLastComposeText; | 108 private String mLastComposeText; |
110 | 109 |
111 @VisibleForTesting | 110 @VisibleForTesting |
112 int mLastSyntheticKeyCode; | 111 int mLastSyntheticKeyCode; |
113 | 112 |
114 @VisibleForTesting | 113 @VisibleForTesting |
115 boolean mIsShowWithoutHideOutstanding = false; | 114 boolean mIsShowWithoutHideOutstanding = false; |
116 | 115 |
117 /** | 116 /** |
118 * @param wrapper InputMethodManagerWrapper that should receive all the call
directed to | 117 * @param wrapper InputMethodManagerWrapper that should receive all the call
directed to |
119 * InputMethodManager. | 118 * InputMethodManager. |
120 * @param embedder The view that is used for callbacks from ImeAdapter. | 119 * @param embedder The view that is used for callbacks from ImeAdapter. |
121 */ | 120 */ |
122 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe
dder) { | 121 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe
dder) { |
123 mInputMethodManagerWrapper = wrapper; | 122 mInputMethodManagerWrapper = wrapper; |
124 mViewEmbedder = embedder; | 123 mViewEmbedder = embedder; |
125 mHandler = new Handler(); | 124 mHandler = new Handler(); |
126 } | 125 } |
127 | 126 |
128 /** | 127 /** |
129 * Default factory for AdapterInputConnection classes. | |
130 */ | |
131 public static class AdapterInputConnectionFactory { | |
132 public AdapterInputConnection get(View view, ImeAdapter imeAdapter, | |
133 Editable editable, EditorInfo outAttrs) { | |
134 return new AdapterInputConnection(view, imeAdapter, editable, outAtt
rs); | |
135 } | |
136 } | |
137 | |
138 /** | |
139 * Overrides the InputMethodManagerWrapper that ImeAdapter uses to make call
s to | 128 * Overrides the InputMethodManagerWrapper that ImeAdapter uses to make call
s to |
140 * InputMethodManager. | 129 * InputMethodManager. |
141 * @param immw InputMethodManagerWrapper that should be used to call InputMe
thodManager. | 130 * @param immw InputMethodManagerWrapper that should be used to call InputMe
thodManager. |
142 */ | 131 */ |
143 @VisibleForTesting | 132 @VisibleForTesting |
144 public void setInputMethodManagerWrapper(InputMethodManagerWrapper immw) { | 133 public void setInputMethodManagerWrapper(InputMethodManagerWrapper immw) { |
145 mInputMethodManagerWrapper = immw; | 134 mInputMethodManagerWrapper = immw; |
146 } | 135 } |
147 | 136 |
148 /** | 137 /** |
149 * Should be only used by AdapterInputConnection. | 138 * Should be only used by AdapterInputConnection. |
150 * @return InputMethodManagerWrapper that should receive all the calls direc
ted to | 139 * @return InputMethodManagerWrapper that should receive all the calls direc
ted to |
151 * InputMethodManager. | 140 * InputMethodManager. |
152 */ | 141 */ |
153 InputMethodManagerWrapper getInputMethodManagerWrapper() { | 142 InputMethodManagerWrapper getInputMethodManagerWrapper() { |
154 return mInputMethodManagerWrapper; | 143 return mInputMethodManagerWrapper; |
155 } | 144 } |
156 | 145 |
157 /** | 146 /** |
158 * Set the current active InputConnection when a new InputConnection is cons
tructed. | 147 * Set the current active InputConnection when a new InputConnection is cons
tructed. |
159 * @param inputConnection The input connection that is currently used with I
ME. | 148 * @param inputConnection The input connection that is currently used with I
ME. |
160 */ | 149 */ |
161 void setInputConnection(AdapterInputConnection inputConnection) { | 150 void setInputConnection(ChromiumBaseInputConnection inputConnection) { |
162 mInputConnection = inputConnection; | 151 mInputConnection = inputConnection; |
163 mLastComposeText = null; | 152 mLastComposeText = null; |
164 } | 153 } |
165 | 154 |
166 /** | 155 /** |
167 * Should be used only by AdapterInputConnection. | 156 * Should be used only by AdapterInputConnection. |
168 * @return The input type of currently focused element. | 157 * @return The input type of currently focused element. |
169 */ | 158 */ |
170 int getTextInputType() { | 159 int getTextInputType() { |
171 return mTextInputType; | 160 return mTextInputType; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
214 } | 203 } |
215 | 204 |
216 if (mNativeImeAdapterAndroid != nativeImeAdapter || mTextInputType != te
xtInputType) { | 205 if (mNativeImeAdapterAndroid != nativeImeAdapter || mTextInputType != te
xtInputType) { |
217 // We have to attach immediately, even if we're going to delay the d
ismissing of | 206 // We have to attach immediately, even if we're going to delay the d
ismissing of |
218 // currently visible keyboard because otherwise we have a race condi
tion: If the | 207 // currently visible keyboard because otherwise we have a race condi
tion: If the |
219 // native IME adapter gets destructed before the delayed-dismiss fir
es, we'll access | 208 // native IME adapter gets destructed before the delayed-dismiss fir
es, we'll access |
220 // an object that has been already released. http://crbug.com/44728
7 | 209 // an object that has been already released. http://crbug.com/44728
7 |
221 attach(nativeImeAdapter, textInputType, textInputFlags, true); | 210 attach(nativeImeAdapter, textInputType, textInputFlags, true); |
222 | 211 |
223 if (mTextInputType != TextInputType.NONE) { | 212 if (mTextInputType != TextInputType.NONE) { |
224 mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttache
dView()); | 213 if (mInputConnection != null) mInputConnection.restartInput(); |
225 if (showIfNeeded) { | 214 if (showIfNeeded) { |
226 showKeyboard(); | 215 showKeyboard(); |
227 } | 216 } |
228 } | 217 } |
229 } else if (hasInputType() && showIfNeeded) { | 218 } else if (hasInputType() && showIfNeeded) { |
230 showKeyboard(); | 219 showKeyboard(); |
231 } | 220 } |
232 } | 221 } |
233 | 222 |
234 private void attach(long nativeImeAdapter, int textInputType, int textInputF
lags, | 223 private void attach(long nativeImeAdapter, int textInputType, int textInputF
lags, |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 // The content has shrunk in length: assume that backspace was pressed. | 367 // The content has shrunk in length: assume that backspace was pressed. |
379 if (oldtext.length() > newtext.length() && oldtext.startsWith(newtext))
{ | 368 if (oldtext.length() > newtext.length() && oldtext.startsWith(newtext))
{ |
380 return new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL); | 369 return new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL); |
381 } | 370 } |
382 | 371 |
383 // The content is unchanged or has undergone a complex change (i.e. not
a simple tail | 372 // The content is unchanged or has undergone a complex change (i.e. not
a simple tail |
384 // modification) so return an unknown key-code. | 373 // modification) so return an unknown key-code. |
385 return null; | 374 return null; |
386 } | 375 } |
387 | 376 |
388 void sendKeyEventWithKeyCode(int keyCode, int flags) { | 377 boolean sendKeyEventWithKeyCode(int keyCode, int flags) { |
389 long eventTime = SystemClock.uptimeMillis(); | 378 long eventTime = SystemClock.uptimeMillis(); |
390 mLastSyntheticKeyCode = keyCode; | 379 mLastSyntheticKeyCode = keyCode; |
391 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, | 380 boolean result = translateAndSendNativeEvents(new KeyEvent(eventTime, ev
entTime, |
392 KeyEvent.ACTION_DOWN, keyCode, 0, 0, | 381 KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEY
BOARD, 0, flags)); |
393 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | 382 if (!result) return false; |
394 flags)); | 383 result = translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMil
lis(), eventTime, |
395 translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), ev
entTime, | 384 KeyEvent.ACTION_UP, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBO
ARD, 0, flags)); |
396 KeyEvent.ACTION_UP, keyCode, 0, 0, | 385 return result; |
397 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, | |
398 flags)); | |
399 } | 386 } |
400 | 387 |
401 // Calls from Java to C++ | 388 // Calls from Java to C++ |
402 // TODO: Add performance tracing to more complicated functions. | 389 // TODO: Add performance tracing to more complicated functions. |
403 | 390 |
404 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP
osition, | 391 boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorP
osition, |
405 boolean isCommit) { | 392 boolean isCommit) { |
406 if (mNativeImeAdapterAndroid == 0) return false; | 393 if (mNativeImeAdapterAndroid == 0) return false; |
407 mViewEmbedder.onImeEvent(); | 394 mViewEmbedder.onImeEvent(); |
408 | 395 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
475 } | 462 } |
476 | 463 |
477 mLastSyntheticKeyCode = keyCode; | 464 mLastSyntheticKeyCode = keyCode; |
478 } | 465 } |
479 | 466 |
480 mLastComposeText = textStr; | 467 mLastComposeText = textStr; |
481 return true; | 468 return true; |
482 } | 469 } |
483 | 470 |
484 @VisibleForTesting | 471 @VisibleForTesting |
485 protected void finishComposingText() { | 472 protected boolean finishComposingText() { |
486 mLastComposeText = null; | 473 mLastComposeText = null; |
487 if (mNativeImeAdapterAndroid == 0) return; | 474 if (mNativeImeAdapterAndroid == 0) return false; |
488 nativeFinishComposingText(mNativeImeAdapterAndroid); | 475 nativeFinishComposingText(mNativeImeAdapterAndroid); |
| 476 return true; |
489 } | 477 } |
490 | 478 |
491 boolean translateAndSendNativeEvents(KeyEvent event) { | 479 boolean translateAndSendNativeEvents(KeyEvent event) { |
492 if (mNativeImeAdapterAndroid == 0) return false; | 480 if (mNativeImeAdapterAndroid == 0) return false; |
493 | 481 |
494 int action = event.getAction(); | 482 int action = event.getAction(); |
495 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) { | 483 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) { |
496 // action == KeyEvent.ACTION_MULTIPLE | 484 // action == KeyEvent.ACTION_MULTIPLE |
497 // TODO(bulach): confirm the actual behavior. Apparently: | 485 // TODO(bulach): confirm the actual behavior. Apparently: |
498 // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a | 486 // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
553 * @param end The end of the composition. | 541 * @param end The end of the composition. |
554 * @return Whether the native counterpart of ImeAdapter received the call. | 542 * @return Whether the native counterpart of ImeAdapter received the call. |
555 */ | 543 */ |
556 boolean setComposingRegion(CharSequence text, int start, int end) { | 544 boolean setComposingRegion(CharSequence text, int start, int end) { |
557 if (mNativeImeAdapterAndroid == 0) return false; | 545 if (mNativeImeAdapterAndroid == 0) return false; |
558 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); | 546 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); |
559 mLastComposeText = text != null ? text.toString() : null; | 547 mLastComposeText = text != null ? text.toString() : null; |
560 return true; | 548 return true; |
561 } | 549 } |
562 | 550 |
563 // Calls from C++ to Java | 551 /** |
| 552 * Send a request to the native counterpart to give the latest text input st
ate update. |
| 553 */ |
| 554 boolean requestTextInputStateUpdate() { |
| 555 if (mNativeImeAdapterAndroid == 0) return false; |
| 556 nativeRequestTextInputStateUpdate(mNativeImeAdapterAndroid); |
| 557 return true; |
| 558 } |
564 | 559 |
565 @CalledByNative | 560 @CalledByNative |
566 private void focusedNodeChanged(boolean isEditable) { | 561 private void focusedNodeChanged(boolean isEditable) { |
567 if (mInputConnection != null && isEditable) mInputConnection.restartInpu
t(); | 562 if (mInputConnection != null && isEditable) { |
| 563 mInputConnection.restartInput(); |
| 564 } |
568 } | 565 } |
569 | 566 |
570 @CalledByNative | 567 @CalledByNative |
571 private void populateUnderlinesFromSpans(CharSequence text, long underlines)
{ | 568 private void populateUnderlinesFromSpans(CharSequence text, long underlines)
{ |
572 if (!(text instanceof SpannableString)) return; | 569 if (!(text instanceof SpannableString)) return; |
573 | 570 |
574 SpannableString spannableString = ((SpannableString) text); | 571 SpannableString spannableString = ((SpannableString) text); |
575 CharacterStyle spans[] = | 572 CharacterStyle spans[] = |
576 spannableString.getSpans(0, text.length(), CharacterStyle.class)
; | 573 spannableString.getSpans(0, text.length(), CharacterStyle.class)
; |
577 for (CharacterStyle span : spans) { | 574 for (CharacterStyle span : spans) { |
578 if (span instanceof BackgroundColorSpan) { | 575 if (span instanceof BackgroundColorSpan) { |
579 nativeAppendBackgroundColorSpan(underlines, spannableString.getS
panStart(span), | 576 nativeAppendBackgroundColorSpan(underlines, spannableString.getS
panStart(span), |
580 spannableString.getSpanEnd(span), | 577 spannableString.getSpanEnd(span), |
581 ((BackgroundColorSpan) span).getBackgroundColor()); | 578 ((BackgroundColorSpan) span).getBackgroundColor()); |
582 } else if (span instanceof UnderlineSpan) { | 579 } else if (span instanceof UnderlineSpan) { |
583 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta
rt(span), | 580 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta
rt(span), |
584 spannableString.getSpanEnd(span)); | 581 spannableString.getSpanEnd(span)); |
585 } | 582 } |
586 } | 583 } |
587 } | 584 } |
588 | 585 |
589 @CalledByNative | 586 @CalledByNative |
590 private void cancelComposition() { | 587 private void cancelComposition() { |
591 if (mInputConnection != null) mInputConnection.restartInput(); | 588 mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView())
; |
592 mLastComposeText = null; | 589 mLastComposeText = null; |
593 } | 590 } |
594 | 591 |
595 @CalledByNative | 592 @CalledByNative |
596 void detach() { | 593 void detach() { |
597 mHandler.removeCallbacks(mDismissInputRunnable); | 594 mHandler.removeCallbacks(mDismissInputRunnable); |
598 mNativeImeAdapterAndroid = 0; | 595 mNativeImeAdapterAndroid = 0; |
599 mTextInputType = 0; | 596 mTextInputType = 0; |
600 } | 597 } |
601 | 598 |
602 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr
oid, | 599 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr
oid, |
603 int eventType, long timestampMs, int keyCode, int modifiers, int uni
codeChar); | 600 int eventType, long timestampMs, int keyCode, int modifiers, int uni
codeChar); |
604 | |
605 private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyE
vent event, | 601 private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyE
vent event, |
606 int action, int modifiers, long timestampMs, int keyCode, boolean is
SystemKey, | 602 int action, int modifiers, long timestampMs, int keyCode, boolean is
SystemKey, |
607 int unicodeChar); | 603 int unicodeChar); |
608 | |
609 private static native void nativeAppendUnderlineSpan(long underlinePtr, int
start, int end); | 604 private static native void nativeAppendUnderlineSpan(long underlinePtr, int
start, int end); |
610 | |
611 private static native void nativeAppendBackgroundColorSpan(long underlinePtr
, int start, | 605 private static native void nativeAppendBackgroundColorSpan(long underlinePtr
, int start, |
612 int end, int backgroundColor); | 606 int end, int backgroundColor); |
613 | |
614 private native void nativeSetComposingText(long nativeImeAdapterAndroid, Cha
rSequence text, | 607 private native void nativeSetComposingText(long nativeImeAdapterAndroid, Cha
rSequence text, |
615 String textStr, int newCursorPosition); | 608 String textStr, int newCursorPosition); |
616 | |
617 private native void nativeCommitText(long nativeImeAdapterAndroid, String te
xtStr); | 609 private native void nativeCommitText(long nativeImeAdapterAndroid, String te
xtStr); |
618 | |
619 private native void nativeFinishComposingText(long nativeImeAdapterAndroid); | 610 private native void nativeFinishComposingText(long nativeImeAdapterAndroid); |
620 | |
621 private native void nativeAttachImeAdapter(long nativeImeAdapterAndroid); | 611 private native void nativeAttachImeAdapter(long nativeImeAdapterAndroid); |
622 | |
623 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA
ndroid, | 612 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA
ndroid, |
624 int start, int end); | 613 int start, int end); |
625 | |
626 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i
nt start, int end); | 614 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i
nt start, int end); |
627 | |
628 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid
, | 615 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid
, |
629 int before, int after); | 616 int before, int after); |
630 | |
631 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); | 617 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); |
| 618 private native void nativeRequestTextInputStateUpdate(long nativeImeAdapterA
ndroid); |
632 } | 619 } |
OLD | NEW |