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