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