InputMethodManager.java revision 67ddf9cbd5d7133c7f443cd3c55841ed1109c3a0
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.view.inputmethod; 18 19import android.content.Context; 20import android.graphics.Rect; 21import android.os.Bundle; 22import android.os.Handler; 23import android.os.IBinder; 24import android.os.Looper; 25import android.os.Message; 26import android.os.RemoteException; 27import android.os.ResultReceiver; 28import android.os.ServiceManager; 29import android.util.Log; 30import android.util.Pair; 31import android.util.PrintWriterPrinter; 32import android.util.Printer; 33import android.view.KeyEvent; 34import android.view.MotionEvent; 35import android.view.View; 36import android.view.ViewRoot; 37import android.view.inputmethod.InputMethodSubtype; 38 39import com.android.internal.os.HandlerCaller; 40import com.android.internal.view.IInputConnectionWrapper; 41import com.android.internal.view.IInputContext; 42import com.android.internal.view.IInputMethodCallback; 43import com.android.internal.view.IInputMethodClient; 44import com.android.internal.view.IInputMethodManager; 45import com.android.internal.view.IInputMethodSession; 46import com.android.internal.view.InputBindResult; 47 48import java.io.FileDescriptor; 49import java.io.PrintWriter; 50import java.util.List; 51import java.util.concurrent.CountDownLatch; 52import java.util.concurrent.TimeUnit; 53 54/** 55 * Central system API to the overall input method framework (IMF) architecture, 56 * which arbitrates interaction between applications and the current input method. 57 * You can retrieve an instance of this interface with 58 * {@link Context#getSystemService(String) Context.getSystemService()}. 59 * 60 * <p>Topics covered here: 61 * <ol> 62 * <li><a href="#ArchitectureOverview">Architecture Overview</a> 63 * </ol> 64 * 65 * <a name="ArchitectureOverview"></a> 66 * <h3>Architecture Overview</h3> 67 * 68 * <p>There are three primary parties involved in the input method 69 * framework (IMF) architecture:</p> 70 * 71 * <ul> 72 * <li> The <strong>input method manager</strong> as expressed by this class 73 * is the central point of the system that manages interaction between all 74 * other parts. It is expressed as the client-side API here which exists 75 * in each application context and communicates with a global system service 76 * that manages the interaction across all processes. 77 * <li> An <strong>input method (IME)</strong> implements a particular 78 * interaction model allowing the user to generate text. The system binds 79 * to the current input method that is use, causing it to be created and run, 80 * and tells it when to hide and show its UI. Only one IME is running at a time. 81 * <li> Multiple <strong>client applications</strong> arbitrate with the input 82 * method manager for input focus and control over the state of the IME. Only 83 * one such client is ever active (working with the IME) at a time. 84 * </ul> 85 * 86 * 87 * <a name="Applications"></a> 88 * <h3>Applications</h3> 89 * 90 * <p>In most cases, applications that are using the standard 91 * {@link android.widget.TextView} or its subclasses will have little they need 92 * to do to work well with soft input methods. The main things you need to 93 * be aware of are:</p> 94 * 95 * <ul> 96 * <li> Properly set the {@link android.R.attr#inputType} if your editable 97 * text views, so that the input method will have enough context to help the 98 * user in entering text into them. 99 * <li> Deal well with losing screen space when the input method is 100 * displayed. Ideally an application should handle its window being resized 101 * smaller, but it can rely on the system performing panning of the window 102 * if needed. You should set the {@link android.R.attr#windowSoftInputMode} 103 * attribute on your activity or the corresponding values on windows you 104 * create to help the system determine whether to pan or resize (it will 105 * try to determine this automatically but may get it wrong). 106 * <li> You can also control the preferred soft input state (open, closed, etc) 107 * for your window using the same {@link android.R.attr#windowSoftInputMode} 108 * attribute. 109 * </ul> 110 * 111 * <p>More finer-grained control is available through the APIs here to directly 112 * interact with the IMF and its IME -- either showing or hiding the input 113 * area, letting the user pick an input method, etc.</p> 114 * 115 * <p>For the rare people amongst us writing their own text editors, you 116 * will need to implement {@link android.view.View#onCreateInputConnection} 117 * to return a new instance of your own {@link InputConnection} interface 118 * allowing the IME to interact with your editor.</p> 119 * 120 * 121 * <a name="InputMethods"></a> 122 * <h3>Input Methods</h3> 123 * 124 * <p>An input method (IME) is implemented 125 * as a {@link android.app.Service}, typically deriving from 126 * {@link android.inputmethodservice.InputMethodService}. It must provide 127 * the core {@link InputMethod} interface, though this is normally handled by 128 * {@link android.inputmethodservice.InputMethodService} and implementors will 129 * only need to deal with the higher-level API there.</p> 130 * 131 * See the {@link android.inputmethodservice.InputMethodService} class for 132 * more information on implementing IMEs. 133 * 134 * 135 * <a name="Security"></a> 136 * <h3>Security</h3> 137 * 138 * <p>There are a lot of security issues associated with input methods, 139 * since they essentially have freedom to completely drive the UI and monitor 140 * everything the user enters. The Android input method framework also allows 141 * arbitrary third party IMEs, so care must be taken to restrict their 142 * selection and interactions.</p> 143 * 144 * <p>Here are some key points about the security architecture behind the 145 * IMF:</p> 146 * 147 * <ul> 148 * <li> <p>Only the system is allowed to directly access an IME's 149 * {@link InputMethod} interface, via the 150 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission. This is 151 * enforced in the system by not binding to an input method service that does 152 * not require this permission, so the system can guarantee no other untrusted 153 * clients are accessing the current input method outside of its control.</p> 154 * 155 * <li> <p>There may be many client processes of the IMF, but only one may 156 * be active at a time. The inactive clients can not interact with key 157 * parts of the IMF through the mechanisms described below.</p> 158 * 159 * <li> <p>Clients of an input method are only given access to its 160 * {@link InputMethodSession} interface. One instance of this interface is 161 * created for each client, and only calls from the session associated with 162 * the active client will be processed by the current IME. This is enforced 163 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal 164 * IMEs, but must be explicitly handled by an IME that is customizing the 165 * raw {@link InputMethodSession} implementation.</p> 166 * 167 * <li> <p>Only the active client's {@link InputConnection} will accept 168 * operations. The IMF tells each client process whether it is active, and 169 * the framework enforces that in inactive processes calls on to the current 170 * InputConnection will be ignored. This ensures that the current IME can 171 * only deliver events and text edits to the UI that the user sees as 172 * being in focus.</p> 173 * 174 * <li> <p>An IME can never interact with an {@link InputConnection} while 175 * the screen is off. This is enforced by making all clients inactive while 176 * the screen is off, and prevents bad IMEs from driving the UI when the user 177 * can not be aware of its behavior.</p> 178 * 179 * <li> <p>A client application can ask that the system let the user pick a 180 * new IME, but can not programmatically switch to one itself. This avoids 181 * malicious applications from switching the user to their own IME, which 182 * remains running when the user navigates away to another application. An 183 * IME, on the other hand, <em>is</em> allowed to programmatically switch 184 * the system to another IME, since it already has full control of user 185 * input.</p> 186 * 187 * <li> <p>The user must explicitly enable a new IME in settings before 188 * they can switch to it, to confirm with the system that they know about it 189 * and want to make it available for use.</p> 190 * </ul> 191 */ 192public final class InputMethodManager { 193 static final boolean DEBUG = false; 194 static final String TAG = "InputMethodManager"; 195 196 static final Object mInstanceSync = new Object(); 197 static InputMethodManager mInstance; 198 199 final IInputMethodManager mService; 200 final Looper mMainLooper; 201 202 // For scheduling work on the main thread. This also serves as our 203 // global lock. 204 final H mH; 205 206 // Our generic input connection if the current target does not have its own. 207 final IInputContext mIInputContext; 208 209 /** 210 * True if this input method client is active, initially false. 211 */ 212 boolean mActive = false; 213 214 /** 215 * Set whenever this client becomes inactive, to know we need to reset 216 * state with the IME then next time we receive focus. 217 */ 218 boolean mHasBeenInactive = true; 219 220 /** 221 * As reported by IME through InputConnection. 222 */ 223 boolean mFullscreenMode; 224 225 // ----------------------------------------------------------- 226 227 /** 228 * This is the root view of the overall window that currently has input 229 * method focus. 230 */ 231 View mCurRootView; 232 /** 233 * This is the view that should currently be served by an input method, 234 * regardless of the state of setting that up. 235 */ 236 View mServedView; 237 /** 238 * This is then next view that will be served by the input method, when 239 * we get around to updating things. 240 */ 241 View mNextServedView; 242 /** 243 * True if we should restart input in the next served view, even if the 244 * view hasn't actually changed from the current serve view. 245 */ 246 boolean mNextServedNeedsStart; 247 /** 248 * This is set when we are in the process of connecting, to determine 249 * when we have actually finished. 250 */ 251 boolean mServedConnecting; 252 /** 253 * This is non-null when we have connected the served view; it holds 254 * the attributes that were last retrieved from the served view and given 255 * to the input connection. 256 */ 257 EditorInfo mCurrentTextBoxAttribute; 258 /** 259 * The InputConnection that was last retrieved from the served view. 260 */ 261 InputConnection mServedInputConnection; 262 /** 263 * The completions that were last provided by the served view. 264 */ 265 CompletionInfo[] mCompletions; 266 267 // Cursor position on the screen. 268 Rect mTmpCursorRect = new Rect(); 269 Rect mCursorRect = new Rect(); 270 int mCursorSelStart; 271 int mCursorSelEnd; 272 int mCursorCandStart; 273 int mCursorCandEnd; 274 275 // ----------------------------------------------------------- 276 277 /** 278 * Sequence number of this binding, as returned by the server. 279 */ 280 int mBindSequence = -1; 281 /** 282 * ID of the method we are bound to. 283 */ 284 String mCurId; 285 /** 286 * The actual instance of the method to make calls on it. 287 */ 288 IInputMethodSession mCurMethod; 289 290 // ----------------------------------------------------------- 291 292 static final int MSG_DUMP = 1; 293 static final int MSG_BIND = 2; 294 static final int MSG_UNBIND = 3; 295 static final int MSG_SET_ACTIVE = 4; 296 297 class H extends Handler { 298 H(Looper looper) { 299 super(looper); 300 } 301 302 @Override 303 public void handleMessage(Message msg) { 304 switch (msg.what) { 305 case MSG_DUMP: { 306 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; 307 try { 308 doDump((FileDescriptor)args.arg1, 309 (PrintWriter)args.arg2, (String[])args.arg3); 310 } catch (RuntimeException e) { 311 ((PrintWriter)args.arg2).println("Exception: " + e); 312 } 313 synchronized (args.arg4) { 314 ((CountDownLatch)args.arg4).countDown(); 315 } 316 return; 317 } 318 case MSG_BIND: { 319 final InputBindResult res = (InputBindResult)msg.obj; 320 synchronized (mH) { 321 if (mBindSequence < 0 || mBindSequence != res.sequence) { 322 Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence 323 + ", given seq=" + res.sequence); 324 return; 325 } 326 327 mCurMethod = res.method; 328 mCurId = res.id; 329 mBindSequence = res.sequence; 330 } 331 startInputInner(); 332 return; 333 } 334 case MSG_UNBIND: { 335 final int sequence = msg.arg1; 336 synchronized (mH) { 337 if (mBindSequence == sequence) { 338 if (false) { 339 // XXX the server has already unbound! 340 if (mCurMethod != null && mCurrentTextBoxAttribute != null) { 341 try { 342 mCurMethod.finishInput(); 343 } catch (RemoteException e) { 344 Log.w(TAG, "IME died: " + mCurId, e); 345 } 346 } 347 } 348 clearBindingLocked(); 349 350 // If we were actively using the last input method, then 351 // we would like to re-connect to the next input method. 352 if (mServedView != null && mServedView.isFocused()) { 353 mServedConnecting = true; 354 } 355 } 356 startInputInner(); 357 } 358 return; 359 } 360 case MSG_SET_ACTIVE: { 361 final boolean active = msg.arg1 != 0; 362 synchronized (mH) { 363 mActive = active; 364 mFullscreenMode = false; 365 if (!active) { 366 // Some other client has starting using the IME, so note 367 // that this happened and make sure our own editor's 368 // state is reset. 369 mHasBeenInactive = true; 370 try { 371 // Note that finishComposingText() is allowed to run 372 // even when we are not active. 373 mIInputContext.finishComposingText(); 374 } catch (RemoteException e) { 375 } 376 } 377 } 378 return; 379 } 380 } 381 } 382 } 383 384 class ControlledInputConnectionWrapper extends IInputConnectionWrapper { 385 public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) { 386 super(mainLooper, conn); 387 } 388 389 public boolean isActive() { 390 return mActive; 391 } 392 } 393 394 final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { 395 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 396 // No need to check for dump permission, since we only give this 397 // interface to the system. 398 399 CountDownLatch latch = new CountDownLatch(1); 400 HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs(); 401 sargs.arg1 = fd; 402 sargs.arg2 = fout; 403 sargs.arg3 = args; 404 sargs.arg4 = latch; 405 mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs)); 406 try { 407 if (!latch.await(5, TimeUnit.SECONDS)) { 408 fout.println("Timeout waiting for dump"); 409 } 410 } catch (InterruptedException e) { 411 fout.println("Interrupted waiting for dump"); 412 } 413 } 414 415 public void setUsingInputMethod(boolean state) { 416 } 417 418 public void onBindMethod(InputBindResult res) { 419 mH.sendMessage(mH.obtainMessage(MSG_BIND, res)); 420 } 421 422 public void onUnbindMethod(int sequence) { 423 mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0)); 424 } 425 426 public void setActive(boolean active) { 427 mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0)); 428 } 429 }; 430 431 final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); 432 433 InputMethodManager(IInputMethodManager service, Looper looper) { 434 mService = service; 435 mMainLooper = looper; 436 mH = new H(looper); 437 mIInputContext = new ControlledInputConnectionWrapper(looper, 438 mDummyInputConnection); 439 440 if (mInstance == null) { 441 mInstance = this; 442 } 443 } 444 445 /** 446 * Retrieve the global InputMethodManager instance, creating it if it 447 * doesn't already exist. 448 * @hide 449 */ 450 static public InputMethodManager getInstance(Context context) { 451 return getInstance(context.getMainLooper()); 452 } 453 454 /** 455 * Internally, the input method manager can't be context-dependent, so 456 * we have this here for the places that need it. 457 * @hide 458 */ 459 static public InputMethodManager getInstance(Looper mainLooper) { 460 synchronized (mInstanceSync) { 461 if (mInstance != null) { 462 return mInstance; 463 } 464 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); 465 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b); 466 mInstance = new InputMethodManager(service, mainLooper); 467 } 468 return mInstance; 469 } 470 471 /** 472 * Private optimization: retrieve the global InputMethodManager instance, 473 * if it exists. 474 * @hide 475 */ 476 static public InputMethodManager peekInstance() { 477 return mInstance; 478 } 479 480 /** @hide */ 481 public IInputMethodClient getClient() { 482 return mClient; 483 } 484 485 /** @hide */ 486 public IInputContext getInputContext() { 487 return mIInputContext; 488 } 489 490 public List<InputMethodInfo> getInputMethodList() { 491 try { 492 return mService.getInputMethodList(); 493 } catch (RemoteException e) { 494 throw new RuntimeException(e); 495 } 496 } 497 498 public List<InputMethodInfo> getEnabledInputMethodList() { 499 try { 500 return mService.getEnabledInputMethodList(); 501 } catch (RemoteException e) { 502 throw new RuntimeException(e); 503 } 504 } 505 506 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi) { 507 try { 508 return mService.getEnabledInputMethodSubtypeList(imi); 509 } catch (RemoteException e) { 510 throw new RuntimeException(e); 511 } 512 } 513 514 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) { 515 try { 516 mService.updateStatusIcon(imeToken, packageName, iconId); 517 } catch (RemoteException e) { 518 throw new RuntimeException(e); 519 } 520 } 521 522 public void hideStatusIcon(IBinder imeToken) { 523 try { 524 mService.updateStatusIcon(imeToken, null, 0); 525 } catch (RemoteException e) { 526 throw new RuntimeException(e); 527 } 528 } 529 530 /** @hide */ 531 public void setIMEButtonVisible(IBinder imeToken, boolean visible) { 532 try { 533 mService.setIMEButtonVisible(imeToken, visible); 534 } catch (RemoteException e) { 535 throw new RuntimeException(e); 536 } 537 } 538 539 /** @hide */ 540 public void setFullscreenMode(boolean fullScreen) { 541 mFullscreenMode = fullScreen; 542 } 543 544 /** 545 * Allows you to discover whether the attached input method is running 546 * in fullscreen mode. Return true if it is fullscreen, entirely covering 547 * your UI, else returns false. 548 */ 549 public boolean isFullscreenMode() { 550 return mFullscreenMode; 551 } 552 553 /** 554 * Return true if the given view is the currently active view for the 555 * input method. 556 */ 557 public boolean isActive(View view) { 558 checkFocus(); 559 synchronized (mH) { 560 return (mServedView == view 561 || (mServedView != null 562 && mServedView.checkInputConnectionProxy(view))) 563 && mCurrentTextBoxAttribute != null; 564 } 565 } 566 567 /** 568 * Return true if any view is currently active in the input method. 569 */ 570 public boolean isActive() { 571 checkFocus(); 572 synchronized (mH) { 573 return mServedView != null && mCurrentTextBoxAttribute != null; 574 } 575 } 576 577 /** 578 * Return true if the currently served view is accepting full text edits. 579 * If false, it has no input connection, so can only handle raw key events. 580 */ 581 public boolean isAcceptingText() { 582 checkFocus(); 583 return mServedInputConnection != null; 584 } 585 586 /** 587 * Reset all of the state associated with being bound to an input method. 588 */ 589 void clearBindingLocked() { 590 clearConnectionLocked(); 591 mBindSequence = -1; 592 mCurId = null; 593 mCurMethod = null; 594 } 595 596 /** 597 * Reset all of the state associated with a served view being connected 598 * to an input method 599 */ 600 void clearConnectionLocked() { 601 mCurrentTextBoxAttribute = null; 602 mServedInputConnection = null; 603 } 604 605 /** 606 * Disconnect any existing input connection, clearing the served view. 607 */ 608 void finishInputLocked() { 609 mNextServedView = null; 610 if (mServedView != null) { 611 if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView); 612 613 if (mCurrentTextBoxAttribute != null) { 614 try { 615 mService.finishInput(mClient); 616 } catch (RemoteException e) { 617 } 618 } 619 620 if (mServedInputConnection != null) { 621 // We need to tell the previously served view that it is no 622 // longer the input target, so it can reset its state. Schedule 623 // this call on its window's Handler so it will be on the correct 624 // thread and outside of our lock. 625 Handler vh = mServedView.getHandler(); 626 if (vh != null) { 627 // This will result in a call to reportFinishInputConnection() 628 // below. 629 vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION, 630 mServedInputConnection)); 631 } 632 } 633 634 mServedView = null; 635 mCompletions = null; 636 mServedConnecting = false; 637 clearConnectionLocked(); 638 } 639 } 640 641 /** 642 * Called from the FINISH_INPUT_CONNECTION message above. 643 * @hide 644 */ 645 public void reportFinishInputConnection(InputConnection ic) { 646 if (mServedInputConnection != ic) { 647 ic.finishComposingText(); 648 } 649 } 650 651 public void displayCompletions(View view, CompletionInfo[] completions) { 652 checkFocus(); 653 synchronized (mH) { 654 if (mServedView != view && (mServedView == null 655 || !mServedView.checkInputConnectionProxy(view))) { 656 return; 657 } 658 659 mCompletions = completions; 660 if (mCurMethod != null) { 661 try { 662 mCurMethod.displayCompletions(mCompletions); 663 } catch (RemoteException e) { 664 } 665 } 666 } 667 } 668 669 public void updateExtractedText(View view, int token, ExtractedText text) { 670 checkFocus(); 671 synchronized (mH) { 672 if (mServedView != view && (mServedView == null 673 || !mServedView.checkInputConnectionProxy(view))) { 674 return; 675 } 676 677 if (mCurMethod != null) { 678 try { 679 mCurMethod.updateExtractedText(token, text); 680 } catch (RemoteException e) { 681 } 682 } 683 } 684 } 685 686 /** 687 * Flag for {@link #showSoftInput} to indicate that this is an implicit 688 * request to show the input window, not as the result of a direct request 689 * by the user. The window may not be shown in this case. 690 */ 691 public static final int SHOW_IMPLICIT = 0x0001; 692 693 /** 694 * Flag for {@link #showSoftInput} to indicate that the user has forced 695 * the input method open (such as by long-pressing menu) so it should 696 * not be closed until they explicitly do so. 697 */ 698 public static final int SHOW_FORCED = 0x0002; 699 700 /** 701 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without 702 * a result receiver: explicitly request that the current input method's 703 * soft input area be shown to the user, if needed. 704 * 705 * @param view The currently focused view, which would like to receive 706 * soft keyboard input. 707 * @param flags Provides additional operating flags. Currently may be 708 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 709 */ 710 public boolean showSoftInput(View view, int flags) { 711 return showSoftInput(view, flags, null); 712 } 713 714 /** 715 * Flag for the {@link ResultReceiver} result code from 716 * {@link #showSoftInput(View, int, ResultReceiver)} and 717 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 718 * state of the soft input window was unchanged and remains shown. 719 */ 720 public static final int RESULT_UNCHANGED_SHOWN = 0; 721 722 /** 723 * Flag for the {@link ResultReceiver} result code from 724 * {@link #showSoftInput(View, int, ResultReceiver)} and 725 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 726 * state of the soft input window was unchanged and remains hidden. 727 */ 728 public static final int RESULT_UNCHANGED_HIDDEN = 1; 729 730 /** 731 * Flag for the {@link ResultReceiver} result code from 732 * {@link #showSoftInput(View, int, ResultReceiver)} and 733 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 734 * state of the soft input window changed from hidden to shown. 735 */ 736 public static final int RESULT_SHOWN = 2; 737 738 /** 739 * Flag for the {@link ResultReceiver} result code from 740 * {@link #showSoftInput(View, int, ResultReceiver)} and 741 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 742 * state of the soft input window changed from shown to hidden. 743 */ 744 public static final int RESULT_HIDDEN = 3; 745 746 /** 747 * Explicitly request that the current input method's soft input area be 748 * shown to the user, if needed. Call this if the user interacts with 749 * your view in such a way that they have expressed they would like to 750 * start performing input into it. 751 * 752 * @param view The currently focused view, which would like to receive 753 * soft keyboard input. 754 * @param flags Provides additional operating flags. Currently may be 755 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 756 * @param resultReceiver If non-null, this will be called by the IME when 757 * it has processed your request to tell you what it has done. The result 758 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 759 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 760 * {@link #RESULT_HIDDEN}. 761 */ 762 public boolean showSoftInput(View view, int flags, 763 ResultReceiver resultReceiver) { 764 checkFocus(); 765 synchronized (mH) { 766 if (mServedView != view && (mServedView == null 767 || !mServedView.checkInputConnectionProxy(view))) { 768 return false; 769 } 770 771 try { 772 return mService.showSoftInput(mClient, flags, resultReceiver); 773 } catch (RemoteException e) { 774 } 775 776 return false; 777 } 778 } 779 780 /** @hide */ 781 public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) { 782 try { 783 mService.showSoftInput(mClient, flags, resultReceiver); 784 } catch (RemoteException e) { 785 } 786 } 787 788 /** 789 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 790 * input window should only be hidden if it was not explicitly shown 791 * by the user. 792 */ 793 public static final int HIDE_IMPLICIT_ONLY = 0x0001; 794 795 /** 796 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 797 * input window should normally be hidden, unless it was originally 798 * shown with {@link #SHOW_FORCED}. 799 */ 800 public static final int HIDE_NOT_ALWAYS = 0x0002; 801 802 /** 803 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver) 804 * without a result: request to hide the soft input window from the 805 * context of the window that is currently accepting input. 806 * 807 * @param windowToken The token of the window that is making the request, 808 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 809 * @param flags Provides additional operating flags. Currently may be 810 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 811 */ 812 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) { 813 return hideSoftInputFromWindow(windowToken, flags, null); 814 } 815 816 /** 817 * Request to hide the soft input window from the context of the window 818 * that is currently accepting input. This should be called as a result 819 * of the user doing some actually than fairly explicitly requests to 820 * have the input window hidden. 821 * 822 * @param windowToken The token of the window that is making the request, 823 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 824 * @param flags Provides additional operating flags. Currently may be 825 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 826 * @param resultReceiver If non-null, this will be called by the IME when 827 * it has processed your request to tell you what it has done. The result 828 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 829 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 830 * {@link #RESULT_HIDDEN}. 831 */ 832 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, 833 ResultReceiver resultReceiver) { 834 checkFocus(); 835 synchronized (mH) { 836 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 837 return false; 838 } 839 840 try { 841 return mService.hideSoftInput(mClient, flags, resultReceiver); 842 } catch (RemoteException e) { 843 } 844 return false; 845 } 846 } 847 848 849 /** 850 * This method toggles the input method window display. 851 * If the input window is already displayed, it gets hidden. 852 * If not the input window will be displayed. 853 * @param windowToken The token of the window that is making the request, 854 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 855 * @param showFlags Provides additional operating flags. May be 856 * 0 or have the {@link #SHOW_IMPLICIT}, 857 * {@link #SHOW_FORCED} bit set. 858 * @param hideFlags Provides additional operating flags. May be 859 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 860 * {@link #HIDE_NOT_ALWAYS} bit set. 861 **/ 862 public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) { 863 synchronized (mH) { 864 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 865 return; 866 } 867 if (mCurMethod != null) { 868 try { 869 mCurMethod.toggleSoftInput(showFlags, hideFlags); 870 } catch (RemoteException e) { 871 } 872 } 873 } 874 } 875 876 /* 877 * This method toggles the input method window display. 878 * If the input window is already displayed, it gets hidden. 879 * If not the input window will be displayed. 880 * @param showFlags Provides additional operating flags. May be 881 * 0 or have the {@link #SHOW_IMPLICIT}, 882 * {@link #SHOW_FORCED} bit set. 883 * @param hideFlags Provides additional operating flags. May be 884 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 885 * {@link #HIDE_NOT_ALWAYS} bit set. 886 * @hide 887 */ 888 public void toggleSoftInput(int showFlags, int hideFlags) { 889 if (mCurMethod != null) { 890 try { 891 mCurMethod.toggleSoftInput(showFlags, hideFlags); 892 } catch (RemoteException e) { 893 } 894 } 895 } 896 897 /** 898 * If the input method is currently connected to the given view, 899 * restart it with its new contents. You should call this when the text 900 * within your view changes outside of the normal input method or key 901 * input flow, such as when an application calls TextView.setText(). 902 * 903 * @param view The view whose text has changed. 904 */ 905 public void restartInput(View view) { 906 checkFocus(); 907 synchronized (mH) { 908 if (mServedView != view && (mServedView == null 909 || !mServedView.checkInputConnectionProxy(view))) { 910 return; 911 } 912 913 mServedConnecting = true; 914 } 915 916 startInputInner(); 917 } 918 919 void startInputInner() { 920 final View view; 921 synchronized (mH) { 922 view = mServedView; 923 924 // Make sure we have a window token for the served view. 925 if (DEBUG) Log.v(TAG, "Starting input: view=" + view); 926 if (view == null) { 927 if (DEBUG) Log.v(TAG, "ABORT input: no served view!"); 928 return; 929 } 930 } 931 932 // Now we need to get an input connection from the served view. 933 // This is complicated in a couple ways: we can't be holding our lock 934 // when calling out to the view, and we need to make sure we call into 935 // the view on the same thread that is driving its view hierarchy. 936 Handler vh = view.getHandler(); 937 if (vh == null) { 938 // If the view doesn't have a handler, something has changed out 939 // from under us, so just bail. 940 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!"); 941 return; 942 } 943 if (vh.getLooper() != Looper.myLooper()) { 944 // The view is running on a different thread than our own, so 945 // we need to reschedule our work for over there. 946 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); 947 vh.post(new Runnable() { 948 public void run() { 949 startInputInner(); 950 } 951 }); 952 return; 953 } 954 955 // Okay we are now ready to call into the served view and have it 956 // do its stuff. 957 // Life is good: let's hook everything up! 958 EditorInfo tba = new EditorInfo(); 959 tba.packageName = view.getContext().getPackageName(); 960 tba.fieldId = view.getId(); 961 InputConnection ic = view.onCreateInputConnection(tba); 962 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic); 963 964 synchronized (mH) { 965 // Now that we are locked again, validate that our state hasn't 966 // changed. 967 if (mServedView != view || !mServedConnecting) { 968 // Something else happened, so abort. 969 if (DEBUG) Log.v(TAG, 970 "Starting input: finished by someone else (view=" 971 + mServedView + " conn=" + mServedConnecting + ")"); 972 return; 973 } 974 975 // If we already have a text box, then this view is already 976 // connected so we want to restart it. 977 final boolean initial = mCurrentTextBoxAttribute == null; 978 979 // Hook 'em up and let 'er rip. 980 mCurrentTextBoxAttribute = tba; 981 mServedConnecting = false; 982 mServedInputConnection = ic; 983 IInputContext servedContext; 984 if (ic != null) { 985 mCursorSelStart = tba.initialSelStart; 986 mCursorSelEnd = tba.initialSelEnd; 987 mCursorCandStart = -1; 988 mCursorCandEnd = -1; 989 mCursorRect.setEmpty(); 990 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic); 991 } else { 992 servedContext = null; 993 } 994 995 try { 996 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" 997 + ic + " tba=" + tba + " initial=" + initial); 998 InputBindResult res = mService.startInput(mClient, 999 servedContext, tba, initial, mCurMethod == null); 1000 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); 1001 if (res != null) { 1002 if (res.id != null) { 1003 mBindSequence = res.sequence; 1004 mCurMethod = res.method; 1005 } else { 1006 // This means there is no input method available. 1007 if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); 1008 return; 1009 } 1010 } 1011 if (mCurMethod != null && mCompletions != null) { 1012 try { 1013 mCurMethod.displayCompletions(mCompletions); 1014 } catch (RemoteException e) { 1015 } 1016 } 1017 } catch (RemoteException e) { 1018 Log.w(TAG, "IME died: " + mCurId, e); 1019 } 1020 } 1021 } 1022 1023 /** 1024 * When the focused window is dismissed, this method is called to finish the 1025 * input method started before. 1026 * @hide 1027 */ 1028 public void windowDismissed(IBinder appWindowToken) { 1029 checkFocus(); 1030 synchronized (mH) { 1031 if (mServedView != null && 1032 mServedView.getWindowToken() == appWindowToken) { 1033 finishInputLocked(); 1034 } 1035 } 1036 } 1037 1038 /** 1039 * Call this when a view receives focus. 1040 * @hide 1041 */ 1042 public void focusIn(View view) { 1043 synchronized (mH) { 1044 focusInLocked(view); 1045 } 1046 } 1047 1048 void focusInLocked(View view) { 1049 if (DEBUG) Log.v(TAG, "focusIn: " + view); 1050 1051 if (mCurRootView != view.getRootView()) { 1052 // This is a request from a window that isn't in the window with 1053 // IME focus, so ignore it. 1054 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring"); 1055 return; 1056 } 1057 1058 mNextServedView = view; 1059 scheduleCheckFocusLocked(view); 1060 } 1061 1062 /** 1063 * Call this when a view loses focus. 1064 * @hide 1065 */ 1066 public void focusOut(View view) { 1067 synchronized (mH) { 1068 if (DEBUG) Log.v(TAG, "focusOut: " + view 1069 + " mServedView=" + mServedView 1070 + " winFocus=" + view.hasWindowFocus()); 1071 if (mServedView != view) { 1072 // The following code would auto-hide the IME if we end up 1073 // with no more views with focus. This can happen, however, 1074 // whenever we go into touch mode, so it ends up hiding 1075 // at times when we don't really want it to. For now it 1076 // seems better to just turn it all off. 1077 if (false && view.hasWindowFocus()) { 1078 mNextServedView = null; 1079 scheduleCheckFocusLocked(view); 1080 } 1081 } 1082 } 1083 } 1084 1085 void scheduleCheckFocusLocked(View view) { 1086 Handler vh = view.getHandler(); 1087 if (vh != null && !vh.hasMessages(ViewRoot.CHECK_FOCUS)) { 1088 // This will result in a call to checkFocus() below. 1089 vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS)); 1090 } 1091 } 1092 1093 /** 1094 * @hide 1095 */ 1096 public void checkFocus() { 1097 // This is called a lot, so short-circuit before locking. 1098 if (mServedView == mNextServedView && !mNextServedNeedsStart) { 1099 return; 1100 } 1101 1102 InputConnection ic = null; 1103 synchronized (mH) { 1104 if (mServedView == mNextServedView && !mNextServedNeedsStart) { 1105 return; 1106 } 1107 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView 1108 + " next=" + mNextServedView 1109 + " restart=" + mNextServedNeedsStart); 1110 1111 mNextServedNeedsStart = false; 1112 if (mNextServedView == null) { 1113 finishInputLocked(); 1114 // In this case, we used to have a focused view on the window, 1115 // but no longer do. We should make sure the input method is 1116 // no longer shown, since it serves no purpose. 1117 closeCurrentInput(); 1118 return; 1119 } 1120 1121 ic = mServedInputConnection; 1122 1123 mServedView = mNextServedView; 1124 mCurrentTextBoxAttribute = null; 1125 mCompletions = null; 1126 mServedConnecting = true; 1127 } 1128 1129 if (ic != null) { 1130 ic.finishComposingText(); 1131 } 1132 1133 startInputInner(); 1134 } 1135 1136 void closeCurrentInput() { 1137 try { 1138 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null); 1139 } catch (RemoteException e) { 1140 } 1141 } 1142 1143 /** 1144 * Called by ViewRoot when its window gets input focus. 1145 * @hide 1146 */ 1147 public void onWindowFocus(View rootView, View focusedView, int softInputMode, 1148 boolean first, int windowFlags) { 1149 synchronized (mH) { 1150 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView 1151 + " softInputMode=" + softInputMode 1152 + " first=" + first + " flags=#" 1153 + Integer.toHexString(windowFlags)); 1154 if (mHasBeenInactive) { 1155 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh"); 1156 mHasBeenInactive = false; 1157 mNextServedNeedsStart = true; 1158 } 1159 focusInLocked(focusedView != null ? focusedView : rootView); 1160 } 1161 1162 checkFocus(); 1163 1164 synchronized (mH) { 1165 try { 1166 final boolean isTextEditor = focusedView != null && 1167 focusedView.onCheckIsTextEditor(); 1168 mService.windowGainedFocus(mClient, rootView.getWindowToken(), 1169 focusedView != null, isTextEditor, softInputMode, first, 1170 windowFlags); 1171 } catch (RemoteException e) { 1172 } 1173 } 1174 } 1175 1176 /** @hide */ 1177 public void startGettingWindowFocus(View rootView) { 1178 synchronized (mH) { 1179 mCurRootView = rootView; 1180 } 1181 } 1182 1183 /** 1184 * Report the current selection range. 1185 */ 1186 public void updateSelection(View view, int selStart, int selEnd, 1187 int candidatesStart, int candidatesEnd) { 1188 checkFocus(); 1189 synchronized (mH) { 1190 if ((mServedView != view && (mServedView == null 1191 || !mServedView.checkInputConnectionProxy(view))) 1192 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1193 return; 1194 } 1195 1196 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd 1197 || mCursorCandStart != candidatesStart 1198 || mCursorCandEnd != candidatesEnd) { 1199 if (DEBUG) Log.d(TAG, "updateSelection"); 1200 1201 try { 1202 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod); 1203 mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd, 1204 selStart, selEnd, candidatesStart, candidatesEnd); 1205 mCursorSelStart = selStart; 1206 mCursorSelEnd = selEnd; 1207 mCursorCandStart = candidatesStart; 1208 mCursorCandEnd = candidatesEnd; 1209 } catch (RemoteException e) { 1210 Log.w(TAG, "IME died: " + mCurId, e); 1211 } 1212 } 1213 } 1214 } 1215 1216 /** 1217 * Returns true if the current input method wants to watch the location 1218 * of the input editor's cursor in its window. 1219 */ 1220 public boolean isWatchingCursor(View view) { 1221 return false; 1222 } 1223 1224 /** 1225 * Report the current cursor location in its window. 1226 */ 1227 public void updateCursor(View view, int left, int top, int right, int bottom) { 1228 checkFocus(); 1229 synchronized (mH) { 1230 if ((mServedView != view && (mServedView == null 1231 || !mServedView.checkInputConnectionProxy(view))) 1232 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1233 return; 1234 } 1235 1236 mTmpCursorRect.set(left, top, right, bottom); 1237 if (!mCursorRect.equals(mTmpCursorRect)) { 1238 if (DEBUG) Log.d(TAG, "updateCursor"); 1239 1240 try { 1241 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); 1242 mCurMethod.updateCursor(mTmpCursorRect); 1243 mCursorRect.set(mTmpCursorRect); 1244 } catch (RemoteException e) { 1245 Log.w(TAG, "IME died: " + mCurId, e); 1246 } 1247 } 1248 } 1249 } 1250 1251 /** 1252 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) 1253 * InputMethodSession.appPrivateCommand()} on the current Input Method. 1254 * @param view Optional View that is sending the command, or null if 1255 * you want to send the command regardless of the view that is attached 1256 * to the input method. 1257 * @param action Name of the command to be performed. This <em>must</em> 1258 * be a scoped name, i.e. prefixed with a package name you own, so that 1259 * different developers will not create conflicting commands. 1260 * @param data Any data to include with the command. 1261 */ 1262 public void sendAppPrivateCommand(View view, String action, Bundle data) { 1263 checkFocus(); 1264 synchronized (mH) { 1265 if ((mServedView != view && (mServedView == null 1266 || !mServedView.checkInputConnectionProxy(view))) 1267 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1268 return; 1269 } 1270 try { 1271 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); 1272 mCurMethod.appPrivateCommand(action, data); 1273 } catch (RemoteException e) { 1274 Log.w(TAG, "IME died: " + mCurId, e); 1275 } 1276 } 1277 } 1278 1279 /** 1280 * Force switch to a new input method component. This can only be called 1281 * from the currently active input method, as validated by the given token. 1282 * @param token Supplies the identifying token given to an input method 1283 * when it was started, which allows it to perform this operation on 1284 * itself. 1285 * @param id The unique identifier for the new input method to be switched to. 1286 */ 1287 public void setInputMethod(IBinder token, String id) { 1288 try { 1289 mService.setInputMethod(token, id); 1290 } catch (RemoteException e) { 1291 throw new RuntimeException(e); 1292 } 1293 } 1294 1295 /** 1296 * Close/hide the input method's soft input area, so the user no longer 1297 * sees it or can interact with it. This can only be called 1298 * from the currently active input method, as validated by the given token. 1299 * 1300 * @param token Supplies the identifying token given to an input method 1301 * when it was started, which allows it to perform this operation on 1302 * itself. 1303 * @param flags Provides additional operating flags. Currently may be 1304 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 1305 * {@link #HIDE_NOT_ALWAYS} bit set. 1306 */ 1307 public void hideSoftInputFromInputMethod(IBinder token, int flags) { 1308 try { 1309 mService.hideMySoftInput(token, flags); 1310 } catch (RemoteException e) { 1311 throw new RuntimeException(e); 1312 } 1313 } 1314 1315 /** 1316 * Show the input method's soft input area, so the user 1317 * sees the input method window and can interact with it. 1318 * This can only be called from the currently active input method, 1319 * as validated by the given token. 1320 * 1321 * @param token Supplies the identifying token given to an input method 1322 * when it was started, which allows it to perform this operation on 1323 * itself. 1324 * @param flags Provides additional operating flags. Currently may be 1325 * 0 or have the {@link #SHOW_IMPLICIT} or 1326 * {@link #SHOW_FORCED} bit set. 1327 */ 1328 public void showSoftInputFromInputMethod(IBinder token, int flags) { 1329 try { 1330 mService.showMySoftInput(token, flags); 1331 } catch (RemoteException e) { 1332 throw new RuntimeException(e); 1333 } 1334 } 1335 1336 /** 1337 * @hide 1338 */ 1339 public void dispatchKeyEvent(Context context, int seq, KeyEvent key, 1340 IInputMethodCallback callback) { 1341 synchronized (mH) { 1342 if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); 1343 1344 if (mCurMethod == null) { 1345 try { 1346 callback.finishedEvent(seq, false); 1347 } catch (RemoteException e) { 1348 } 1349 return; 1350 } 1351 1352 if (key.getAction() == KeyEvent.ACTION_DOWN 1353 && key.getKeyCode() == KeyEvent.KEYCODE_SYM) { 1354 showInputMethodPicker(); 1355 try { 1356 callback.finishedEvent(seq, true); 1357 } catch (RemoteException e) { 1358 } 1359 return; 1360 } 1361 try { 1362 if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); 1363 mCurMethod.dispatchKeyEvent(seq, key, callback); 1364 } catch (RemoteException e) { 1365 Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); 1366 try { 1367 callback.finishedEvent(seq, false); 1368 } catch (RemoteException ex) { 1369 } 1370 } 1371 } 1372 } 1373 1374 /** 1375 * @hide 1376 */ 1377 void dispatchTrackballEvent(Context context, int seq, MotionEvent motion, 1378 IInputMethodCallback callback) { 1379 synchronized (mH) { 1380 if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); 1381 1382 if (mCurMethod == null || mCurrentTextBoxAttribute == null) { 1383 try { 1384 callback.finishedEvent(seq, false); 1385 } catch (RemoteException e) { 1386 } 1387 return; 1388 } 1389 1390 try { 1391 if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); 1392 mCurMethod.dispatchTrackballEvent(seq, motion, callback); 1393 } catch (RemoteException e) { 1394 Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); 1395 try { 1396 callback.finishedEvent(seq, false); 1397 } catch (RemoteException ex) { 1398 } 1399 } 1400 } 1401 } 1402 1403 public void showInputMethodPicker() { 1404 synchronized (mH) { 1405 try { 1406 mService.showInputMethodPickerFromClient(mClient); 1407 } catch (RemoteException e) { 1408 Log.w(TAG, "IME died: " + mCurId, e); 1409 } 1410 } 1411 } 1412 1413 public void showInputMethodSubtypePicker() { 1414 synchronized (mH) { 1415 try { 1416 mService.showInputMethodSubtypePickerFromClient(mClient); 1417 } catch (RemoteException e) { 1418 Log.w(TAG, "IME died: " + mCurId, e); 1419 } 1420 } 1421 } 1422 1423 public void showInputMethodAndSubtypeEnabler(String topId) { 1424 synchronized (mH) { 1425 try { 1426 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, topId); 1427 } catch (RemoteException e) { 1428 Log.w(TAG, "IME died: " + mCurId, e); 1429 } 1430 } 1431 } 1432 1433 public InputMethodSubtype getCurrentInputMethodSubtype() { 1434 synchronized (mH) { 1435 try { 1436 return mService.getCurrentInputMethodSubtype(); 1437 } catch (RemoteException e) { 1438 Log.w(TAG, "IME died: " + mCurId, e); 1439 return null; 1440 } 1441 } 1442 } 1443 1444 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { 1445 synchronized (mH) { 1446 try { 1447 return mService.setCurrentInputMethodSubtype(subtype); 1448 } catch (RemoteException e) { 1449 Log.w(TAG, "IME died: " + mCurId, e); 1450 return false; 1451 } 1452 } 1453 } 1454 1455 public boolean switchToLastInputMethod(IBinder imeToken) { 1456 synchronized (mH) { 1457 try { 1458 return mService.switchToLastInputMethod(imeToken); 1459 } catch (RemoteException e) { 1460 Log.w(TAG, "IME died: " + mCurId, e); 1461 return false; 1462 } 1463 } 1464 } 1465 1466 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { 1467 final Printer p = new PrintWriterPrinter(fout); 1468 p.println("Input method client state for " + this + ":"); 1469 1470 p.println(" mService=" + mService); 1471 p.println(" mMainLooper=" + mMainLooper); 1472 p.println(" mIInputContext=" + mIInputContext); 1473 p.println(" mActive=" + mActive 1474 + " mHasBeenInactive=" + mHasBeenInactive 1475 + " mBindSequence=" + mBindSequence 1476 + " mCurId=" + mCurId); 1477 p.println(" mCurMethod=" + mCurMethod); 1478 p.println(" mCurRootView=" + mCurRootView); 1479 p.println(" mServedView=" + mServedView); 1480 p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart 1481 + " mNextServedView=" + mNextServedView); 1482 p.println(" mServedConnecting=" + mServedConnecting); 1483 if (mCurrentTextBoxAttribute != null) { 1484 p.println(" mCurrentTextBoxAttribute:"); 1485 mCurrentTextBoxAttribute.dump(p, " "); 1486 } else { 1487 p.println(" mCurrentTextBoxAttribute: null"); 1488 } 1489 p.println(" mServedInputConnection=" + mServedInputConnection); 1490 p.println(" mCompletions=" + mCompletions); 1491 p.println(" mCursorRect=" + mCursorRect); 1492 p.println(" mCursorSelStart=" + mCursorSelStart 1493 + " mCursorSelEnd=" + mCursorSelEnd 1494 + " mCursorCandStart=" + mCursorCandStart 1495 + " mCursorCandEnd=" + mCursorCandEnd); 1496 } 1497} 1498