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