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