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