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