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