1 /*
2  * Copyright (C) 2007-2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package android.inputmethodservice;
18 
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21 
22 import android.annotation.DrawableRes;
23 import android.app.ActivityManager;
24 import android.app.Dialog;
25 import android.content.Context;
26 import android.content.res.Configuration;
27 import android.content.res.Resources;
28 import android.content.res.TypedArray;
29 import android.graphics.Rect;
30 import android.graphics.Region;
31 import android.os.Bundle;
32 import android.os.IBinder;
33 import android.os.ResultReceiver;
34 import android.os.SystemClock;
35 import android.provider.Settings;
36 import android.text.InputType;
37 import android.text.Layout;
38 import android.text.Spannable;
39 import android.text.method.MovementMethod;
40 import android.util.Log;
41 import android.util.PrintWriterPrinter;
42 import android.util.Printer;
43 import android.view.Gravity;
44 import android.view.KeyCharacterMap;
45 import android.view.KeyEvent;
46 import android.view.LayoutInflater;
47 import android.view.MotionEvent;
48 import android.view.View;
49 import android.view.ViewGroup;
50 import android.view.ViewTreeObserver;
51 import android.view.Window;
52 import android.view.WindowManager;
53 import android.view.WindowManager.BadTokenException;
54 import android.view.animation.AnimationUtils;
55 import android.view.inputmethod.CompletionInfo;
56 import android.view.inputmethod.CursorAnchorInfo;
57 import android.view.inputmethod.EditorInfo;
58 import android.view.inputmethod.ExtractedText;
59 import android.view.inputmethod.ExtractedTextRequest;
60 import android.view.inputmethod.InputBinding;
61 import android.view.inputmethod.InputConnection;
62 import android.view.inputmethod.InputMethod;
63 import android.view.inputmethod.InputMethodManager;
64 import android.view.inputmethod.InputMethodSubtype;
65 import android.widget.Button;
66 import android.widget.FrameLayout;
67 import android.widget.LinearLayout;
68 
69 import java.io.FileDescriptor;
70 import java.io.PrintWriter;
71 
72 /**
73  * InputMethodService provides a standard implementation of an InputMethod,
74  * which final implementations can derive from and customize.  See the
75  * base class {@link AbstractInputMethodService} and the {@link InputMethod}
76  * interface for more information on the basics of writing input methods.
77  *
78  * <p>In addition to the normal Service lifecycle methods, this class
79  * introduces some new specific callbacks that most subclasses will want
80  * to make use of:</p>
81  * <ul>
82  * <li> {@link #onInitializeInterface()} for user-interface initialization,
83  * in particular to deal with configuration changes while the service is
84  * running.
85  * <li> {@link #onBindInput} to find out about switching to a new client.
86  * <li> {@link #onStartInput} to deal with an input session starting with
87  * the client.
88  * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
89  * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
90  * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
91  * starting within the input area of the IME.
92  * </ul>
93  *
94  * <p>An input method has significant discretion in how it goes about its
95  * work: the {@link android.inputmethodservice.InputMethodService} provides
96  * a basic framework for standard UI elements (input view, candidates view,
97  * and running in fullscreen mode), but it is up to a particular implementor
98  * to decide how to use them.  For example, one input method could implement
99  * an input area with a keyboard, another could allow the user to draw text,
100  * while a third could have no input area (and thus not be visible to the
101  * user) but instead listen to audio and perform text to speech conversion.</p>
102  *
103  * <p>In the implementation provided here, all of these elements are placed
104  * together in a single window managed by the InputMethodService.  It will
105  * execute callbacks as it needs information about them, and provides APIs for
106  * programmatic control over them.  They layout of these elements is explicitly
107  * defined:</p>
108  *
109  * <ul>
110  * <li>The soft input view, if available, is placed at the bottom of the
111  * screen.
112  * <li>The candidates view, if currently shown, is placed above the soft
113  * input view.
114  * <li>If not running fullscreen, the application is moved or resized to be
115  * above these views; if running fullscreen, the window will completely cover
116  * the application and its top part will contain the extract text of what is
117  * currently being edited by the application.
118  * </ul>
119  *
120  *
121  * <a name="SoftInputView"></a>
122  * <h3>Soft Input View</h3>
123  *
124  * <p>Central to most input methods is the soft input view.  This is where most
125  * user interaction occurs: pressing on soft keys, drawing characters, or
126  * however else your input method wants to generate text.  Most implementations
127  * will simply have their own view doing all of this work, and return a new
128  * instance of it when {@link #onCreateInputView()} is called.  At that point,
129  * as long as the input view is visible, you will see user interaction in
130  * that view and can call back on the InputMethodService to interact with the
131  * application as appropriate.</p>
132  *
133  * <p>There are some situations where you want to decide whether or not your
134  * soft input view should be shown to the user.  This is done by implementing
135  * the {@link #onEvaluateInputViewShown()} to return true or false based on
136  * whether it should be shown in the current environment.  If any of your
137  * state has changed that may impact this, call
138  * {@link #updateInputViewShown()} to have it re-evaluated.  The default
139  * implementation always shows the input view unless there is a hard
140  * keyboard available, which is the appropriate behavior for most input
141  * methods.</p>
142  *
143  *
144  * <a name="CandidatesView"></a>
145  * <h3>Candidates View</h3>
146  *
147  * <p>Often while the user is generating raw text, an input method wants to
148  * provide them with a list of possible interpretations of that text that can
149  * be selected for use.  This is accomplished with the candidates view, and
150  * like the soft input view you implement {@link #onCreateCandidatesView()}
151  * to instantiate your own view implementing your candidates UI.</p>
152  *
153  * <p>Management of the candidates view is a little different than the input
154  * view, because the candidates view tends to be more transient, being shown
155  * only when there are possible candidates for the current text being entered
156  * by the user.  To control whether the candidates view is shown, you use
157  * {@link #setCandidatesViewShown(boolean)}.  Note that because the candidate
158  * view tends to be shown and hidden a lot, it does not impact the application
159  * UI in the same way as the soft input view: it will never cause application
160  * windows to resize, only cause them to be panned if needed for the user to
161  * see the current focus.</p>
162  *
163  *
164  * <a name="FullscreenMode"></a>
165  * <h3>Fullscreen Mode</h3>
166  *
167  * <p>Sometimes your input method UI is too large to integrate with the
168  * application UI, so you just want to take over the screen.  This is
169  * accomplished by switching to full-screen mode, causing the input method
170  * window to fill the entire screen and add its own "extracted text" editor
171  * showing the user the text that is being typed.  Unlike the other UI elements,
172  * there is a standard implementation for the extract editor that you should
173  * not need to change.  The editor is placed at the top of the IME, above the
174  * input and candidates views.</p>
175  *
176  * <p>Similar to the input view, you control whether the IME is running in
177  * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
178  * to return true or false based on
179  * whether it should be fullscreen in the current environment.  If any of your
180  * state has changed that may impact this, call
181  * {@link #updateFullscreenMode()} to have it re-evaluated.  The default
182  * implementation selects fullscreen mode when the screen is in a landscape
183  * orientation, which is appropriate behavior for most input methods that have
184  * a significant input area.</p>
185  *
186  * <p>When in fullscreen mode, you have some special requirements because the
187  * user can not see the application UI.  In particular, you should implement
188  * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
189  * generated by your application, typically in your candidates view like you
190  * would normally show candidates.
191  *
192  *
193  * <a name="GeneratingText"></a>
194  * <h3>Generating Text</h3>
195  *
196  * <p>The key part of an IME is of course generating text for the application.
197  * This is done through calls to the
198  * {@link android.view.inputmethod.InputConnection} interface to the
199  * application, which can be retrieved from {@link #getCurrentInputConnection()}.
200  * This interface allows you to generate raw key events or, if the target
201  * supports it, directly edit in strings of candidates and committed text.</p>
202  *
203  * <p>Information about what the target is expected and supports can be found
204  * through the {@link android.view.inputmethod.EditorInfo} class, which is
205  * retrieved with {@link #getCurrentInputEditorInfo()} method.  The most
206  * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
207  * EditorInfo.inputType}; in particular, if this is
208  * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
209  * then the target does not support complex edits and you need to only deliver
210  * raw key events to it.  An input method will also want to look at other
211  * values here, to for example detect password mode, auto complete text views,
212  * phone number entry, etc.</p>
213  *
214  * <p>When the user switches between input targets, you will receive calls to
215  * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
216  * You can use these to reset and initialize your input state for the current
217  * target.  For example, you will often want to clear any input state, and
218  * update a soft keyboard to be appropriate for the new inputType.</p>
219  *
220  * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
221  * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
222  * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
223  */
224 public class InputMethodService extends AbstractInputMethodService {
225     static final String TAG = "InputMethodService";
226     static final boolean DEBUG = false;
227 
228     /**
229      * The back button will close the input window.
230      */
231     public static final int BACK_DISPOSITION_DEFAULT = 0;  // based on window
232 
233     /**
234      * This input method will not consume the back key.
235      */
236     public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // back
237 
238     /**
239      * This input method will consume the back key.
240      */
241     public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // down
242 
243     /**
244      * @hide
245      * The IME is active.  It may or may not be visible.
246      */
247     public static final int IME_ACTIVE = 0x1;
248 
249     /**
250      * @hide
251      * The IME is visible.
252      */
253     public static final int IME_VISIBLE = 0x2;
254 
255     InputMethodManager mImm;
256 
257     int mTheme = 0;
258     boolean mHardwareAccelerated = false;
259 
260     LayoutInflater mInflater;
261     TypedArray mThemeAttrs;
262     View mRootView;
263     SoftInputWindow mWindow;
264     boolean mInitialized;
265     boolean mWindowCreated;
266     boolean mWindowAdded;
267     boolean mWindowVisible;
268     boolean mWindowWasVisible;
269     boolean mInShowWindow;
270     ViewGroup mFullscreenArea;
271     FrameLayout mExtractFrame;
272     FrameLayout mCandidatesFrame;
273     FrameLayout mInputFrame;
274 
275     IBinder mToken;
276 
277     InputBinding mInputBinding;
278     InputConnection mInputConnection;
279     boolean mInputStarted;
280     boolean mInputViewStarted;
281     boolean mCandidatesViewStarted;
282     InputConnection mStartedInputConnection;
283     EditorInfo mInputEditorInfo;
284 
285     int mShowInputFlags;
286     boolean mShowInputRequested;
287     boolean mLastShowInputRequested;
288     int mCandidatesVisibility;
289     CompletionInfo[] mCurCompletions;
290 
291     boolean mShowInputForced;
292 
293     boolean mFullscreenApplied;
294     boolean mIsFullscreen;
295     View mExtractView;
296     boolean mExtractViewHidden;
297     ExtractEditText mExtractEditText;
298     ViewGroup mExtractAccessories;
299     Button mExtractAction;
300     ExtractedText mExtractedText;
301     int mExtractedToken;
302 
303     View mInputView;
304     boolean mIsInputViewShown;
305 
306     int mStatusIcon;
307     int mBackDisposition;
308 
309     /**
310      * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
311      * have not shown our own window yet.  In this situation, the previous inset continues to be
312      * shown as an empty region until it is explicitly updated. Basically we can trigger the update
313      * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}.
314      */
315     boolean mShouldClearInsetOfPreviousIme;
316 
317     final Insets mTmpInsets = new Insets();
318     final int[] mTmpLocation = new int[2];
319 
320     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
321             new ViewTreeObserver.OnComputeInternalInsetsListener() {
322         public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
323             if (isExtractViewShown()) {
324                 // In true fullscreen mode, we just say the window isn't covering
325                 // any content so we don't impact whatever is behind.
326                 View decor = getWindow().getWindow().getDecorView();
327                 info.contentInsets.top = info.visibleInsets.top
328                         = decor.getHeight();
329                 info.touchableRegion.setEmpty();
330                 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
331             } else {
332                 onComputeInsets(mTmpInsets);
333                 info.contentInsets.top = mTmpInsets.contentTopInsets;
334                 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
335                 info.touchableRegion.set(mTmpInsets.touchableRegion);
336                 info.setTouchableInsets(mTmpInsets.touchableInsets);
337             }
338         }
339     };
340 
341     final View.OnClickListener mActionClickListener = new View.OnClickListener() {
342         public void onClick(View v) {
343             final EditorInfo ei = getCurrentInputEditorInfo();
344             final InputConnection ic = getCurrentInputConnection();
345             if (ei != null && ic != null) {
346                 if (ei.actionId != 0) {
347                     ic.performEditorAction(ei.actionId);
348                 } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION)
349                         != EditorInfo.IME_ACTION_NONE) {
350                     ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
351                 }
352             }
353         }
354     };
355 
356     /**
357      * Concrete implementation of
358      * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
359      * all of the standard behavior for an input method.
360      */
361     public class InputMethodImpl extends AbstractInputMethodImpl {
362         /**
363          * Take care of attaching the given window token provided by the system.
364          */
attachToken(IBinder token)365         public void attachToken(IBinder token) {
366             if (mToken == null) {
367                 mToken = token;
368                 mWindow.setToken(token);
369             }
370         }
371 
372         /**
373          * Handle a new input binding, calling
374          * {@link InputMethodService#onBindInput InputMethodService.onBindInput()}
375          * when done.
376          */
bindInput(InputBinding binding)377         public void bindInput(InputBinding binding) {
378             mInputBinding = binding;
379             mInputConnection = binding.getConnection();
380             if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
381                     + " ic=" + mInputConnection);
382             InputConnection ic = getCurrentInputConnection();
383             if (ic != null) ic.reportFullscreenMode(mIsFullscreen);
384             initialize();
385             onBindInput();
386         }
387 
388         /**
389          * Clear the current input binding.
390          */
unbindInput()391         public void unbindInput() {
392             if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
393                     + " ic=" + mInputConnection);
394             onUnbindInput();
395             mInputBinding = null;
396             mInputConnection = null;
397         }
398 
startInput(InputConnection ic, EditorInfo attribute)399         public void startInput(InputConnection ic, EditorInfo attribute) {
400             if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
401             doStartInput(ic, attribute, false);
402         }
403 
restartInput(InputConnection ic, EditorInfo attribute)404         public void restartInput(InputConnection ic, EditorInfo attribute) {
405             if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
406             doStartInput(ic, attribute, true);
407         }
408 
409         /**
410          * Handle a request by the system to hide the soft input area.
411          */
hideSoftInput(int flags, ResultReceiver resultReceiver)412         public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
413             if (DEBUG) Log.v(TAG, "hideSoftInput()");
414             boolean wasVis = isInputViewShown();
415             mShowInputFlags = 0;
416             mShowInputRequested = false;
417             mShowInputForced = false;
418             doHideWindow();
419             clearInsetOfPreviousIme();
420             if (resultReceiver != null) {
421                 resultReceiver.send(wasVis != isInputViewShown()
422                         ? InputMethodManager.RESULT_HIDDEN
423                         : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
424                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
425             }
426         }
427 
428         /**
429          * Handle a request by the system to show the soft input area.
430          */
showSoftInput(int flags, ResultReceiver resultReceiver)431         public void showSoftInput(int flags, ResultReceiver resultReceiver) {
432             if (DEBUG) Log.v(TAG, "showSoftInput()");
433             boolean wasVis = isInputViewShown();
434             mShowInputFlags = 0;
435             if (onShowInputRequested(flags, false)) {
436                 try {
437                     showWindow(true);
438                 } catch (BadTokenException e) {
439                     if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
440                     mWindowVisible = false;
441                     mWindowAdded = false;
442                 }
443             }
444             clearInsetOfPreviousIme();
445             // If user uses hard keyboard, IME button should always be shown.
446             boolean showing = isInputViewShown();
447             mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
448                     mBackDisposition);
449             if (resultReceiver != null) {
450                 resultReceiver.send(wasVis != isInputViewShown()
451                         ? InputMethodManager.RESULT_SHOWN
452                         : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
453                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
454             }
455         }
456 
changeInputMethodSubtype(InputMethodSubtype subtype)457         public void changeInputMethodSubtype(InputMethodSubtype subtype) {
458             onCurrentInputMethodSubtypeChanged(subtype);
459         }
460     }
461 
462     /**
463      * Concrete implementation of
464      * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
465      * all of the standard behavior for an input method session.
466      */
467     public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
finishInput()468         public void finishInput() {
469             if (!isEnabled()) {
470                 return;
471             }
472             if (DEBUG) Log.v(TAG, "finishInput() in " + this);
473             doFinishInput();
474         }
475 
476         /**
477          * Call {@link InputMethodService#onDisplayCompletions
478          * InputMethodService.onDisplayCompletions()}.
479          */
displayCompletions(CompletionInfo[] completions)480         public void displayCompletions(CompletionInfo[] completions) {
481             if (!isEnabled()) {
482                 return;
483             }
484             mCurCompletions = completions;
485             onDisplayCompletions(completions);
486         }
487 
488         /**
489          * Call {@link InputMethodService#onUpdateExtractedText
490          * InputMethodService.onUpdateExtractedText()}.
491          */
updateExtractedText(int token, ExtractedText text)492         public void updateExtractedText(int token, ExtractedText text) {
493             if (!isEnabled()) {
494                 return;
495             }
496             onUpdateExtractedText(token, text);
497         }
498 
499         /**
500          * Call {@link InputMethodService#onUpdateSelection
501          * InputMethodService.onUpdateSelection()}.
502          */
updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)503         public void updateSelection(int oldSelStart, int oldSelEnd,
504                 int newSelStart, int newSelEnd,
505                 int candidatesStart, int candidatesEnd) {
506             if (!isEnabled()) {
507                 return;
508             }
509             InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
510                     newSelStart, newSelEnd, candidatesStart, candidatesEnd);
511         }
512 
513         @Override
viewClicked(boolean focusChanged)514         public void viewClicked(boolean focusChanged) {
515             if (!isEnabled()) {
516                 return;
517             }
518             InputMethodService.this.onViewClicked(focusChanged);
519         }
520 
521         /**
522          * Call {@link InputMethodService#onUpdateCursor
523          * InputMethodService.onUpdateCursor()}.
524          */
updateCursor(Rect newCursor)525         public void updateCursor(Rect newCursor) {
526             if (!isEnabled()) {
527                 return;
528             }
529             InputMethodService.this.onUpdateCursor(newCursor);
530         }
531 
532         /**
533          * Call {@link InputMethodService#onAppPrivateCommand
534          * InputMethodService.onAppPrivateCommand()}.
535          */
appPrivateCommand(String action, Bundle data)536         public void appPrivateCommand(String action, Bundle data) {
537             if (!isEnabled()) {
538                 return;
539             }
540             InputMethodService.this.onAppPrivateCommand(action, data);
541         }
542 
543         /**
544          *
545          */
toggleSoftInput(int showFlags, int hideFlags)546         public void toggleSoftInput(int showFlags, int hideFlags) {
547             InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
548         }
549 
550         /**
551          * Call {@link InputMethodService#onUpdateCursorAnchorInfo
552          * InputMethodService.onUpdateCursorAnchorInfo()}.
553          */
updateCursorAnchorInfo(CursorAnchorInfo info)554         public void updateCursorAnchorInfo(CursorAnchorInfo info) {
555             if (!isEnabled()) {
556                 return;
557             }
558             InputMethodService.this.onUpdateCursorAnchorInfo(info);
559         }
560     }
561 
562     /**
563      * Information about where interesting parts of the input method UI appear.
564      */
565     public static final class Insets {
566         /**
567          * This is the top part of the UI that is the main content.  It is
568          * used to determine the basic space needed, to resize/pan the
569          * application behind.  It is assumed that this inset does not
570          * change very much, since any change will cause a full resize/pan
571          * of the application behind.  This value is relative to the top edge
572          * of the input method window.
573          */
574         public int contentTopInsets;
575 
576         /**
577          * This is the top part of the UI that is visibly covering the
578          * application behind it.  This provides finer-grained control over
579          * visibility, allowing you to change it relatively frequently (such
580          * as hiding or showing candidates) without disrupting the underlying
581          * UI too much.  For example, this will never resize the application
582          * UI, will only pan if needed to make the current focus visible, and
583          * will not aggressively move the pan position when this changes unless
584          * needed to make the focus visible.  This value is relative to the top edge
585          * of the input method window.
586          */
587         public int visibleTopInsets;
588 
589         /**
590          * This is the region of the UI that is touchable.  It is used when
591          * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
592          * The region should be specified relative to the origin of the window frame.
593          */
594         public final Region touchableRegion = new Region();
595 
596         /**
597          * Option for {@link #touchableInsets}: the entire window frame
598          * can be touched.
599          */
600         public static final int TOUCHABLE_INSETS_FRAME
601                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
602 
603         /**
604          * Option for {@link #touchableInsets}: the area inside of
605          * the content insets can be touched.
606          */
607         public static final int TOUCHABLE_INSETS_CONTENT
608                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
609 
610         /**
611          * Option for {@link #touchableInsets}: the area inside of
612          * the visible insets can be touched.
613          */
614         public static final int TOUCHABLE_INSETS_VISIBLE
615                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
616 
617         /**
618          * Option for {@link #touchableInsets}: the region specified by
619          * {@link #touchableRegion} can be touched.
620          */
621         public static final int TOUCHABLE_INSETS_REGION
622                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
623 
624         /**
625          * Determine which area of the window is touchable by the user.  May
626          * be one of: {@link #TOUCHABLE_INSETS_FRAME},
627          * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
628          * or {@link #TOUCHABLE_INSETS_REGION}.
629          */
630         public int touchableInsets;
631     }
632 
633     /**
634      * You can call this to customize the theme used by your IME's window.
635      * This theme should typically be one that derives from
636      * {@link android.R.style#Theme_InputMethod}, which is the default theme
637      * you will get.  This must be set before {@link #onCreate}, so you
638      * will typically call it in your constructor with the resource ID
639      * of your custom theme.
640      */
641     @Override
setTheme(int theme)642     public void setTheme(int theme) {
643         if (mWindow != null) {
644             throw new IllegalStateException("Must be called before onCreate()");
645         }
646         mTheme = theme;
647     }
648 
649     /**
650      * You can call this to try to enable hardware accelerated drawing for
651      * your IME. This must be set before {@link #onCreate}, so you
652      * will typically call it in your constructor.  It is not always possible
653      * to use hardware accelerated drawing in an IME (for example on low-end
654      * devices that do not have the resources to support this), so the call
655      * returns true if it succeeds otherwise false if you will need to draw
656      * in software.  You must be able to handle either case.
657      *
658      * @deprecated Starting in API 21, hardware acceleration is always enabled
659      *             on capable devices.
660      */
enableHardwareAcceleration()661     public boolean enableHardwareAcceleration() {
662         if (mWindow != null) {
663             throw new IllegalStateException("Must be called before onCreate()");
664         }
665         if (ActivityManager.isHighEndGfx()) {
666             mHardwareAccelerated = true;
667             return true;
668         }
669         return false;
670     }
671 
onCreate()672     @Override public void onCreate() {
673         mTheme = Resources.selectSystemTheme(mTheme,
674                 getApplicationInfo().targetSdkVersion,
675                 android.R.style.Theme_InputMethod,
676                 android.R.style.Theme_Holo_InputMethod,
677                 android.R.style.Theme_DeviceDefault_InputMethod,
678                 android.R.style.Theme_DeviceDefault_InputMethod);
679         super.setTheme(mTheme);
680         super.onCreate();
681         mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
682         // If the previous IME has occupied non-empty inset in the screen, we need to decide whether
683         // we continue to use the same size of the inset or update it
684         mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
685         mInflater = (LayoutInflater)getSystemService(
686                 Context.LAYOUT_INFLATER_SERVICE);
687         mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
688                 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
689         if (mHardwareAccelerated) {
690             mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
691         }
692         initViews();
693         mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
694     }
695 
696     /**
697      * This is a hook that subclasses can use to perform initialization of
698      * their interface.  It is called for you prior to any of your UI objects
699      * being created, both after the service is first created and after a
700      * configuration change happens.
701      */
onInitializeInterface()702     public void onInitializeInterface() {
703         // Intentionally empty
704     }
705 
initialize()706     void initialize() {
707         if (!mInitialized) {
708             mInitialized = true;
709             onInitializeInterface();
710         }
711     }
712 
initViews()713     void initViews() {
714         mInitialized = false;
715         mWindowCreated = false;
716         mShowInputRequested = false;
717         mShowInputForced = false;
718 
719         mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
720         mRootView = mInflater.inflate(
721                 com.android.internal.R.layout.input_method, null);
722         mRootView.setSystemUiVisibility(
723                 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
724         mWindow.setContentView(mRootView);
725         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
726         mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
727         if (Settings.Global.getInt(getContentResolver(),
728                 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
729             mWindow.getWindow().setWindowAnimations(
730                     com.android.internal.R.style.Animation_InputMethodFancy);
731         }
732         mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
733         mExtractViewHidden = false;
734         mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
735         mExtractView = null;
736         mExtractEditText = null;
737         mExtractAccessories = null;
738         mExtractAction = null;
739         mFullscreenApplied = false;
740 
741         mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
742         mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
743         mInputView = null;
744         mIsInputViewShown = false;
745 
746         mExtractFrame.setVisibility(View.GONE);
747         mCandidatesVisibility = getCandidatesHiddenVisibility();
748         mCandidatesFrame.setVisibility(mCandidatesVisibility);
749         mInputFrame.setVisibility(View.GONE);
750     }
751 
onDestroy()752     @Override public void onDestroy() {
753         super.onDestroy();
754         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
755                 mInsetsComputer);
756         doFinishInput();
757         if (mWindowAdded) {
758             // Disable exit animation for the current IME window
759             // to avoid the race condition between the exit and enter animations
760             // when the current IME is being switched to another one.
761             mWindow.getWindow().setWindowAnimations(0);
762             mWindow.dismiss();
763         }
764     }
765 
766     /**
767      * Take care of handling configuration changes.  Subclasses of
768      * InputMethodService generally don't need to deal directly with
769      * this on their own; the standard implementation here takes care of
770      * regenerating the input method UI as a result of the configuration
771      * change, so you can rely on your {@link #onCreateInputView} and
772      * other methods being called as appropriate due to a configuration change.
773      *
774      * <p>When a configuration change does happen,
775      * {@link #onInitializeInterface()} is guaranteed to be called the next
776      * time prior to any of the other input or UI creation callbacks.  The
777      * following will be called immediately depending if appropriate for current
778      * state: {@link #onStartInput} if input is active, and
779      * {@link #onCreateInputView} and {@link #onStartInputView} and related
780      * appropriate functions if the UI is displayed.
781      */
onConfigurationChanged(Configuration newConfig)782     @Override public void onConfigurationChanged(Configuration newConfig) {
783         super.onConfigurationChanged(newConfig);
784 
785         boolean visible = mWindowVisible;
786         int showFlags = mShowInputFlags;
787         boolean showingInput = mShowInputRequested;
788         CompletionInfo[] completions = mCurCompletions;
789         initViews();
790         mInputViewStarted = false;
791         mCandidatesViewStarted = false;
792         if (mInputStarted) {
793             doStartInput(getCurrentInputConnection(),
794                     getCurrentInputEditorInfo(), true);
795         }
796         if (visible) {
797             if (showingInput) {
798                 // If we were last showing the soft keyboard, try to do so again.
799                 if (onShowInputRequested(showFlags, true)) {
800                     showWindow(true);
801                     if (completions != null) {
802                         mCurCompletions = completions;
803                         onDisplayCompletions(completions);
804                     }
805                 } else {
806                     doHideWindow();
807                 }
808             } else if (mCandidatesVisibility == View.VISIBLE) {
809                 // If the candidates are currently visible, make sure the
810                 // window is shown for them.
811                 showWindow(false);
812             } else {
813                 // Otherwise hide the window.
814                 doHideWindow();
815             }
816             // If user uses hard keyboard, IME button should always be shown.
817             boolean showing = onEvaluateInputViewShown();
818             mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
819                     mBackDisposition);
820         }
821     }
822 
823     /**
824      * Implement to return our standard {@link InputMethodImpl}.  Subclasses
825      * can override to provide their own customized version.
826      */
827     @Override
onCreateInputMethodInterface()828     public AbstractInputMethodImpl onCreateInputMethodInterface() {
829         return new InputMethodImpl();
830     }
831 
832     /**
833      * Implement to return our standard {@link InputMethodSessionImpl}.  Subclasses
834      * can override to provide their own customized version.
835      */
836     @Override
onCreateInputMethodSessionInterface()837     public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
838         return new InputMethodSessionImpl();
839     }
840 
getLayoutInflater()841     public LayoutInflater getLayoutInflater() {
842         return mInflater;
843     }
844 
getWindow()845     public Dialog getWindow() {
846         return mWindow;
847     }
848 
setBackDisposition(int disposition)849     public void setBackDisposition(int disposition) {
850         mBackDisposition = disposition;
851     }
852 
getBackDisposition()853     public int getBackDisposition() {
854         return mBackDisposition;
855     }
856 
857     /**
858      * Return the maximum width, in pixels, available the input method.
859      * Input methods are positioned at the bottom of the screen and, unless
860      * running in fullscreen, will generally want to be as short as possible
861      * so should compute their height based on their contents.  However, they
862      * can stretch as much as needed horizontally.  The function returns to
863      * you the maximum amount of space available horizontally, which you can
864      * use if needed for UI placement.
865      *
866      * <p>In many cases this is not needed, you can just rely on the normal
867      * view layout mechanisms to position your views within the full horizontal
868      * space given to the input method.
869      *
870      * <p>Note that this value can change dynamically, in particular when the
871      * screen orientation changes.
872      */
getMaxWidth()873     public int getMaxWidth() {
874         WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
875         return wm.getDefaultDisplay().getWidth();
876     }
877 
878     /**
879      * Return the currently active InputBinding for the input method, or
880      * null if there is none.
881      */
getCurrentInputBinding()882     public InputBinding getCurrentInputBinding() {
883         return mInputBinding;
884     }
885 
886     /**
887      * Retrieve the currently active InputConnection that is bound to
888      * the input method, or null if there is none.
889      */
getCurrentInputConnection()890     public InputConnection getCurrentInputConnection() {
891         InputConnection ic = mStartedInputConnection;
892         if (ic != null) {
893             return ic;
894         }
895         return mInputConnection;
896     }
897 
getCurrentInputStarted()898     public boolean getCurrentInputStarted() {
899         return mInputStarted;
900     }
901 
getCurrentInputEditorInfo()902     public EditorInfo getCurrentInputEditorInfo() {
903         return mInputEditorInfo;
904     }
905 
906     /**
907      * Re-evaluate whether the input method should be running in fullscreen
908      * mode, and update its UI if this has changed since the last time it
909      * was evaluated.  This will call {@link #onEvaluateFullscreenMode()} to
910      * determine whether it should currently run in fullscreen mode.  You
911      * can use {@link #isFullscreenMode()} to determine if the input method
912      * is currently running in fullscreen mode.
913      */
updateFullscreenMode()914     public void updateFullscreenMode() {
915         boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
916         boolean changed = mLastShowInputRequested != mShowInputRequested;
917         if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
918             changed = true;
919             mIsFullscreen = isFullscreen;
920             InputConnection ic = getCurrentInputConnection();
921             if (ic != null) ic.reportFullscreenMode(isFullscreen);
922             mFullscreenApplied = true;
923             initialize();
924             LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
925                     mFullscreenArea.getLayoutParams();
926             if (isFullscreen) {
927                 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
928                         com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
929                 lp.height = 0;
930                 lp.weight = 1;
931             } else {
932                 mFullscreenArea.setBackgroundDrawable(null);
933                 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
934                 lp.weight = 0;
935             }
936             ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
937                     mFullscreenArea, lp);
938             if (isFullscreen) {
939                 if (mExtractView == null) {
940                     View v = onCreateExtractTextView();
941                     if (v != null) {
942                         setExtractView(v);
943                     }
944                 }
945                 startExtractingText(false);
946             }
947             updateExtractFrameVisibility();
948         }
949 
950         if (changed) {
951             onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
952             mLastShowInputRequested = mShowInputRequested;
953         }
954     }
955 
956     /**
957      * Update the given window's parameters for the given mode.  This is called
958      * when the window is first displayed and each time the fullscreen or
959      * candidates only mode changes.
960      *
961      * <p>The default implementation makes the layout for the window
962      * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
963      * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
964      *
965      * @param win The input method's window.
966      * @param isFullscreen If true, the window is running in fullscreen mode
967      * and intended to cover the entire application display.
968      * @param isCandidatesOnly If true, the window is only showing the
969      * candidates view and none of the rest of its UI.  This is mutually
970      * exclusive with fullscreen mode.
971      */
onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly)972     public void onConfigureWindow(Window win, boolean isFullscreen,
973             boolean isCandidatesOnly) {
974         final int currentHeight = mWindow.getWindow().getAttributes().height;
975         final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
976         if (mIsInputViewShown && currentHeight != newHeight) {
977             Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: "
978                     + currentHeight + " -> " + newHeight);
979         }
980         mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
981     }
982 
983     /**
984      * Return whether the input method is <em>currently</em> running in
985      * fullscreen mode.  This is the mode that was last determined and
986      * applied by {@link #updateFullscreenMode()}.
987      */
isFullscreenMode()988     public boolean isFullscreenMode() {
989         return mIsFullscreen;
990     }
991 
992     /**
993      * Override this to control when the input method should run in
994      * fullscreen mode.  The default implementation runs in fullsceen only
995      * when the screen is in landscape mode.  If you change what
996      * this returns, you will need to call {@link #updateFullscreenMode()}
997      * yourself whenever the returned value may have changed to have it
998      * re-evaluated and applied.
999      */
onEvaluateFullscreenMode()1000     public boolean onEvaluateFullscreenMode() {
1001         Configuration config = getResources().getConfiguration();
1002         if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
1003             return false;
1004         }
1005         if (mInputEditorInfo != null
1006                 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
1007             return false;
1008         }
1009         return true;
1010     }
1011 
1012     /**
1013      * Controls the visibility of the extracted text area.  This only applies
1014      * when the input method is in fullscreen mode, and thus showing extracted
1015      * text.  When false, the extracted text will not be shown, allowing some
1016      * of the application to be seen behind.  This is normally set for you
1017      * by {@link #onUpdateExtractingVisibility}.  This controls the visibility
1018      * of both the extracted text and candidate view; the latter since it is
1019      * not useful if there is no text to see.
1020      */
setExtractViewShown(boolean shown)1021     public void setExtractViewShown(boolean shown) {
1022         if (mExtractViewHidden == shown) {
1023             mExtractViewHidden = !shown;
1024             updateExtractFrameVisibility();
1025         }
1026     }
1027 
1028     /**
1029      * Return whether the fullscreen extract view is shown.  This will only
1030      * return true if {@link #isFullscreenMode()} returns true, and in that
1031      * case its value depends on the last call to
1032      * {@link #setExtractViewShown(boolean)}.  This effectively lets you
1033      * determine if the application window is entirely covered (when this
1034      * returns true) or if some part of it may be shown (if this returns
1035      * false, though if {@link #isFullscreenMode()} returns true in that case
1036      * then it is probably only a sliver of the application).
1037      */
isExtractViewShown()1038     public boolean isExtractViewShown() {
1039         return mIsFullscreen && !mExtractViewHidden;
1040     }
1041 
updateExtractFrameVisibility()1042     void updateExtractFrameVisibility() {
1043         final int vis;
1044         if (isFullscreenMode()) {
1045             vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
1046             // "vis" should be applied for the extract frame as well in the fullscreen mode.
1047             mExtractFrame.setVisibility(vis);
1048         } else {
1049             vis = View.VISIBLE;
1050             mExtractFrame.setVisibility(View.GONE);
1051         }
1052         updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
1053         if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
1054             int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1055                     ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1056                     : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1057                     0);
1058             if (animRes != 0) {
1059                 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1060                         this, animRes));
1061             }
1062         }
1063         mFullscreenArea.setVisibility(vis);
1064     }
1065 
1066     /**
1067      * Compute the interesting insets into your UI.  The default implementation
1068      * uses the top of the candidates frame for the visible insets, and the
1069      * top of the input frame for the content insets.  The default touchable
1070      * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1071      *
1072      * <p>Note that this method is not called when
1073      * {@link #isExtractViewShown} returns true, since
1074      * in that case the application is left as-is behind the input method and
1075      * not impacted by anything in its UI.
1076      *
1077      * @param outInsets Fill in with the current UI insets.
1078      */
onComputeInsets(Insets outInsets)1079     public void onComputeInsets(Insets outInsets) {
1080         int[] loc = mTmpLocation;
1081         if (mInputFrame.getVisibility() == View.VISIBLE) {
1082             mInputFrame.getLocationInWindow(loc);
1083         } else {
1084             View decor = getWindow().getWindow().getDecorView();
1085             loc[1] = decor.getHeight();
1086         }
1087         if (isFullscreenMode()) {
1088             // In fullscreen mode, we never resize the underlying window.
1089             View decor = getWindow().getWindow().getDecorView();
1090             outInsets.contentTopInsets = decor.getHeight();
1091         } else {
1092             outInsets.contentTopInsets = loc[1];
1093         }
1094         if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1095             mCandidatesFrame.getLocationInWindow(loc);
1096         }
1097         outInsets.visibleTopInsets = loc[1];
1098         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
1099         outInsets.touchableRegion.setEmpty();
1100     }
1101 
1102     /**
1103      * Re-evaluate whether the soft input area should currently be shown, and
1104      * update its UI if this has changed since the last time it
1105      * was evaluated.  This will call {@link #onEvaluateInputViewShown()} to
1106      * determine whether the input view should currently be shown.  You
1107      * can use {@link #isInputViewShown()} to determine if the input view
1108      * is currently shown.
1109      */
updateInputViewShown()1110     public void updateInputViewShown() {
1111         boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
1112         if (mIsInputViewShown != isShown && mWindowVisible) {
1113             mIsInputViewShown = isShown;
1114             mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1115             if (mInputView == null) {
1116                 initialize();
1117                 View v = onCreateInputView();
1118                 if (v != null) {
1119                     setInputView(v);
1120                 }
1121             }
1122         }
1123     }
1124 
1125     /**
1126      * Returns true if we have been asked to show our input view.
1127      */
isShowInputRequested()1128     public boolean isShowInputRequested() {
1129         return mShowInputRequested;
1130     }
1131 
1132     /**
1133      * Return whether the soft input view is <em>currently</em> shown to the
1134      * user.  This is the state that was last determined and
1135      * applied by {@link #updateInputViewShown()}.
1136      */
isInputViewShown()1137     public boolean isInputViewShown() {
1138         return mIsInputViewShown && mWindowVisible;
1139     }
1140 
1141     /**
1142      * Override this to control when the soft input area should be shown to
1143      * the user.  The default implementation only shows the input view when
1144      * there is no hard keyboard or the keyboard is hidden.  If you change what
1145      * this returns, you will need to call {@link #updateInputViewShown()}
1146      * yourself whenever the returned value may have changed to have it
1147      * re-evaluated and applied.
1148      */
onEvaluateInputViewShown()1149     public boolean onEvaluateInputViewShown() {
1150         Configuration config = getResources().getConfiguration();
1151         return config.keyboard == Configuration.KEYBOARD_NOKEYS
1152                 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
1153     }
1154 
1155     /**
1156      * Controls the visibility of the candidates display area.  By default
1157      * it is hidden.
1158      */
setCandidatesViewShown(boolean shown)1159     public void setCandidatesViewShown(boolean shown) {
1160         updateCandidatesVisibility(shown);
1161         if (!mShowInputRequested && mWindowVisible != shown) {
1162             // If we are being asked to show the candidates view while the app
1163             // has not asked for the input view to be shown, then we need
1164             // to update whether the window is shown.
1165             if (shown) {
1166                 showWindow(false);
1167             } else {
1168                 doHideWindow();
1169             }
1170         }
1171     }
1172 
updateCandidatesVisibility(boolean shown)1173     void updateCandidatesVisibility(boolean shown) {
1174         int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1175         if (mCandidatesVisibility != vis) {
1176             mCandidatesFrame.setVisibility(vis);
1177             mCandidatesVisibility = vis;
1178         }
1179     }
1180 
1181     /**
1182      * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1183      * or {@link View#GONE View.GONE}) of the candidates view when it is not
1184      * shown.  The default implementation returns GONE when
1185      * {@link #isExtractViewShown} returns true,
1186      * otherwise VISIBLE.  Be careful if you change this to return GONE in
1187      * other situations -- if showing or hiding the candidates view causes
1188      * your window to resize, this can cause temporary drawing artifacts as
1189      * the resize takes place.
1190      */
getCandidatesHiddenVisibility()1191     public int getCandidatesHiddenVisibility() {
1192         return isExtractViewShown() ? View.GONE : View.INVISIBLE;
1193     }
1194 
showStatusIcon(@rawableRes int iconResId)1195     public void showStatusIcon(@DrawableRes int iconResId) {
1196         mStatusIcon = iconResId;
1197         mImm.showStatusIcon(mToken, getPackageName(), iconResId);
1198     }
1199 
hideStatusIcon()1200     public void hideStatusIcon() {
1201         mStatusIcon = 0;
1202         mImm.hideStatusIcon(mToken);
1203     }
1204 
1205     /**
1206      * Force switch to a new input method, as identified by <var>id</var>.  This
1207      * input method will be destroyed, and the requested one started on the
1208      * current input field.
1209      *
1210      * @param id Unique identifier of the new input method ot start.
1211      */
switchInputMethod(String id)1212     public void switchInputMethod(String id) {
1213         mImm.setInputMethod(mToken, id);
1214     }
1215 
setExtractView(View view)1216     public void setExtractView(View view) {
1217         mExtractFrame.removeAllViews();
1218         mExtractFrame.addView(view, new FrameLayout.LayoutParams(
1219                 ViewGroup.LayoutParams.MATCH_PARENT,
1220                 ViewGroup.LayoutParams.MATCH_PARENT));
1221         mExtractView = view;
1222         if (view != null) {
1223             mExtractEditText = (ExtractEditText)view.findViewById(
1224                     com.android.internal.R.id.inputExtractEditText);
1225             mExtractEditText.setIME(this);
1226             mExtractAction = (Button)view.findViewById(
1227                     com.android.internal.R.id.inputExtractAction);
1228             if (mExtractAction != null) {
1229                 mExtractAccessories = (ViewGroup)view.findViewById(
1230                         com.android.internal.R.id.inputExtractAccessories);
1231             }
1232             startExtractingText(false);
1233         } else {
1234             mExtractEditText = null;
1235             mExtractAccessories = null;
1236             mExtractAction = null;
1237         }
1238     }
1239 
1240     /**
1241      * Replaces the current candidates view with a new one.  You only need to
1242      * call this when dynamically changing the view; normally, you should
1243      * implement {@link #onCreateCandidatesView()} and create your view when
1244      * first needed by the input method.
1245      */
setCandidatesView(View view)1246     public void setCandidatesView(View view) {
1247         mCandidatesFrame.removeAllViews();
1248         mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
1249                 ViewGroup.LayoutParams.MATCH_PARENT,
1250                 ViewGroup.LayoutParams.WRAP_CONTENT));
1251     }
1252 
1253     /**
1254      * Replaces the current input view with a new one.  You only need to
1255      * call this when dynamically changing the view; normally, you should
1256      * implement {@link #onCreateInputView()} and create your view when
1257      * first needed by the input method.
1258      */
setInputView(View view)1259     public void setInputView(View view) {
1260         mInputFrame.removeAllViews();
1261         mInputFrame.addView(view, new FrameLayout.LayoutParams(
1262                 ViewGroup.LayoutParams.MATCH_PARENT,
1263                 ViewGroup.LayoutParams.WRAP_CONTENT));
1264         mInputView = view;
1265     }
1266 
1267     /**
1268      * Called by the framework to create the layout for showing extacted text.
1269      * Only called when in fullscreen mode.  The returned view hierarchy must
1270      * have an {@link ExtractEditText} whose ID is
1271      * {@link android.R.id#inputExtractEditText}.
1272      */
onCreateExtractTextView()1273     public View onCreateExtractTextView() {
1274         return mInflater.inflate(
1275                 com.android.internal.R.layout.input_method_extract_view, null);
1276     }
1277 
1278     /**
1279      * Create and return the view hierarchy used to show candidates.  This will
1280      * be called once, when the candidates are first displayed.  You can return
1281      * null to have no candidates view; the default implementation returns null.
1282      *
1283      * <p>To control when the candidates view is displayed, use
1284      * {@link #setCandidatesViewShown(boolean)}.
1285      * To change the candidates view after the first one is created by this
1286      * function, use {@link #setCandidatesView(View)}.
1287      */
onCreateCandidatesView()1288     public View onCreateCandidatesView() {
1289         return null;
1290     }
1291 
1292     /**
1293      * Create and return the view hierarchy used for the input area (such as
1294      * a soft keyboard).  This will be called once, when the input area is
1295      * first displayed.  You can return null to have no input area; the default
1296      * implementation returns null.
1297      *
1298      * <p>To control when the input view is displayed, implement
1299      * {@link #onEvaluateInputViewShown()}.
1300      * To change the input view after the first one is created by this
1301      * function, use {@link #setInputView(View)}.
1302      */
onCreateInputView()1303     public View onCreateInputView() {
1304         return null;
1305     }
1306 
1307     /**
1308      * Called when the input view is being shown and input has started on
1309      * a new editor.  This will always be called after {@link #onStartInput},
1310      * allowing you to do your general setup there and just view-specific
1311      * setup here.  You are guaranteed that {@link #onCreateInputView()} will
1312      * have been called some time before this function is called.
1313      *
1314      * @param info Description of the type of text being edited.
1315      * @param restarting Set to true if we are restarting input on the
1316      * same text field as before.
1317      */
onStartInputView(EditorInfo info, boolean restarting)1318     public void onStartInputView(EditorInfo info, boolean restarting) {
1319         // Intentionally empty
1320     }
1321 
1322     /**
1323      * Called when the input view is being hidden from the user.  This will
1324      * be called either prior to hiding the window, or prior to switching to
1325      * another target for editing.
1326      *
1327      * <p>The default
1328      * implementation uses the InputConnection to clear any active composing
1329      * text; you can override this (not calling the base class implementation)
1330      * to perform whatever behavior you would like.
1331      *
1332      * @param finishingInput If true, {@link #onFinishInput} will be
1333      * called immediately after.
1334      */
onFinishInputView(boolean finishingInput)1335     public void onFinishInputView(boolean finishingInput) {
1336         if (!finishingInput) {
1337             InputConnection ic = getCurrentInputConnection();
1338             if (ic != null) {
1339                 ic.finishComposingText();
1340             }
1341         }
1342     }
1343 
1344     /**
1345      * Called when only the candidates view has been shown for showing
1346      * processing as the user enters text through a hard keyboard.
1347      * This will always be called after {@link #onStartInput},
1348      * allowing you to do your general setup there and just view-specific
1349      * setup here.  You are guaranteed that {@link #onCreateCandidatesView()}
1350      * will have been called some time before this function is called.
1351      *
1352      * <p>Note that this will <em>not</em> be called when the input method
1353      * is running in full editing mode, and thus receiving
1354      * {@link #onStartInputView} to initiate that operation.  This is only
1355      * for the case when candidates are being shown while the input method
1356      * editor is hidden but wants to show its candidates UI as text is
1357      * entered through some other mechanism.
1358      *
1359      * @param info Description of the type of text being edited.
1360      * @param restarting Set to true if we are restarting input on the
1361      * same text field as before.
1362      */
onStartCandidatesView(EditorInfo info, boolean restarting)1363     public void onStartCandidatesView(EditorInfo info, boolean restarting) {
1364         // Intentionally empty
1365     }
1366 
1367     /**
1368      * Called when the candidates view is being hidden from the user.  This will
1369      * be called either prior to hiding the window, or prior to switching to
1370      * another target for editing.
1371      *
1372      * <p>The default
1373      * implementation uses the InputConnection to clear any active composing
1374      * text; you can override this (not calling the base class implementation)
1375      * to perform whatever behavior you would like.
1376      *
1377      * @param finishingInput If true, {@link #onFinishInput} will be
1378      * called immediately after.
1379      */
onFinishCandidatesView(boolean finishingInput)1380     public void onFinishCandidatesView(boolean finishingInput) {
1381         if (!finishingInput) {
1382             InputConnection ic = getCurrentInputConnection();
1383             if (ic != null) {
1384                 ic.finishComposingText();
1385             }
1386         }
1387     }
1388 
1389     /**
1390      * The system has decided that it may be time to show your input method.
1391      * This is called due to a corresponding call to your
1392      * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
1393      * method.  The default implementation uses
1394      * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
1395      * and the current configuration to decide whether the input view should
1396      * be shown at this point.
1397      *
1398      * @param flags Provides additional information about the show request,
1399      * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
1400      * @param configChange This is true if we are re-showing due to a
1401      * configuration change.
1402      * @return Returns true to indicate that the window should be shown.
1403      */
onShowInputRequested(int flags, boolean configChange)1404     public boolean onShowInputRequested(int flags, boolean configChange) {
1405         if (!onEvaluateInputViewShown()) {
1406             return false;
1407         }
1408         if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
1409             if (!configChange && onEvaluateFullscreenMode()) {
1410                 // Don't show if this is not explicitly requested by the user and
1411                 // the input method is fullscreen.  That would be too disruptive.
1412                 // However, we skip this change for a config change, since if
1413                 // the IME is already shown we do want to go into fullscreen
1414                 // mode at this point.
1415                 return false;
1416             }
1417             Configuration config = getResources().getConfiguration();
1418             if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
1419                 // And if the device has a hard keyboard, even if it is
1420                 // currently hidden, don't show the input method implicitly.
1421                 // These kinds of devices don't need it that much.
1422                 return false;
1423             }
1424         }
1425         if ((flags&InputMethod.SHOW_FORCED) != 0) {
1426             mShowInputForced = true;
1427         }
1428         return true;
1429     }
1430 
showWindow(boolean showInput)1431     public void showWindow(boolean showInput) {
1432         if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
1433                 + " mShowInputRequested=" + mShowInputRequested
1434                 + " mWindowAdded=" + mWindowAdded
1435                 + " mWindowCreated=" + mWindowCreated
1436                 + " mWindowVisible=" + mWindowVisible
1437                 + " mInputStarted=" + mInputStarted);
1438 
1439         if (mInShowWindow) {
1440             Log.w(TAG, "Re-entrance in to showWindow");
1441             return;
1442         }
1443 
1444         try {
1445             mWindowWasVisible = mWindowVisible;
1446             mInShowWindow = true;
1447             showWindowInner(showInput);
1448         } finally {
1449             mWindowWasVisible = true;
1450             mInShowWindow = false;
1451         }
1452     }
1453 
showWindowInner(boolean showInput)1454     void showWindowInner(boolean showInput) {
1455         boolean doShowInput = false;
1456         final int previousImeWindowStatus =
1457                 (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
1458         mWindowVisible = true;
1459         if (!mShowInputRequested) {
1460             if (mInputStarted) {
1461                 if (showInput) {
1462                     doShowInput = true;
1463                     mShowInputRequested = true;
1464                 }
1465             }
1466         } else {
1467             showInput = true;
1468         }
1469 
1470         if (DEBUG) Log.v(TAG, "showWindow: updating UI");
1471         initialize();
1472         updateFullscreenMode();
1473         updateInputViewShown();
1474 
1475         if (!mWindowAdded || !mWindowCreated) {
1476             mWindowAdded = true;
1477             mWindowCreated = true;
1478             initialize();
1479             if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
1480             View v = onCreateCandidatesView();
1481             if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
1482             if (v != null) {
1483                 setCandidatesView(v);
1484             }
1485         }
1486         if (mShowInputRequested) {
1487             if (!mInputViewStarted) {
1488                 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1489                 mInputViewStarted = true;
1490                 onStartInputView(mInputEditorInfo, false);
1491             }
1492         } else if (!mCandidatesViewStarted) {
1493             if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1494             mCandidatesViewStarted = true;
1495             onStartCandidatesView(mInputEditorInfo, false);
1496         }
1497 
1498         if (doShowInput) {
1499             startExtractingText(false);
1500         }
1501 
1502         final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
1503         if (previousImeWindowStatus != nextImeWindowStatus) {
1504             mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
1505         }
1506         if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
1507             if (DEBUG) Log.v(TAG, "showWindow: showing!");
1508             onWindowShown();
1509             mWindow.show();
1510             // Put here rather than in onWindowShown() in case people forget to call
1511             // super.onWindowShown().
1512             mShouldClearInsetOfPreviousIme = false;
1513         }
1514     }
1515 
finishViews()1516     private void finishViews() {
1517         if (mInputViewStarted) {
1518             if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1519             onFinishInputView(false);
1520         } else if (mCandidatesViewStarted) {
1521             if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1522             onFinishCandidatesView(false);
1523         }
1524         mInputViewStarted = false;
1525         mCandidatesViewStarted = false;
1526     }
1527 
doHideWindow()1528     private void doHideWindow() {
1529         mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
1530         hideWindow();
1531     }
1532 
hideWindow()1533     public void hideWindow() {
1534         finishViews();
1535         if (mWindowVisible) {
1536             mWindow.hide();
1537             mWindowVisible = false;
1538             onWindowHidden();
1539             mWindowWasVisible = false;
1540         }
1541     }
1542 
1543     /**
1544      * Called when the input method window has been shown to the user, after
1545      * previously not being visible.  This is done after all of the UI setup
1546      * for the window has occurred (creating its views etc).
1547      */
onWindowShown()1548     public void onWindowShown() {
1549         // Intentionally empty
1550     }
1551 
1552     /**
1553      * Called when the input method window has been hidden from the user,
1554      * after previously being visible.
1555      */
onWindowHidden()1556     public void onWindowHidden() {
1557         // Intentionally empty
1558     }
1559 
1560     /**
1561      * Reset the inset occupied the previous IME when and only when
1562      * {@link #mShouldClearInsetOfPreviousIme} is {@code true}.
1563      */
clearInsetOfPreviousIme()1564     private void clearInsetOfPreviousIme() {
1565         if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() "
1566                 + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
1567         if (!mShouldClearInsetOfPreviousIme || mWindow == null) return;
1568         try {
1569             // We do not call onWindowShown() and onWindowHidden() so as not to make the IME author
1570             // confused.
1571             // TODO: Find out a better way which has less side-effect.
1572             mWindow.show();
1573             mWindow.hide();
1574         } catch (WindowManager.BadTokenException e) {
1575             if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme: BadTokenException: IME is done.");
1576             mWindowVisible = false;
1577             mWindowAdded = false;
1578         }
1579         mShouldClearInsetOfPreviousIme = false;
1580     }
1581 
1582     /**
1583      * Called when a new client has bound to the input method.  This
1584      * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
1585      * and {@link #onFinishInput()} calls as the user navigates through its
1586      * UI.  Upon this call you know that {@link #getCurrentInputBinding}
1587      * and {@link #getCurrentInputConnection} return valid objects.
1588      */
onBindInput()1589     public void onBindInput() {
1590         // Intentionally empty
1591     }
1592 
1593     /**
1594      * Called when the previous bound client is no longer associated
1595      * with the input method.  After returning {@link #getCurrentInputBinding}
1596      * and {@link #getCurrentInputConnection} will no longer return
1597      * valid objects.
1598      */
onUnbindInput()1599     public void onUnbindInput() {
1600         // Intentionally empty
1601     }
1602 
1603     /**
1604      * Called to inform the input method that text input has started in an
1605      * editor.  You should use this callback to initialize the state of your
1606      * input to match the state of the editor given to it.
1607      *
1608      * @param attribute The attributes of the editor that input is starting
1609      * in.
1610      * @param restarting Set to true if input is restarting in the same
1611      * editor such as because the application has changed the text in
1612      * the editor.  Otherwise will be false, indicating this is a new
1613      * session with the editor.
1614      */
onStartInput(EditorInfo attribute, boolean restarting)1615     public void onStartInput(EditorInfo attribute, boolean restarting) {
1616         // Intentionally empty
1617     }
1618 
doFinishInput()1619     void doFinishInput() {
1620         if (mInputViewStarted) {
1621             if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1622             onFinishInputView(true);
1623         } else if (mCandidatesViewStarted) {
1624             if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1625             onFinishCandidatesView(true);
1626         }
1627         mInputViewStarted = false;
1628         mCandidatesViewStarted = false;
1629         if (mInputStarted) {
1630             if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
1631             onFinishInput();
1632         }
1633         mInputStarted = false;
1634         mStartedInputConnection = null;
1635         mCurCompletions = null;
1636     }
1637 
doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting)1638     void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
1639         if (!restarting) {
1640             doFinishInput();
1641         }
1642         mInputStarted = true;
1643         mStartedInputConnection = ic;
1644         mInputEditorInfo = attribute;
1645         initialize();
1646         if (DEBUG) Log.v(TAG, "CALL: onStartInput");
1647         onStartInput(attribute, restarting);
1648         if (mWindowVisible) {
1649             if (mShowInputRequested) {
1650                 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1651                 mInputViewStarted = true;
1652                 onStartInputView(mInputEditorInfo, restarting);
1653                 startExtractingText(true);
1654             } else if (mCandidatesVisibility == View.VISIBLE) {
1655                 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1656                 mCandidatesViewStarted = true;
1657                 onStartCandidatesView(mInputEditorInfo, restarting);
1658             }
1659         }
1660     }
1661 
1662     /**
1663      * Called to inform the input method that text input has finished in
1664      * the last editor.  At this point there may be a call to
1665      * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
1666      * new editor, or the input method may be left idle.  This method is
1667      * <em>not</em> called when input restarts in the same editor.
1668      *
1669      * <p>The default
1670      * implementation uses the InputConnection to clear any active composing
1671      * text; you can override this (not calling the base class implementation)
1672      * to perform whatever behavior you would like.
1673      */
onFinishInput()1674     public void onFinishInput() {
1675         InputConnection ic = getCurrentInputConnection();
1676         if (ic != null) {
1677             ic.finishComposingText();
1678         }
1679     }
1680 
1681     /**
1682      * Called when the application has reported auto-completion candidates that
1683      * it would like to have the input method displayed.  Typically these are
1684      * only used when an input method is running in full-screen mode, since
1685      * otherwise the user can see and interact with the pop-up window of
1686      * completions shown by the application.
1687      *
1688      * <p>The default implementation here does nothing.
1689      */
onDisplayCompletions(CompletionInfo[] completions)1690     public void onDisplayCompletions(CompletionInfo[] completions) {
1691         // Intentionally empty
1692     }
1693 
1694     /**
1695      * Called when the application has reported new extracted text to be shown
1696      * due to changes in its current text state.  The default implementation
1697      * here places the new text in the extract edit text, when the input
1698      * method is running in fullscreen mode.
1699      */
onUpdateExtractedText(int token, ExtractedText text)1700     public void onUpdateExtractedText(int token, ExtractedText text) {
1701         if (mExtractedToken != token) {
1702             return;
1703         }
1704         if (text != null) {
1705             if (mExtractEditText != null) {
1706                 mExtractedText = text;
1707                 mExtractEditText.setExtractedText(text);
1708             }
1709         }
1710     }
1711 
1712     /**
1713      * Called when the application has reported a new selection region of
1714      * the text.  This is called whether or not the input method has requested
1715      * extracted text updates, although if so it will not receive this call
1716      * if the extracted text has changed as well.
1717      *
1718      * <p>Be careful about changing the text in reaction to this call with
1719      * methods such as setComposingText, commitText or
1720      * deleteSurroundingText. If the cursor moves as a result, this method
1721      * will be called again, which may result in an infinite loop.
1722      *
1723      * <p>The default implementation takes care of updating the cursor in
1724      * the extract text, if it is being shown.
1725      */
onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)1726     public void onUpdateSelection(int oldSelStart, int oldSelEnd,
1727             int newSelStart, int newSelEnd,
1728             int candidatesStart, int candidatesEnd) {
1729         final ExtractEditText eet = mExtractEditText;
1730         if (eet != null && isFullscreenMode() && mExtractedText != null) {
1731             final int off = mExtractedText.startOffset;
1732             eet.startInternalChanges();
1733             newSelStart -= off;
1734             newSelEnd -= off;
1735             final int len = eet.getText().length();
1736             if (newSelStart < 0) newSelStart = 0;
1737             else if (newSelStart > len) newSelStart = len;
1738             if (newSelEnd < 0) newSelEnd = 0;
1739             else if (newSelEnd > len) newSelEnd = len;
1740             eet.setSelection(newSelStart, newSelEnd);
1741             eet.finishInternalChanges();
1742         }
1743     }
1744 
1745     /**
1746      * Called when the user tapped or clicked a text view.
1747      * IMEs can't rely on this method being called because this was not part of the original IME
1748      * protocol, so applications with custom text editing written before this method appeared will
1749      * not call to inform the IME of this interaction.
1750      * @param focusChanged true if the user changed the focused view by this click.
1751      */
onViewClicked(boolean focusChanged)1752     public void onViewClicked(boolean focusChanged) {
1753         // Intentionally empty
1754     }
1755 
1756     /**
1757      * Called when the application has reported a new location of its text
1758      * cursor.  This is only called if explicitly requested by the input method.
1759      * The default implementation does nothing.
1760      * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
1761      */
1762     @Deprecated
onUpdateCursor(Rect newCursor)1763     public void onUpdateCursor(Rect newCursor) {
1764         // Intentionally empty
1765     }
1766 
1767     /**
1768      * Called when the application has reported a new location of its text insertion point and
1769      * characters in the composition string.  This is only called if explicitly requested by the
1770      * input method. The default implementation does nothing.
1771      * @param cursorAnchorInfo The positional information of the text insertion point and the
1772      * composition string.
1773      */
onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo)1774     public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
1775         // Intentionally empty
1776     }
1777 
1778     /**
1779      * Close this input method's soft input area, removing it from the display.
1780      * The input method will continue running, but the user can no longer use
1781      * it to generate input by touching the screen.
1782      * @param flags Provides additional operating flags.  Currently may be
1783      * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
1784      * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
1785      */
requestHideSelf(int flags)1786     public void requestHideSelf(int flags) {
1787         mImm.hideSoftInputFromInputMethod(mToken, flags);
1788     }
1789 
1790     /**
1791      * Show the input method. This is a call back to the
1792      * IMF to handle showing the input method.
1793      * @param flags Provides additional operating flags.  Currently may be
1794      * 0 or have the {@link InputMethodManager#SHOW_FORCED
1795      * InputMethodManager.} bit set.
1796      */
requestShowSelf(int flags)1797     private void requestShowSelf(int flags) {
1798         mImm.showSoftInputFromInputMethod(mToken, flags);
1799     }
1800 
handleBack(boolean doIt)1801     private boolean handleBack(boolean doIt) {
1802         if (mShowInputRequested) {
1803             // If the soft input area is shown, back closes it and we
1804             // consume the back key.
1805             if (doIt) requestHideSelf(0);
1806             return true;
1807         } else if (mWindowVisible) {
1808             if (mCandidatesVisibility == View.VISIBLE) {
1809                 // If we are showing candidates even if no input area, then
1810                 // hide them.
1811                 if (doIt) setCandidatesViewShown(false);
1812             } else {
1813                 // If we have the window visible for some other reason --
1814                 // most likely to show candidates -- then just get rid
1815                 // of it.  This really shouldn't happen, but just in case...
1816                 if (doIt) doHideWindow();
1817             }
1818             return true;
1819         }
1820         return false;
1821     }
1822 
1823     /**
1824      * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise
1825      * {@code null} is returned.
1826      */
getExtractEditTextIfVisible()1827     private ExtractEditText getExtractEditTextIfVisible() {
1828         if (!isExtractViewShown() || !isInputViewShown()) {
1829             return null;
1830         }
1831         return mExtractEditText;
1832     }
1833 
1834     /**
1835      * Override this to intercept key down events before they are processed by the
1836      * application.  If you return true, the application will not
1837      * process the event itself.  If you return false, the normal application processing
1838      * will occur as if the IME had not seen the event at all.
1839      *
1840      * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
1841      * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
1842      * possibly hide it when the key goes up (if not canceled or long pressed).  In
1843      * addition, in fullscreen mode only, it will consume DPAD movement
1844      * events to move the cursor in the extracted text view, not allowing
1845      * them to perform navigation in the underlying application.
1846      */
onKeyDown(int keyCode, KeyEvent event)1847     public boolean onKeyDown(int keyCode, KeyEvent event) {
1848         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
1849             final ExtractEditText eet = getExtractEditTextIfVisible();
1850             if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
1851                 return true;
1852             }
1853             if (handleBack(false)) {
1854                 event.startTracking();
1855                 return true;
1856             }
1857             return false;
1858         }
1859         return doMovementKey(keyCode, event, MOVEMENT_DOWN);
1860     }
1861 
1862     /**
1863      * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
1864      * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
1865      * the event).
1866      */
onKeyLongPress(int keyCode, KeyEvent event)1867     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1868         return false;
1869     }
1870 
1871     /**
1872      * Override this to intercept special key multiple events before they are
1873      * processed by the
1874      * application.  If you return true, the application will not itself
1875      * process the event.  If you return false, the normal application processing
1876      * will occur as if the IME had not seen the event at all.
1877      *
1878      * <p>The default implementation always returns false, except when
1879      * in fullscreen mode, where it will consume DPAD movement
1880      * events to move the cursor in the extracted text view, not allowing
1881      * them to perform navigation in the underlying application.
1882      */
onKeyMultiple(int keyCode, int count, KeyEvent event)1883     public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1884         return doMovementKey(keyCode, event, count);
1885     }
1886 
1887     /**
1888      * Override this to intercept key up events before they are processed by the
1889      * application.  If you return true, the application will not itself
1890      * process the event.  If you return false, the normal application processing
1891      * will occur as if the IME had not seen the event at all.
1892      *
1893      * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
1894      * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown.  In
1895      * addition, in fullscreen mode only, it will consume DPAD movement
1896      * events to move the cursor in the extracted text view, not allowing
1897      * them to perform navigation in the underlying application.
1898      */
onKeyUp(int keyCode, KeyEvent event)1899     public boolean onKeyUp(int keyCode, KeyEvent event) {
1900         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
1901             final ExtractEditText eet = getExtractEditTextIfVisible();
1902             if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
1903                 return true;
1904             }
1905             if (event.isTracking() && !event.isCanceled()) {
1906                 return handleBack(true);
1907             }
1908         }
1909         return doMovementKey(keyCode, event, MOVEMENT_UP);
1910     }
1911 
1912     /**
1913      * Override this to intercept trackball motion events before they are
1914      * processed by the application.
1915      * If you return true, the application will not itself process the event.
1916      * If you return false, the normal application processing will occur as if
1917      * the IME had not seen the event at all.
1918      */
1919     @Override
onTrackballEvent(MotionEvent event)1920     public boolean onTrackballEvent(MotionEvent event) {
1921         if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
1922         return false;
1923     }
1924 
1925     /**
1926      * Override this to intercept generic motion events before they are
1927      * processed by the application.
1928      * If you return true, the application will not itself process the event.
1929      * If you return false, the normal application processing will occur as if
1930      * the IME had not seen the event at all.
1931      */
1932     @Override
onGenericMotionEvent(MotionEvent event)1933     public boolean onGenericMotionEvent(MotionEvent event) {
1934         if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
1935         return false;
1936     }
1937 
onAppPrivateCommand(String action, Bundle data)1938     public void onAppPrivateCommand(String action, Bundle data) {
1939     }
1940 
1941     /**
1942      * Handle a request by the system to toggle the soft input area.
1943      */
onToggleSoftInput(int showFlags, int hideFlags)1944     private void onToggleSoftInput(int showFlags, int hideFlags) {
1945         if (DEBUG) Log.v(TAG, "toggleSoftInput()");
1946         if (isInputViewShown()) {
1947             requestHideSelf(hideFlags);
1948         } else {
1949             requestShowSelf(showFlags);
1950         }
1951     }
1952 
1953     static final int MOVEMENT_DOWN = -1;
1954     static final int MOVEMENT_UP = -2;
1955 
reportExtractedMovement(int keyCode, int count)1956     void reportExtractedMovement(int keyCode, int count) {
1957         int dx = 0, dy = 0;
1958         switch (keyCode) {
1959             case KeyEvent.KEYCODE_DPAD_LEFT:
1960                 dx = -count;
1961                 break;
1962             case KeyEvent.KEYCODE_DPAD_RIGHT:
1963                 dx = count;
1964                 break;
1965             case KeyEvent.KEYCODE_DPAD_UP:
1966                 dy = -count;
1967                 break;
1968             case KeyEvent.KEYCODE_DPAD_DOWN:
1969                 dy = count;
1970                 break;
1971         }
1972         onExtractedCursorMovement(dx, dy);
1973     }
1974 
doMovementKey(int keyCode, KeyEvent event, int count)1975     boolean doMovementKey(int keyCode, KeyEvent event, int count) {
1976         final ExtractEditText eet = getExtractEditTextIfVisible();
1977         if (eet != null) {
1978             // If we are in fullscreen mode, the cursor will move around
1979             // the extract edit text, but should NOT cause focus to move
1980             // to other fields.
1981             MovementMethod movement = eet.getMovementMethod();
1982             Layout layout = eet.getLayout();
1983             if (movement != null && layout != null) {
1984                 // We want our own movement method to handle the key, so the
1985                 // cursor will properly move in our own word wrapping.
1986                 if (count == MOVEMENT_DOWN) {
1987                     if (movement.onKeyDown(eet,
1988                             (Spannable)eet.getText(), keyCode, event)) {
1989                         reportExtractedMovement(keyCode, 1);
1990                         return true;
1991                     }
1992                 } else if (count == MOVEMENT_UP) {
1993                     if (movement.onKeyUp(eet,
1994                             (Spannable)eet.getText(), keyCode, event)) {
1995                         return true;
1996                     }
1997                 } else {
1998                     if (movement.onKeyOther(eet, (Spannable)eet.getText(), event)) {
1999                         reportExtractedMovement(keyCode, count);
2000                     } else {
2001                         KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
2002                         if (movement.onKeyDown(eet,
2003                                 (Spannable)eet.getText(), keyCode, down)) {
2004                             KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
2005                             movement.onKeyUp(eet,
2006                                     (Spannable)eet.getText(), keyCode, up);
2007                             while (--count > 0) {
2008                                 movement.onKeyDown(eet,
2009                                         (Spannable)eet.getText(), keyCode, down);
2010                                 movement.onKeyUp(eet,
2011                                         (Spannable)eet.getText(), keyCode, up);
2012                             }
2013                             reportExtractedMovement(keyCode, count);
2014                         }
2015                     }
2016                 }
2017             }
2018             // Regardless of whether the movement method handled the key,
2019             // we never allow DPAD navigation to the application.
2020             switch (keyCode) {
2021                 case KeyEvent.KEYCODE_DPAD_LEFT:
2022                 case KeyEvent.KEYCODE_DPAD_RIGHT:
2023                 case KeyEvent.KEYCODE_DPAD_UP:
2024                 case KeyEvent.KEYCODE_DPAD_DOWN:
2025                     return true;
2026             }
2027         }
2028 
2029         return false;
2030     }
2031 
2032     /**
2033      * Send the given key event code (as defined by {@link KeyEvent}) to the
2034      * current input connection is a key down + key up event pair.  The sent
2035      * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
2036      * set, so that the recipient can identify them as coming from a software
2037      * input method, and
2038      * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
2039      * that they don't impact the current touch mode of the UI.
2040      *
2041      * <p>Note that it's discouraged to send such key events in normal operation;
2042      * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
2043      * text fields, or for non-rich input methods. A reasonably capable software
2044      * input method should use the
2045      * {@link android.view.inputmethod.InputConnection#commitText} family of methods
2046      * to send text to an application, rather than sending key events.</p>
2047      *
2048      * @param keyEventCode The raw key code to send, as defined by
2049      * {@link KeyEvent}.
2050      */
sendDownUpKeyEvents(int keyEventCode)2051     public void sendDownUpKeyEvents(int keyEventCode) {
2052         InputConnection ic = getCurrentInputConnection();
2053         if (ic == null) return;
2054         long eventTime = SystemClock.uptimeMillis();
2055         ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
2056                 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
2057                 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2058         ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
2059                 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
2060                 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2061     }
2062 
2063     /**
2064      * Ask the input target to execute its default action via
2065      * {@link InputConnection#performEditorAction
2066      * InputConnection.performEditorAction()}.
2067      *
2068      * @param fromEnterKey If true, this will be executed as if the user had
2069      * pressed an enter key on the keyboard, that is it will <em>not</em>
2070      * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
2071      * EditorInfo.IME_FLAG_NO_ENTER_ACTION}.  If false, the action will be
2072      * sent regardless of how the editor has set that flag.
2073      *
2074      * @return Returns a boolean indicating whether an action has been sent.
2075      * If false, either the editor did not specify a default action or it
2076      * does not want an action from the enter key.  If true, the action was
2077      * sent (or there was no input connection at all).
2078      */
sendDefaultEditorAction(boolean fromEnterKey)2079     public boolean sendDefaultEditorAction(boolean fromEnterKey) {
2080         EditorInfo ei = getCurrentInputEditorInfo();
2081         if (ei != null &&
2082                 (!fromEnterKey || (ei.imeOptions &
2083                         EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
2084                 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
2085                     EditorInfo.IME_ACTION_NONE) {
2086             // If the enter key was pressed, and the editor has a default
2087             // action associated with pressing enter, then send it that
2088             // explicit action instead of the key event.
2089             InputConnection ic = getCurrentInputConnection();
2090             if (ic != null) {
2091                 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
2092             }
2093             return true;
2094         }
2095 
2096         return false;
2097     }
2098 
2099     /**
2100      * Send the given UTF-16 character to the current input connection.  Most
2101      * characters will be delivered simply by calling
2102      * {@link InputConnection#commitText InputConnection.commitText()} with
2103      * the character; some, however, may be handled different.  In particular,
2104      * the enter character ('\n') will either be delivered as an action code
2105      * or a raw key event, as appropriate.  Consider this as a convenience
2106      * method for IMEs that do not have a full implementation of actions; a
2107      * fully complying IME will decide of the right action for each event and
2108      * will likely never call this method except maybe to handle events coming
2109      * from an actual hardware keyboard.
2110      *
2111      * @param charCode The UTF-16 character code to send.
2112      */
sendKeyChar(char charCode)2113     public void sendKeyChar(char charCode) {
2114         switch (charCode) {
2115             case '\n': // Apps may be listening to an enter key to perform an action
2116                 if (!sendDefaultEditorAction(true)) {
2117                     sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2118                 }
2119                 break;
2120             default:
2121                 // Make sure that digits go through any text watcher on the client side.
2122                 if (charCode >= '0' && charCode <= '9') {
2123                     sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2124                 } else {
2125                     InputConnection ic = getCurrentInputConnection();
2126                     if (ic != null) {
2127                         ic.commitText(String.valueOf((char) charCode), 1);
2128                     }
2129                 }
2130                 break;
2131         }
2132     }
2133 
2134     /**
2135      * This is called when the user has moved the cursor in the extracted
2136      * text view, when running in fullsreen mode.  The default implementation
2137      * performs the corresponding selection change on the underlying text
2138      * editor.
2139      */
onExtractedSelectionChanged(int start, int end)2140     public void onExtractedSelectionChanged(int start, int end) {
2141         InputConnection conn = getCurrentInputConnection();
2142         if (conn != null) {
2143             conn.setSelection(start, end);
2144         }
2145     }
2146 
2147     /**
2148      * @hide
2149      */
onExtractedDeleteText(int start, int end)2150     public void onExtractedDeleteText(int start, int end) {
2151         InputConnection conn = getCurrentInputConnection();
2152         if (conn != null) {
2153             conn.setSelection(start, start);
2154             conn.deleteSurroundingText(0, end-start);
2155         }
2156     }
2157 
2158     /**
2159      * @hide
2160      */
onExtractedReplaceText(int start, int end, CharSequence text)2161     public void onExtractedReplaceText(int start, int end, CharSequence text) {
2162         InputConnection conn = getCurrentInputConnection();
2163         if (conn != null) {
2164             conn.setComposingRegion(start, end);
2165             conn.commitText(text, 1);
2166         }
2167     }
2168 
2169     /**
2170      * @hide
2171      */
onExtractedSetSpan(Object span, int start, int end, int flags)2172     public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2173         InputConnection conn = getCurrentInputConnection();
2174         if (conn != null) {
2175             if (!conn.setSelection(start, end)) return;
2176             CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2177             if (text instanceof Spannable) {
2178                 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2179                 conn.setComposingRegion(start, end);
2180                 conn.commitText(text, 1);
2181             }
2182         }
2183     }
2184 
2185     /**
2186      * This is called when the user has clicked on the extracted text view,
2187      * when running in fullscreen mode.  The default implementation hides
2188      * the candidates view when this happens, but only if the extracted text
2189      * editor has a vertical scroll bar because its text doesn't fit.
2190      * Re-implement this to provide whatever behavior you want.
2191      */
onExtractedTextClicked()2192     public void onExtractedTextClicked() {
2193         if (mExtractEditText == null) {
2194             return;
2195         }
2196         if (mExtractEditText.hasVerticalScrollBar()) {
2197             setCandidatesViewShown(false);
2198         }
2199     }
2200 
2201     /**
2202      * This is called when the user has performed a cursor movement in the
2203      * extracted text view, when it is running in fullscreen mode.  The default
2204      * implementation hides the candidates view when a vertical movement
2205      * happens, but only if the extracted text editor has a vertical scroll bar
2206      * because its text doesn't fit.
2207      * Re-implement this to provide whatever behavior you want.
2208      * @param dx The amount of cursor movement in the x dimension.
2209      * @param dy The amount of cursor movement in the y dimension.
2210      */
onExtractedCursorMovement(int dx, int dy)2211     public void onExtractedCursorMovement(int dx, int dy) {
2212         if (mExtractEditText == null || dy == 0) {
2213             return;
2214         }
2215         if (mExtractEditText.hasVerticalScrollBar()) {
2216             setCandidatesViewShown(false);
2217         }
2218     }
2219 
2220     /**
2221      * This is called when the user has selected a context menu item from the
2222      * extracted text view, when running in fullscreen mode.  The default
2223      * implementation sends this action to the current InputConnection's
2224      * {@link InputConnection#performContextMenuAction(int)}, for it
2225      * to be processed in underlying "real" editor.  Re-implement this to
2226      * provide whatever behavior you want.
2227      */
onExtractTextContextMenuItem(int id)2228     public boolean onExtractTextContextMenuItem(int id) {
2229         InputConnection ic = getCurrentInputConnection();
2230         if (ic != null) {
2231             ic.performContextMenuAction(id);
2232         }
2233         return true;
2234     }
2235 
2236     /**
2237      * Return text that can be used as a button label for the given
2238      * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.  Returns null
2239      * if there is no action requested.  Note that there is no guarantee that
2240      * the returned text will be relatively short, so you probably do not
2241      * want to use it as text on a soft keyboard key label.
2242      *
2243      * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
2244      *
2245      * @return Returns a label to use, or null if there is no action.
2246      */
getTextForImeAction(int imeOptions)2247     public CharSequence getTextForImeAction(int imeOptions) {
2248         switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2249             case EditorInfo.IME_ACTION_NONE:
2250                 return null;
2251             case EditorInfo.IME_ACTION_GO:
2252                 return getText(com.android.internal.R.string.ime_action_go);
2253             case EditorInfo.IME_ACTION_SEARCH:
2254                 return getText(com.android.internal.R.string.ime_action_search);
2255             case EditorInfo.IME_ACTION_SEND:
2256                 return getText(com.android.internal.R.string.ime_action_send);
2257             case EditorInfo.IME_ACTION_NEXT:
2258                 return getText(com.android.internal.R.string.ime_action_next);
2259             case EditorInfo.IME_ACTION_DONE:
2260                 return getText(com.android.internal.R.string.ime_action_done);
2261             case EditorInfo.IME_ACTION_PREVIOUS:
2262                 return getText(com.android.internal.R.string.ime_action_previous);
2263             default:
2264                 return getText(com.android.internal.R.string.ime_action_default);
2265         }
2266     }
2267 
2268     /**
2269      * Called when the fullscreen-mode extracting editor info has changed,
2270      * to determine whether the extracting (extract text and candidates) portion
2271      * of the UI should be shown.  The standard implementation hides or shows
2272      * the extract area depending on whether it makes sense for the
2273      * current editor.  In particular, a {@link InputType#TYPE_NULL}
2274      * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
2275      * turn off the extract area since there is no text to be shown.
2276      */
onUpdateExtractingVisibility(EditorInfo ei)2277     public void onUpdateExtractingVisibility(EditorInfo ei) {
2278         if (ei.inputType == InputType.TYPE_NULL ||
2279                 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
2280             // No reason to show extract UI!
2281             setExtractViewShown(false);
2282             return;
2283         }
2284 
2285         setExtractViewShown(true);
2286     }
2287 
2288     /**
2289      * Called when the fullscreen-mode extracting editor info has changed,
2290      * to update the state of its UI such as the action buttons shown.
2291      * You do not need to deal with this if you are using the standard
2292      * full screen extract UI.  If replacing it, you will need to re-implement
2293      * this to put the appropriate action button in your own UI and handle it,
2294      * and perform any other changes.
2295      *
2296      * <p>The standard implementation turns on or off its accessory area
2297      * depending on whether there is an action button, and hides or shows
2298      * the entire extract area depending on whether it makes sense for the
2299      * current editor.  In particular, a {@link InputType#TYPE_NULL} or
2300      * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
2301      * extract area since there is no text to be shown.
2302      */
onUpdateExtractingViews(EditorInfo ei)2303     public void onUpdateExtractingViews(EditorInfo ei) {
2304         if (!isExtractViewShown()) {
2305             return;
2306         }
2307 
2308         if (mExtractAccessories == null) {
2309             return;
2310         }
2311         final boolean hasAction = ei.actionLabel != null || (
2312                 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
2313                 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
2314                 ei.inputType != InputType.TYPE_NULL);
2315         if (hasAction) {
2316             mExtractAccessories.setVisibility(View.VISIBLE);
2317             if (mExtractAction != null) {
2318                 if (ei.actionLabel != null) {
2319                     mExtractAction.setText(ei.actionLabel);
2320                 } else {
2321                     mExtractAction.setText(getTextForImeAction(ei.imeOptions));
2322                 }
2323                 mExtractAction.setOnClickListener(mActionClickListener);
2324             }
2325         } else {
2326             mExtractAccessories.setVisibility(View.GONE);
2327             if (mExtractAction != null) {
2328                 mExtractAction.setOnClickListener(null);
2329             }
2330         }
2331     }
2332 
2333     /**
2334      * This is called when, while currently displayed in extract mode, the
2335      * current input target changes.  The default implementation will
2336      * auto-hide the IME if the new target is not a full editor, since this
2337      * can be a confusing experience for the user.
2338      */
onExtractingInputChanged(EditorInfo ei)2339     public void onExtractingInputChanged(EditorInfo ei) {
2340         if (ei.inputType == InputType.TYPE_NULL) {
2341             requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
2342         }
2343     }
2344 
startExtractingText(boolean inputChanged)2345     void startExtractingText(boolean inputChanged) {
2346         final ExtractEditText eet = mExtractEditText;
2347         if (eet != null && getCurrentInputStarted()
2348                 && isFullscreenMode()) {
2349             mExtractedToken++;
2350             ExtractedTextRequest req = new ExtractedTextRequest();
2351             req.token = mExtractedToken;
2352             req.flags = InputConnection.GET_TEXT_WITH_STYLES;
2353             req.hintMaxLines = 10;
2354             req.hintMaxChars = 10000;
2355             InputConnection ic = getCurrentInputConnection();
2356             mExtractedText = ic == null? null
2357                     : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
2358             if (mExtractedText == null || ic == null) {
2359                 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
2360                         + mExtractedText + ", input connection = " + ic);
2361             }
2362             final EditorInfo ei = getCurrentInputEditorInfo();
2363 
2364             try {
2365                 eet.startInternalChanges();
2366                 onUpdateExtractingVisibility(ei);
2367                 onUpdateExtractingViews(ei);
2368                 int inputType = ei.inputType;
2369                 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
2370                         == EditorInfo.TYPE_CLASS_TEXT) {
2371                     if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
2372                         inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2373                     }
2374                 }
2375                 eet.setInputType(inputType);
2376                 eet.setHint(ei.hintText);
2377                 if (mExtractedText != null) {
2378                     eet.setEnabled(true);
2379                     eet.setExtractedText(mExtractedText);
2380                 } else {
2381                     eet.setEnabled(false);
2382                     eet.setText("");
2383                 }
2384             } finally {
2385                 eet.finishInternalChanges();
2386             }
2387 
2388             if (inputChanged) {
2389                 onExtractingInputChanged(ei);
2390             }
2391         }
2392     }
2393 
2394     // TODO: Handle the subtype change event
2395     /**
2396      * Called when the subtype was changed.
2397      * @param newSubtype the subtype which is being changed to.
2398      */
onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype)2399     protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
2400         if (DEBUG) {
2401             int nameResId = newSubtype.getNameResId();
2402             String mode = newSubtype.getMode();
2403             String output = "changeInputMethodSubtype:"
2404                 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
2405                 + mode + ","
2406                 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
2407             Log.v(TAG, "--- " + output);
2408         }
2409     }
2410 
2411     /**
2412      * @return The recommended height of the input method window.
2413      * An IME author can get the last input method's height as the recommended height
2414      * by calling this in
2415      * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}.
2416      * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME
2417      * switching by using this value as a visible inset height. It's efficient for the smooth
2418      * transition between different IMEs. However, note that this may return 0 (or possibly
2419      * unexpectedly low height). You should thus avoid relying on the return value of this method
2420      * all the time. Please make sure to use a reasonable height for the IME.
2421      */
getInputMethodWindowRecommendedHeight()2422     public int getInputMethodWindowRecommendedHeight() {
2423         return mImm.getInputMethodWindowVisibleHeight();
2424     }
2425 
2426     /**
2427      * Performs a dump of the InputMethodService's internal state.  Override
2428      * to add your own information to the dump.
2429      */
dump(FileDescriptor fd, PrintWriter fout, String[] args)2430     @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2431         final Printer p = new PrintWriterPrinter(fout);
2432         p.println("Input method service state for " + this + ":");
2433         p.println("  mWindowCreated=" + mWindowCreated
2434                 + " mWindowAdded=" + mWindowAdded);
2435         p.println("  mWindowVisible=" + mWindowVisible
2436                 + " mWindowWasVisible=" + mWindowWasVisible
2437                 + " mInShowWindow=" + mInShowWindow);
2438         p.println("  Configuration=" + getResources().getConfiguration());
2439         p.println("  mToken=" + mToken);
2440         p.println("  mInputBinding=" + mInputBinding);
2441         p.println("  mInputConnection=" + mInputConnection);
2442         p.println("  mStartedInputConnection=" + mStartedInputConnection);
2443         p.println("  mInputStarted=" + mInputStarted
2444                 + " mInputViewStarted=" + mInputViewStarted
2445                 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
2446 
2447         if (mInputEditorInfo != null) {
2448             p.println("  mInputEditorInfo:");
2449             mInputEditorInfo.dump(p, "    ");
2450         } else {
2451             p.println("  mInputEditorInfo: null");
2452         }
2453 
2454         p.println("  mShowInputRequested=" + mShowInputRequested
2455                 + " mLastShowInputRequested=" + mLastShowInputRequested
2456                 + " mShowInputForced=" + mShowInputForced
2457                 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
2458         p.println("  mCandidatesVisibility=" + mCandidatesVisibility
2459                 + " mFullscreenApplied=" + mFullscreenApplied
2460                 + " mIsFullscreen=" + mIsFullscreen
2461                 + " mExtractViewHidden=" + mExtractViewHidden);
2462 
2463         if (mExtractedText != null) {
2464             p.println("  mExtractedText:");
2465             p.println("    text=" + mExtractedText.text.length() + " chars"
2466                     + " startOffset=" + mExtractedText.startOffset);
2467             p.println("    selectionStart=" + mExtractedText.selectionStart
2468                     + " selectionEnd=" + mExtractedText.selectionEnd
2469                     + " flags=0x" + Integer.toHexString(mExtractedText.flags));
2470         } else {
2471             p.println("  mExtractedText: null");
2472         }
2473         p.println("  mExtractedToken=" + mExtractedToken);
2474         p.println("  mIsInputViewShown=" + mIsInputViewShown
2475                 + " mStatusIcon=" + mStatusIcon);
2476         p.println("Last computed insets:");
2477         p.println("  contentTopInsets=" + mTmpInsets.contentTopInsets
2478                 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
2479                 + " touchableInsets=" + mTmpInsets.touchableInsets
2480                 + " touchableRegion=" + mTmpInsets.touchableRegion);
2481         p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
2482     }
2483 }
2484