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