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