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