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