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