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