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