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