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