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