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