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