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