Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / input / ImeAdapter.java
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
3 // found in the LICENSE file.
4
5 package org.chromium.content.browser.input;
6
7 import android.os.Handler;
8 import android.os.ResultReceiver;
9 import android.os.SystemClock;
10 import android.text.Editable;
11 import android.text.SpannableString;
12 import android.text.style.BackgroundColorSpan;
13 import android.text.style.CharacterStyle;
14 import android.text.style.UnderlineSpan;
15 import android.view.KeyCharacterMap;
16 import android.view.KeyEvent;
17 import android.view.View;
18 import android.view.inputmethod.EditorInfo;
19
20 import org.chromium.base.CalledByNative;
21 import org.chromium.base.JNINamespace;
22 import org.chromium.base.VisibleForTesting;
23 import org.chromium.ui.picker.InputDialogContainer;
24
25 import java.lang.CharSequence;
26
27 /**
28  * Adapts and plumbs android IME service onto the chrome text input API.
29  * ImeAdapter provides an interface in both ways native <-> java:
30  * 1. InputConnectionAdapter notifies native code of text composition state and
31  *    dispatch key events from java -> WebKit.
32  * 2. Native ImeAdapter notifies java side to clear composition text.
33  *
34  * The basic flow is:
35  * 1. When InputConnectionAdapter gets called with composition or result text:
36  *    If we receive a composition text or a result text, then we just need to
37  *    dispatch a synthetic key event with special keycode 229, and then dispatch
38  *    the composition or result text.
39  * 2. Intercept dispatchKeyEvent() method for key events not handled by IME, we
40  *   need to dispatch them to webkit and check webkit's reply. Then inject a
41  *   new key event for further processing if webkit didn't handle it.
42  *
43  * Note that the native peer object does not take any strong reference onto the
44  * instance of this java object, hence it is up to the client of this class (e.g.
45  * the ViewEmbedder implementor) to hold a strong reference to it for the required
46  * lifetime of the object.
47  */
48 @JNINamespace("content")
49 public class ImeAdapter {
50
51     /**
52      * Interface for the delegate that needs to be notified of IME changes.
53      */
54     public interface ImeAdapterDelegate {
55         /**
56          * Called to notify the delegate about synthetic/real key events before sending to renderer.
57          */
58         void onImeEvent();
59
60         /**
61          * Called when a request to hide the keyboard is sent to InputMethodManager.
62          */
63         void onDismissInput();
64
65         /**
66          * @return View that the keyboard should be attached to.
67          */
68         View getAttachedView();
69
70         /**
71          * @return Object that should be called for all keyboard show and hide requests.
72          */
73         ResultReceiver getNewShowKeyboardReceiver();
74     }
75
76     private class DelayedDismissInput implements Runnable {
77         private long mNativeImeAdapter;
78
79         DelayedDismissInput(long nativeImeAdapter) {
80             mNativeImeAdapter = nativeImeAdapter;
81         }
82
83         // http://crbug.com/413744
84         void detach() {
85             mNativeImeAdapter = 0;
86         }
87
88         @Override
89         public void run() {
90             if (mNativeImeAdapter != 0) {
91                 attach(mNativeImeAdapter, sTextInputTypeNone, sTextInputFlagNone);
92             }
93             dismissInput(true);
94         }
95     }
96
97     private static final int COMPOSITION_KEY_CODE = 229;
98
99     // Delay introduced to avoid hiding the keyboard if new show requests are received.
100     // The time required by the unfocus-focus events triggered by tab has been measured in soju:
101     // Mean: 18.633 ms, Standard deviation: 7.9837 ms.
102     // The value here should be higher enough to cover these cases, but not too high to avoid
103     // letting the user perceiving important delays.
104     private static final int INPUT_DISMISS_DELAY = 150;
105
106     // All the constants that are retrieved from the C++ code.
107     // They get set through initializeWebInputEvents and initializeTextInputTypes calls.
108     static int sEventTypeRawKeyDown;
109     static int sEventTypeKeyUp;
110     static int sEventTypeChar;
111     static int sTextInputTypeNone;
112     static int sTextInputTypeText;
113     static int sTextInputTypeTextArea;
114     static int sTextInputTypePassword;
115     static int sTextInputTypeSearch;
116     static int sTextInputTypeUrl;
117     static int sTextInputTypeEmail;
118     static int sTextInputTypeTel;
119     static int sTextInputTypeNumber;
120     static int sTextInputTypeContentEditable;
121     static int sTextInputFlagNone = 0;
122     static int sTextInputFlagAutocompleteOn;
123     static int sTextInputFlagAutocompleteOff;
124     static int sTextInputFlagAutocorrectOn;
125     static int sTextInputFlagAutocorrectOff;
126     static int sTextInputFlagSpellcheckOn;
127     static int sTextInputFlagSpellcheckOff;
128     static int sModifierShift;
129     static int sModifierAlt;
130     static int sModifierCtrl;
131     static int sModifierCapsLockOn;
132     static int sModifierNumLockOn;
133     static char[] sSingleCharArray = new char[1];
134     static KeyCharacterMap sKeyCharacterMap;
135
136     private long mNativeImeAdapterAndroid;
137     private InputMethodManagerWrapper mInputMethodManagerWrapper;
138     private AdapterInputConnection mInputConnection;
139     private final ImeAdapterDelegate mViewEmbedder;
140     private final Handler mHandler;
141     private DelayedDismissInput mDismissInput = null;
142     private int mTextInputType;
143     private int mTextInputFlags;
144     private String mLastComposeText;
145
146     @VisibleForTesting
147     int mLastSyntheticKeyCode;
148
149     @VisibleForTesting
150     boolean mIsShowWithoutHideOutstanding = false;
151
152     /**
153      * @param wrapper InputMethodManagerWrapper that should receive all the call directed to
154      *                InputMethodManager.
155      * @param embedder The view that is used for callbacks from ImeAdapter.
156      */
157     public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embedder) {
158         mInputMethodManagerWrapper = wrapper;
159         mViewEmbedder = embedder;
160         mHandler = new Handler();
161     }
162
163     /**
164      * Default factory for AdapterInputConnection classes.
165      */
166     public static class AdapterInputConnectionFactory {
167         public AdapterInputConnection get(View view, ImeAdapter imeAdapter,
168                 Editable editable, EditorInfo outAttrs) {
169             return new AdapterInputConnection(view, imeAdapter, editable, outAttrs);
170         }
171     }
172
173     /**
174      * Overrides the InputMethodManagerWrapper that ImeAdapter uses to make calls to
175      * InputMethodManager.
176      * @param immw InputMethodManagerWrapper that should be used to call InputMethodManager.
177      */
178     @VisibleForTesting
179     public void setInputMethodManagerWrapper(InputMethodManagerWrapper immw) {
180         mInputMethodManagerWrapper = immw;
181     }
182
183     /**
184      * Should be only used by AdapterInputConnection.
185      * @return InputMethodManagerWrapper that should receive all the calls directed to
186      *         InputMethodManager.
187      */
188     InputMethodManagerWrapper getInputMethodManagerWrapper() {
189         return mInputMethodManagerWrapper;
190     }
191
192     /**
193      * Set the current active InputConnection when a new InputConnection is constructed.
194      * @param inputConnection The input connection that is currently used with IME.
195      */
196     void setInputConnection(AdapterInputConnection inputConnection) {
197         mInputConnection = inputConnection;
198         mLastComposeText = null;
199     }
200
201     /**
202      * Should be used only by AdapterInputConnection.
203      * @return The input type of currently focused element.
204      */
205     int getTextInputType() {
206         return mTextInputType;
207     }
208
209     /**
210      * Should be used only by AdapterInputConnection.
211      * @return The input flags of the currently focused element.
212      */
213     int getTextInputFlags() {
214         return mTextInputFlags;
215     }
216
217     /**
218      * @return Constant representing that a focused node is not an input field.
219      */
220     public static int getTextInputTypeNone() {
221         return sTextInputTypeNone;
222     }
223
224     private static int getModifiers(int metaState) {
225         int modifiers = 0;
226         if ((metaState & KeyEvent.META_SHIFT_ON) != 0) {
227             modifiers |= sModifierShift;
228         }
229         if ((metaState & KeyEvent.META_ALT_ON) != 0) {
230             modifiers |= sModifierAlt;
231         }
232         if ((metaState & KeyEvent.META_CTRL_ON) != 0) {
233             modifiers |= sModifierCtrl;
234         }
235         if ((metaState & KeyEvent.META_CAPS_LOCK_ON) != 0) {
236             modifiers |= sModifierCapsLockOn;
237         }
238         if ((metaState & KeyEvent.META_NUM_LOCK_ON) != 0) {
239             modifiers |= sModifierNumLockOn;
240         }
241         return modifiers;
242     }
243
244     /**
245      * Shows or hides the keyboard based on passed parameters.
246      * @param nativeImeAdapter Pointer to the ImeAdapterAndroid object that is sending the update.
247      * @param textInputType Text input type for the currently focused field in renderer.
248      * @param showIfNeeded Whether the keyboard should be shown if it is currently hidden.
249      */
250     public void updateKeyboardVisibility(long nativeImeAdapter, int textInputType,
251             int textInputFlags, boolean showIfNeeded) {
252         mHandler.removeCallbacks(mDismissInput);
253
254         // If current input type is none and showIfNeeded is false, IME should not be shown
255         // and input type should remain as none.
256         if (mTextInputType == sTextInputTypeNone && !showIfNeeded) {
257             return;
258         }
259
260         if (mNativeImeAdapterAndroid != nativeImeAdapter || mTextInputType != textInputType) {
261             // Set a delayed task to perform unfocus. This avoids hiding the keyboard when tabbing
262             // through text inputs or when JS rapidly changes focus to another text element.
263             if (textInputType == sTextInputTypeNone) {
264                 mDismissInput = new DelayedDismissInput(nativeImeAdapter);
265                 mHandler.postDelayed(mDismissInput, INPUT_DISMISS_DELAY);
266                 return;
267             }
268
269             attach(nativeImeAdapter, textInputType, textInputFlags);
270
271             mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView());
272             if (showIfNeeded) {
273                 showKeyboard();
274             }
275         } else if (hasInputType() && showIfNeeded) {
276             showKeyboard();
277         }
278     }
279
280     public void attach(long nativeImeAdapter, int textInputType, int textInputFlags) {
281         if (mNativeImeAdapterAndroid != 0) {
282             nativeResetImeAdapter(mNativeImeAdapterAndroid);
283         }
284         mNativeImeAdapterAndroid = nativeImeAdapter;
285         mTextInputType = textInputType;
286         mTextInputFlags = textInputFlags;
287         mLastComposeText = null;
288         if (nativeImeAdapter != 0) {
289             nativeAttachImeAdapter(mNativeImeAdapterAndroid);
290         }
291         if (mTextInputType == sTextInputTypeNone) {
292             dismissInput(false);
293         }
294     }
295
296     /**
297      * Attaches the imeAdapter to its native counterpart. This is needed to start forwarding
298      * keyboard events to WebKit.
299      * @param nativeImeAdapter The pointer to the native ImeAdapter object.
300      */
301     public void attach(long nativeImeAdapter) {
302         attach(nativeImeAdapter, sTextInputTypeNone, sTextInputFlagNone);
303     }
304
305     private void showKeyboard() {
306         mIsShowWithoutHideOutstanding = true;
307         mInputMethodManagerWrapper.showSoftInput(mViewEmbedder.getAttachedView(), 0,
308                 mViewEmbedder.getNewShowKeyboardReceiver());
309     }
310
311     private void dismissInput(boolean unzoomIfNeeded) {
312         mIsShowWithoutHideOutstanding  = false;
313         View view = mViewEmbedder.getAttachedView();
314         if (mInputMethodManagerWrapper.isActive(view)) {
315             mInputMethodManagerWrapper.hideSoftInputFromWindow(view.getWindowToken(), 0,
316                     unzoomIfNeeded ? mViewEmbedder.getNewShowKeyboardReceiver() : null);
317         }
318         mViewEmbedder.onDismissInput();
319     }
320
321     private boolean hasInputType() {
322         return mTextInputType != sTextInputTypeNone;
323     }
324
325     private static boolean isTextInputType(int type) {
326         return type != sTextInputTypeNone && !InputDialogContainer.isDialogInputType(type);
327     }
328
329     public boolean hasTextInputType() {
330         return isTextInputType(mTextInputType);
331     }
332
333     /**
334      * @return true if the selected text is of password.
335      */
336     public boolean isSelectionPassword() {
337         return mTextInputType == sTextInputTypePassword;
338     }
339
340     public boolean dispatchKeyEvent(KeyEvent event) {
341         return translateAndSendNativeEvents(event);
342     }
343
344     private int shouldSendKeyEventWithKeyCode(String text) {
345         if (text.length() != 1) return COMPOSITION_KEY_CODE;
346
347         if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER;
348         else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB;
349         else return COMPOSITION_KEY_CODE;
350     }
351
352     /**
353      * @return Android KeyEvent for a single unicode character.  Only one KeyEvent is returned
354      * even if the system determined that various modifier keys (like Shift) would also have
355      * been pressed.
356      */
357     private static KeyEvent androidKeyEventForCharacter(char chr) {
358         if (sKeyCharacterMap == null) {
359             sKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
360         }
361         sSingleCharArray[0] = chr;
362         // TODO: Evaluate cost of this system call.
363         KeyEvent[] events = sKeyCharacterMap.getEvents(sSingleCharArray);
364         if (events == null) {  // No known key sequence will create that character.
365             return null;
366         }
367
368         for (int i = 0; i < events.length; ++i) {
369             if (events[i].getAction() == KeyEvent.ACTION_DOWN &&
370                     !KeyEvent.isModifierKey(events[i].getKeyCode())) {
371                 return events[i];
372             }
373         }
374
375         return null;  // No printing characters were found.
376     }
377
378     @VisibleForTesting
379     public static KeyEvent getTypedKeyEventGuess(String oldtext, String newtext) {
380         // Starting typing a new composition should add only a single character.  Any composition
381         // beginning with text longer than that must come from something other than typing so
382         // return 0.
383         if (oldtext == null) {
384             if (newtext.length() == 1) {
385                 return androidKeyEventForCharacter(newtext.charAt(0));
386             } else {
387                 return null;
388             }
389         }
390
391         // The content has grown in length: assume the last character is the key that caused it.
392         if (newtext.length() > oldtext.length() && newtext.startsWith(oldtext))
393             return androidKeyEventForCharacter(newtext.charAt(newtext.length() - 1));
394
395         // The content has shrunk in length: assume that backspace was pressed.
396         if (oldtext.length() > newtext.length() && oldtext.startsWith(newtext))
397             return new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
398
399         // The content is unchanged or has undergone a complex change (i.e. not a simple tail
400         // modification) so return an unknown key-code.
401         return null;
402     }
403
404     void sendKeyEventWithKeyCode(int keyCode, int flags) {
405         long eventTime = SystemClock.uptimeMillis();
406         mLastSyntheticKeyCode = keyCode;
407         translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime,
408                 KeyEvent.ACTION_DOWN, keyCode, 0, 0,
409                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
410                 flags));
411         translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
412                 KeyEvent.ACTION_UP, keyCode, 0, 0,
413                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
414                 flags));
415     }
416
417     // Calls from Java to C++
418     // TODO: Add performance tracing to more complicated functions.
419
420     boolean checkCompositionQueueAndCallNative(CharSequence text, int newCursorPosition,
421             boolean isCommit) {
422         if (mNativeImeAdapterAndroid == 0) return false;
423         mViewEmbedder.onImeEvent();
424
425         String textStr = text.toString();
426         int keyCode = shouldSendKeyEventWithKeyCode(textStr);
427         long timeStampMs = SystemClock.uptimeMillis();
428
429         if (keyCode != COMPOSITION_KEY_CODE) {
430             sendKeyEventWithKeyCode(keyCode,
431                     KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
432         } else {
433             KeyEvent keyEvent = getTypedKeyEventGuess(mLastComposeText, textStr);
434             int modifiers = 0;
435             if (keyEvent != null) {
436                 keyCode = keyEvent.getKeyCode();
437                 modifiers = getModifiers(keyEvent.getMetaState());
438             } else if (!textStr.equals(mLastComposeText)) {
439                 keyCode = KeyEvent.KEYCODE_UNKNOWN;
440             } else {
441                 keyCode = -1;
442             }
443
444             // If this is a commit with no previous composition, then treat it as a native
445             // KeyDown/KeyUp pair with no composition rather than a synthetic pair with
446             // composition below.
447             if (keyCode > 0 && isCommit && mLastComposeText == null) {
448                 mLastSyntheticKeyCode = keyCode;
449                 return translateAndSendNativeEvents(keyEvent) &&
450                        translateAndSendNativeEvents(KeyEvent.changeAction(
451                                keyEvent, KeyEvent.ACTION_UP));
452             }
453
454             // When typing, there is no issue sending KeyDown and KeyUp events around the
455             // composition event because those key events do nothing (other than call JS
456             // handlers).  Typing does not cause changes outside of a KeyPress event which
457             // we don't call here.  However, if the key-code is a control key such as
458             // KEYCODE_DEL then there never is an associated KeyPress event and the KeyDown
459             // event itself causes the action.  The net result below is that the Renderer calls
460             // cancelComposition() and then Android starts anew with setComposingRegion().
461             // This stopping and restarting of composition could be a source of problems
462             // with 3rd party keyboards.
463             //
464             // An alternative is to *not* call nativeSetComposingText() in the non-commit case
465             // below.  This avoids the restart of composition described above but fails to send
466             // an update to the composition while in composition which, strictly speaking,
467             // does not match the spec.
468             //
469             // For now, the solution is to endure the restarting of composition and only dive
470             // into the alternate solution should there be problems in the field.  --bcwhite
471
472             if (keyCode >= 0) {
473                 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawKeyDown,
474                         timeStampMs, keyCode, modifiers, 0);
475             }
476
477             if (isCommit) {
478                 nativeCommitText(mNativeImeAdapterAndroid, textStr);
479                 textStr = null;
480             } else {
481                 nativeSetComposingText(mNativeImeAdapterAndroid, text, textStr, newCursorPosition);
482             }
483
484             if (keyCode >= 0) {
485                 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyUp,
486                         timeStampMs, keyCode, modifiers, 0);
487             }
488
489             mLastSyntheticKeyCode = keyCode;
490         }
491
492         mLastComposeText = textStr;
493         return true;
494     }
495
496     void finishComposingText() {
497         mLastComposeText = null;
498         if (mNativeImeAdapterAndroid == 0) return;
499         nativeFinishComposingText(mNativeImeAdapterAndroid);
500     }
501
502     boolean translateAndSendNativeEvents(KeyEvent event) {
503         if (mNativeImeAdapterAndroid == 0) return false;
504
505         int action = event.getAction();
506         if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) {
507             // action == KeyEvent.ACTION_MULTIPLE
508             // TODO(bulach): confirm the actual behavior. Apparently:
509             // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a
510             // composition key down (229) followed by a commit text with the
511             // string from event.getUnicodeChars().
512             // Otherwise, we'd need to send an event with a
513             // WebInputEvent::IsAutoRepeat modifier. We also need to verify when
514             // we receive ACTION_MULTIPLE: we may receive it after an ACTION_DOWN,
515             // and if that's the case, we'll need to review when to send the Char
516             // event.
517             return false;
518         }
519         mViewEmbedder.onImeEvent();
520         return nativeSendKeyEvent(mNativeImeAdapterAndroid, event, event.getAction(),
521                 getModifiers(event.getMetaState()), event.getEventTime(), event.getKeyCode(),
522                              /*isSystemKey=*/false, event.getUnicodeChar());
523     }
524
525     boolean sendSyntheticKeyEvent(int eventType, long timestampMs, int keyCode, int modifiers,
526             int unicodeChar) {
527         if (mNativeImeAdapterAndroid == 0) return false;
528
529         nativeSendSyntheticKeyEvent(
530                 mNativeImeAdapterAndroid, eventType, timestampMs, keyCode, modifiers, unicodeChar);
531         return true;
532     }
533
534     /**
535      * Send a request to the native counterpart to delete a given range of characters.
536      * @param beforeLength Number of characters to extend the selection by before the existing
537      *                     selection.
538      * @param afterLength Number of characters to extend the selection by after the existing
539      *                    selection.
540      * @return Whether the native counterpart of ImeAdapter received the call.
541      */
542     boolean deleteSurroundingText(int beforeLength, int afterLength) {
543         mViewEmbedder.onImeEvent();
544         if (mNativeImeAdapterAndroid == 0) return false;
545         nativeDeleteSurroundingText(mNativeImeAdapterAndroid, beforeLength, afterLength);
546         return true;
547     }
548
549     /**
550      * Send a request to the native counterpart to set the selection to given range.
551      * @param start Selection start index.
552      * @param end Selection end index.
553      * @return Whether the native counterpart of ImeAdapter received the call.
554      */
555     boolean setEditableSelectionOffsets(int start, int end) {
556         if (mNativeImeAdapterAndroid == 0) return false;
557         nativeSetEditableSelectionOffsets(mNativeImeAdapterAndroid, start, end);
558         return true;
559     }
560
561     /**
562      * Send a request to the native counterpart to set composing region to given indices.
563      * @param start The start of the composition.
564      * @param end The end of the composition.
565      * @return Whether the native counterpart of ImeAdapter received the call.
566      */
567     boolean setComposingRegion(CharSequence text, int start, int end) {
568         if (mNativeImeAdapterAndroid == 0) return false;
569         nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end);
570         mLastComposeText = text != null ? text.toString() : null;
571         return true;
572     }
573
574     /**
575      * Send a request to the native counterpart to unselect text.
576      * @return Whether the native counterpart of ImeAdapter received the call.
577      */
578     public boolean unselect() {
579         if (mNativeImeAdapterAndroid == 0) return false;
580         nativeUnselect(mNativeImeAdapterAndroid);
581         return true;
582     }
583
584     /**
585      * Send a request to the native counterpart of ImeAdapter to select all the text.
586      * @return Whether the native counterpart of ImeAdapter received the call.
587      */
588     public boolean selectAll() {
589         if (mNativeImeAdapterAndroid == 0) return false;
590         nativeSelectAll(mNativeImeAdapterAndroid);
591         return true;
592     }
593
594     /**
595      * Send a request to the native counterpart of ImeAdapter to cut the selected text.
596      * @return Whether the native counterpart of ImeAdapter received the call.
597      */
598     public boolean cut() {
599         if (mNativeImeAdapterAndroid == 0) return false;
600         nativeCut(mNativeImeAdapterAndroid);
601         return true;
602     }
603
604     /**
605      * Send a request to the native counterpart of ImeAdapter to copy the selected text.
606      * @return Whether the native counterpart of ImeAdapter received the call.
607      */
608     public boolean copy() {
609         if (mNativeImeAdapterAndroid == 0) return false;
610         nativeCopy(mNativeImeAdapterAndroid);
611         return true;
612     }
613
614     /**
615      * Send a request to the native counterpart of ImeAdapter to paste the text from the clipboard.
616      * @return Whether the native counterpart of ImeAdapter received the call.
617      */
618     public boolean paste() {
619         if (mNativeImeAdapterAndroid == 0) return false;
620         nativePaste(mNativeImeAdapterAndroid);
621         return true;
622     }
623
624     // Calls from C++ to Java
625
626     @CalledByNative
627     private static void initializeWebInputEvents(int eventTypeRawKeyDown, int eventTypeKeyUp,
628             int eventTypeChar, int modifierShift, int modifierAlt, int modifierCtrl,
629             int modifierCapsLockOn, int modifierNumLockOn) {
630         sEventTypeRawKeyDown = eventTypeRawKeyDown;
631         sEventTypeKeyUp = eventTypeKeyUp;
632         sEventTypeChar = eventTypeChar;
633         sModifierShift = modifierShift;
634         sModifierAlt = modifierAlt;
635         sModifierCtrl = modifierCtrl;
636         sModifierCapsLockOn = modifierCapsLockOn;
637         sModifierNumLockOn = modifierNumLockOn;
638     }
639
640     @CalledByNative
641     private static void initializeTextInputTypes(int textInputTypeNone, int textInputTypeText,
642             int textInputTypeTextArea, int textInputTypePassword, int textInputTypeSearch,
643             int textInputTypeUrl, int textInputTypeEmail, int textInputTypeTel,
644             int textInputTypeNumber, int textInputTypeContentEditable) {
645         sTextInputTypeNone = textInputTypeNone;
646         sTextInputTypeText = textInputTypeText;
647         sTextInputTypeTextArea = textInputTypeTextArea;
648         sTextInputTypePassword = textInputTypePassword;
649         sTextInputTypeSearch = textInputTypeSearch;
650         sTextInputTypeUrl = textInputTypeUrl;
651         sTextInputTypeEmail = textInputTypeEmail;
652         sTextInputTypeTel = textInputTypeTel;
653         sTextInputTypeNumber = textInputTypeNumber;
654         sTextInputTypeContentEditable = textInputTypeContentEditable;
655     }
656
657     @CalledByNative
658     private static void initializeTextInputFlags(
659             int textInputFlagAutocompleteOn, int textInputFlagAutocompleteOff,
660             int textInputFlagAutocorrectOn, int textInputFlagAutocorrectOff,
661             int textInputFlagSpellcheckOn, int textInputFlagSpellcheckOff) {
662         sTextInputFlagAutocompleteOn = textInputFlagAutocompleteOn;
663         sTextInputFlagAutocompleteOff = textInputFlagAutocompleteOff;
664         sTextInputFlagAutocorrectOn = textInputFlagAutocorrectOn;
665         sTextInputFlagAutocorrectOff = textInputFlagAutocorrectOff;
666         sTextInputFlagSpellcheckOn = textInputFlagSpellcheckOn;
667         sTextInputFlagSpellcheckOff = textInputFlagSpellcheckOff;
668     }
669
670     @CalledByNative
671     private void focusedNodeChanged(boolean isEditable) {
672         if (mInputConnection != null && isEditable) mInputConnection.restartInput();
673     }
674
675     @CalledByNative
676     private void populateUnderlinesFromSpans(CharSequence text, long underlines) {
677         if (!(text instanceof SpannableString)) return;
678
679         SpannableString spannableString = ((SpannableString) text);
680         CharacterStyle spans[] =
681                 spannableString.getSpans(0, text.length(), CharacterStyle.class);
682         for (CharacterStyle span : spans) {
683             if (span instanceof BackgroundColorSpan) {
684                 nativeAppendBackgroundColorSpan(underlines, spannableString.getSpanStart(span),
685                         spannableString.getSpanEnd(span),
686                         ((BackgroundColorSpan) span).getBackgroundColor());
687             } else if (span instanceof UnderlineSpan) {
688                 nativeAppendUnderlineSpan(underlines, spannableString.getSpanStart(span),
689                         spannableString.getSpanEnd(span));
690             }
691         }
692     }
693
694     @CalledByNative
695     private void cancelComposition() {
696         if (mInputConnection != null) mInputConnection.restartInput();
697         mLastComposeText = null;
698     }
699
700     @CalledByNative
701     private void setCharacterBounds(float[] characterBounds) {
702         // TODO(yukawa): Implement this.
703     }
704
705     @CalledByNative
706     void detach() {
707         if (mDismissInput != null) {
708             mHandler.removeCallbacks(mDismissInput);
709             mDismissInput.detach();
710         }
711         mNativeImeAdapterAndroid = 0;
712         mTextInputType = 0;
713     }
714
715     private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndroid,
716             int eventType, long timestampMs, int keyCode, int modifiers, int unicodeChar);
717
718     private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyEvent event,
719             int action, int modifiers, long timestampMs, int keyCode, boolean isSystemKey,
720             int unicodeChar);
721
722     private static native void nativeAppendUnderlineSpan(long underlinePtr, int start, int end);
723
724     private static native void nativeAppendBackgroundColorSpan(long underlinePtr, int start,
725             int end, int backgroundColor);
726
727     private native void nativeSetComposingText(long nativeImeAdapterAndroid, CharSequence text,
728             String textStr, int newCursorPosition);
729
730     private native void nativeCommitText(long nativeImeAdapterAndroid, String textStr);
731
732     private native void nativeFinishComposingText(long nativeImeAdapterAndroid);
733
734     private native void nativeAttachImeAdapter(long nativeImeAdapterAndroid);
735
736     private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterAndroid,
737             int start, int end);
738
739     private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, int start, int end);
740
741     private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid,
742             int before, int after);
743
744     private native void nativeUnselect(long nativeImeAdapterAndroid);
745     private native void nativeSelectAll(long nativeImeAdapterAndroid);
746     private native void nativeCut(long nativeImeAdapterAndroid);
747     private native void nativeCopy(long nativeImeAdapterAndroid);
748     private native void nativePaste(long nativeImeAdapterAndroid);
749     private native void nativeResetImeAdapter(long nativeImeAdapterAndroid);
750 }