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