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