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