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