InputMethodManager.java revision 3001a035439d8134a7d70d796376d1dfbff3cdcd
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 * Keep track of the view that was set when our window gained focus. 231 */ 232 View mWindowFocusedView; 233 /** 234 * For evaluating the state after a focus change, this is the view that 235 * had focus. 236 */ 237 View mLastServedView; 238 /** 239 * This is set when we are in the process of connecting, to determine 240 * when we have actually finished. 241 */ 242 boolean mServedConnecting; 243 /** 244 * This is non-null when we have connected the served view; it holds 245 * the attributes that were last retrieved from the served view and given 246 * to the input connection. 247 */ 248 EditorInfo mCurrentTextBoxAttribute; 249 /** 250 * The InputConnection that was last retrieved from the served view. 251 */ 252 InputConnection mServedInputConnection; 253 /** 254 * The completions that were last provided by the served view. 255 */ 256 CompletionInfo[] mCompletions; 257 258 // Cursor position on the screen. 259 Rect mTmpCursorRect = new Rect(); 260 Rect mCursorRect = new Rect(); 261 int mCursorSelStart; 262 int mCursorSelEnd; 263 int mCursorCandStart; 264 int mCursorCandEnd; 265 266 // ----------------------------------------------------------- 267 268 /** 269 * Sequence number of this binding, as returned by the server. 270 */ 271 int mBindSequence = -1; 272 /** 273 * ID of the method we are bound to. 274 */ 275 String mCurId; 276 /** 277 * The actual instance of the method to make calls on it. 278 */ 279 IInputMethodSession mCurMethod; 280 281 // ----------------------------------------------------------- 282 283 static final int MSG_DUMP = 1; 284 285 class H extends Handler { 286 H(Looper looper) { 287 super(looper); 288 } 289 290 @Override 291 public void handleMessage(Message msg) { 292 switch (msg.what) { 293 case MSG_DUMP: { 294 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; 295 try { 296 doDump((FileDescriptor)args.arg1, 297 (PrintWriter)args.arg2, (String[])args.arg3); 298 } catch (RuntimeException e) { 299 ((PrintWriter)args.arg2).println("Exception: " + e); 300 } 301 synchronized (args.arg4) { 302 ((CountDownLatch)args.arg4).countDown(); 303 } 304 return; 305 } 306 } 307 } 308 } 309 310 class ControlledInputConnectionWrapper extends IInputConnectionWrapper { 311 public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) { 312 super(mainLooper, conn); 313 } 314 315 public boolean isActive() { 316 return mActive; 317 } 318 } 319 320 final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { 321 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 322 // No need to check for dump permission, since we only give this 323 // interface to the system. 324 325 CountDownLatch latch = new CountDownLatch(1); 326 HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs(); 327 sargs.arg1 = fd; 328 sargs.arg2 = fout; 329 sargs.arg3 = args; 330 sargs.arg4 = latch; 331 mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs)); 332 try { 333 if (!latch.await(5, TimeUnit.SECONDS)) { 334 fout.println("Timeout waiting for dump"); 335 } 336 } catch (InterruptedException e) { 337 fout.println("Interrupted waiting for dump"); 338 } 339 } 340 341 public void setUsingInputMethod(boolean state) { 342 } 343 344 public void onBindMethod(InputBindResult res) { 345 synchronized (mH) { 346 if (mBindSequence < 0 || mBindSequence != res.sequence) { 347 Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence 348 + ", given seq=" + res.sequence); 349 return; 350 } 351 352 mCurMethod = res.method; 353 mCurId = res.id; 354 mBindSequence = res.sequence; 355 } 356 startInputInner(); 357 } 358 359 public void onUnbindMethod(int sequence) { 360 synchronized (mH) { 361 if (mBindSequence == sequence) { 362 if (false) { 363 // XXX the server has already unbound! 364 if (mCurMethod != null && mCurrentTextBoxAttribute != null) { 365 try { 366 mCurMethod.finishInput(); 367 } catch (RemoteException e) { 368 Log.w(TAG, "IME died: " + mCurId, e); 369 } 370 } 371 } 372 clearBindingLocked(); 373 374 // If we were actively using the last input method, then 375 // we would like to re-connect to the next input method. 376 if (mServedView != null && mServedView.isFocused()) { 377 mServedConnecting = true; 378 } 379 } 380 startInputInner(); 381 } 382 } 383 384 public void setActive(boolean active) { 385 mActive = active; 386 mFullscreenMode = false; 387 } 388 }; 389 390 final InputConnection mDummyInputConnection = new BaseInputConnection(this, true); 391 392 InputMethodManager(IInputMethodManager service, Looper looper) { 393 mService = service; 394 mMainLooper = looper; 395 mH = new H(looper); 396 mIInputContext = new ControlledInputConnectionWrapper(looper, 397 mDummyInputConnection); 398 399 if (mInstance == null) { 400 mInstance = this; 401 } 402 } 403 404 /** 405 * Retrieve the global InputMethodManager instance, creating it if it 406 * doesn't already exist. 407 * @hide 408 */ 409 static public InputMethodManager getInstance(Context context) { 410 synchronized (mInstanceSync) { 411 if (mInstance != null) { 412 return mInstance; 413 } 414 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); 415 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b); 416 mInstance = new InputMethodManager(service, context.getMainLooper()); 417 } 418 return mInstance; 419 } 420 421 /** 422 * Private optimization: retrieve the global InputMethodManager instance, 423 * if it exists. 424 * @hide 425 */ 426 static public InputMethodManager peekInstance() { 427 return mInstance; 428 } 429 430 /** @hide */ 431 public IInputMethodClient getClient() { 432 return mClient; 433 } 434 435 /** @hide */ 436 public IInputContext getInputContext() { 437 return mIInputContext; 438 } 439 440 public List<InputMethodInfo> getInputMethodList() { 441 try { 442 return mService.getInputMethodList(); 443 } catch (RemoteException e) { 444 throw new RuntimeException(e); 445 } 446 } 447 448 public List<InputMethodInfo> getEnabledInputMethodList() { 449 try { 450 return mService.getEnabledInputMethodList(); 451 } catch (RemoteException e) { 452 throw new RuntimeException(e); 453 } 454 } 455 456 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) { 457 try { 458 mService.updateStatusIcon(imeToken, packageName, iconId); 459 } catch (RemoteException e) { 460 throw new RuntimeException(e); 461 } 462 } 463 464 public void hideStatusIcon(IBinder imeToken) { 465 try { 466 mService.updateStatusIcon(imeToken, null, 0); 467 } catch (RemoteException e) { 468 throw new RuntimeException(e); 469 } 470 } 471 472 /** @hide */ 473 public void setFullscreenMode(boolean fullScreen) { 474 mFullscreenMode = fullScreen; 475 } 476 477 /** 478 * Allows you to discover whether the attached input method is running 479 * in fullscreen mode. Return true if it is fullscreen, entirely covering 480 * your UI, else returns false. 481 */ 482 public boolean isFullscreenMode() { 483 return mFullscreenMode; 484 } 485 486 /** 487 * Return true if the given view is the currently active view for the 488 * input method. 489 */ 490 public boolean isActive(View view) { 491 synchronized (mH) { 492 return mServedView == view && mCurrentTextBoxAttribute != null; 493 } 494 } 495 496 /** 497 * Return true if any view is currently active in the input method. 498 */ 499 public boolean isActive() { 500 synchronized (mH) { 501 return mServedView != null && mCurrentTextBoxAttribute != null; 502 } 503 } 504 505 /** 506 * Return true if the currently served view is accepting full text edits. 507 * If false, it has no input connection, so can only handle raw key events. 508 */ 509 public boolean isAcceptingText() { 510 return mServedInputConnection != null; 511 } 512 513 /** 514 * Reset all of the state associated with being bound to an input method. 515 */ 516 void clearBindingLocked() { 517 clearConnectionLocked(); 518 mBindSequence = -1; 519 mCurId = null; 520 mCurMethod = null; 521 } 522 523 /** 524 * Reset all of the state associated with a served view being connected 525 * to an input method 526 */ 527 void clearConnectionLocked() { 528 mCurrentTextBoxAttribute = null; 529 mServedInputConnection = null; 530 } 531 532 /** 533 * Disconnect any existing input connection, clearing the served view. 534 */ 535 void finishInputLocked() { 536 if (mServedView != null) { 537 if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView); 538 539 if (mCurrentTextBoxAttribute != null) { 540 try { 541 mService.finishInput(mClient); 542 } catch (RemoteException e) { 543 } 544 } 545 546 if (mServedInputConnection != null) { 547 // We need to tell the previously served view that it is no 548 // longer the input target, so it can reset its state. Schedule 549 // this call on its window's Handler so it will be on the correct 550 // thread and outside of our lock. 551 Handler vh = mServedView.getHandler(); 552 if (vh != null) { 553 // This will result in a call to reportFinishInputConnection() 554 // below. 555 vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION, 556 mServedInputConnection)); 557 } 558 } 559 560 mServedView = null; 561 mCompletions = null; 562 mServedConnecting = false; 563 clearConnectionLocked(); 564 } 565 } 566 567 /** 568 * Called from the FINISH_INPUT_CONNECTION message above. 569 * @hide 570 */ 571 public void reportFinishInputConnection(InputConnection ic) { 572 if (mServedInputConnection != ic) { 573 ic.finishComposingText(); 574 } 575 } 576 577 public void displayCompletions(View view, CompletionInfo[] completions) { 578 synchronized (mH) { 579 if (mServedView != view) { 580 return; 581 } 582 583 mCompletions = completions; 584 if (mCurMethod != null) { 585 try { 586 mCurMethod.displayCompletions(mCompletions); 587 } catch (RemoteException e) { 588 } 589 } 590 } 591 } 592 593 public void updateExtractedText(View view, int token, ExtractedText text) { 594 synchronized (mH) { 595 if (mServedView != view) { 596 return; 597 } 598 599 if (mCurMethod != null) { 600 try { 601 mCurMethod.updateExtractedText(token, text); 602 } catch (RemoteException e) { 603 } 604 } 605 } 606 } 607 608 /** 609 * Flag for {@link #showSoftInput} to indicate that this is an implicit 610 * request to show the input window, not as the result of a direct request 611 * by the user. The window may not be shown in this case. 612 */ 613 public static final int SHOW_IMPLICIT = 0x0001; 614 615 /** 616 * Flag for {@link #showSoftInput} to indicate that the user has forced 617 * the input method open (such as by long-pressing menu) so it should 618 * not be closed until they explicitly do so. 619 */ 620 public static final int SHOW_FORCED = 0x0002; 621 622 /** 623 * Explicitly request that the current input method's soft input area be 624 * shown to the user, if needed. Call this if the user interacts with 625 * your view in such a way that they have expressed they would like to 626 * start performing input into it. 627 * 628 * @param view The currently focused view, which would like to receive 629 * soft keyboard input. 630 * @param flags Provides additional operating flags. Currently may be 631 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 632 */ 633 public void showSoftInput(View view, int flags) { 634 synchronized (mH) { 635 if (mServedView != view) { 636 return; 637 } 638 639 try { 640 mService.showSoftInput(mClient, flags); 641 } catch (RemoteException e) { 642 } 643 } 644 } 645 646 /** @hide */ 647 public void showSoftInputUnchecked(int flags) { 648 try { 649 mService.showSoftInput(mClient, flags); 650 } catch (RemoteException e) { 651 } 652 } 653 654 /** 655 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 656 * input window should only be hidden if it was not explicitly shown 657 * by the user. 658 */ 659 public static final int HIDE_IMPLICIT_ONLY = 0x0001; 660 661 /** 662 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 663 * input window should normally be hidden, unless it was originally 664 * shown with {@link #SHOW_FORCED}. 665 */ 666 public static final int HIDE_NOT_ALWAYS = 0x0002; 667 668 /** 669 * Request to hide the soft input window from the context of the window 670 * that is currently accepting input. This should be called as a result 671 * of the user doing some actually than fairly explicitly requests to 672 * have the input window hidden. 673 * 674 * @param windowToken The token of the window that is making the request, 675 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 676 * @param flags Provides additional operating flags. Currently may be 677 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 678 */ 679 public void hideSoftInputFromWindow(IBinder windowToken, int flags) { 680 synchronized (mH) { 681 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 682 return; 683 } 684 685 try { 686 mService.hideSoftInput(mClient, flags); 687 } catch (RemoteException e) { 688 } 689 } 690 } 691 692 /** 693 * If the input method is currently connected to the given view, 694 * restart it with its new contents. You should call this when the text 695 * within your view changes outside of the normal input method or key 696 * input flow, such as when an application calls TextView.setText(). 697 * 698 * @param view The view whose text has changed. 699 */ 700 public void restartInput(View view) { 701 synchronized (mH) { 702 if (mServedView != view) { 703 return; 704 } 705 706 mServedConnecting = true; 707 } 708 709 startInputInner(); 710 } 711 712 void startInputInner() { 713 final View view; 714 synchronized (mH) { 715 view = mServedView; 716 717 // Make sure we have a window token for the served view. 718 if (DEBUG) Log.v(TAG, "Starting input: view=" + view); 719 if (view == null) { 720 if (DEBUG) Log.v(TAG, "ABORT input: no served view!"); 721 return; 722 } 723 } 724 725 // Now we need to get an input connection from the served view. 726 // This is complicated in a couple ways: we can't be holding our lock 727 // when calling out to the view, and we need to make sure we call into 728 // the view on the same thread that is driving its view hierarchy. 729 Handler vh = view.getHandler(); 730 if (vh == null) { 731 // If the view doesn't have a handler, something has changed out 732 // from under us, so just bail. 733 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!"); 734 return; 735 } 736 if (vh.getLooper() != Looper.myLooper()) { 737 // The view is running on a different thread than our own, so 738 // we need to reschedule our work for over there. 739 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); 740 vh.post(new Runnable() { 741 public void run() { 742 startInputInner(); 743 } 744 }); 745 } 746 747 // Okay we are now ready to call into the served view and have it 748 // do its stuff. 749 // Life is good: let's hook everything up! 750 EditorInfo tba = new EditorInfo(); 751 tba.packageName = view.getContext().getPackageName(); 752 tba.fieldId = view.getId(); 753 InputConnection ic = view.onCreateInputConnection(tba); 754 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic); 755 756 synchronized (mH) { 757 // Now that we are locked again, validate that our state hasn't 758 // changed. 759 if (mServedView != view || !mServedConnecting) { 760 // Something else happened, so abort. 761 if (DEBUG) Log.v(TAG, 762 "Starting input: finished by someone else (view=" 763 + mServedView + " conn=" + mServedConnecting + ")"); 764 return; 765 } 766 767 // If we already have a text box, then this view is already 768 // connected so we want to restart it. 769 final boolean initial = mCurrentTextBoxAttribute == null; 770 771 // Hook 'em up and let 'er rip. 772 mCurrentTextBoxAttribute = tba; 773 mServedConnecting = false; 774 mServedInputConnection = ic; 775 IInputContext servedContext; 776 if (ic != null) { 777 mCursorSelStart = tba.initialSelStart; 778 mCursorSelEnd = tba.initialSelEnd; 779 mCursorCandStart = -1; 780 mCursorCandEnd = -1; 781 mCursorRect.setEmpty(); 782 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic); 783 } else { 784 servedContext = null; 785 } 786 787 try { 788 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" 789 + ic + " tba=" + tba + " initial=" + initial); 790 InputBindResult res = mService.startInput(mClient, 791 servedContext, tba, initial, mCurMethod == null); 792 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); 793 if (res != null) { 794 if (res.id != null) { 795 mBindSequence = res.sequence; 796 mCurMethod = res.method; 797 } else { 798 // This means there is no input method available. 799 if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); 800 return; 801 } 802 } 803 if (mCurMethod != null && mCompletions != null) { 804 try { 805 mCurMethod.displayCompletions(mCompletions); 806 } catch (RemoteException e) { 807 } 808 } 809 } catch (RemoteException e) { 810 Log.w(TAG, "IME died: " + mCurId, e); 811 } 812 } 813 } 814 815 /** 816 * When the focused window is dismissed, this method is called to finish the 817 * input method started before. 818 * @hide 819 */ 820 public void windowDismissed(IBinder appWindowToken) { 821 synchronized (mH) { 822 if (mServedView != null && 823 mServedView.getWindowToken() == appWindowToken) { 824 finishInputLocked(); 825 } 826 } 827 } 828 829 /** 830 * Call this when a view receives focus. 831 * @hide 832 */ 833 public void focusIn(View view) { 834 synchronized (mH) { 835 focusInLocked(view); 836 } 837 838 startInputInner(); 839 } 840 841 void focusInLocked(View view) { 842 if (DEBUG) Log.v(TAG, "focusIn: " + view); 843 // Okay we have a new view that is being served. 844 if (mServedView != view) { 845 mCurrentTextBoxAttribute = null; 846 } 847 mServedView = view; 848 mCompletions = null; 849 mServedConnecting = true; 850 } 851 852 /** 853 * Call this when a view loses focus. 854 * @hide 855 */ 856 public void focusOut(View view) { 857 InputConnection ic = null; 858 synchronized (mH) { 859 if (DEBUG) Log.v(TAG, "focusOut: " + view 860 + " mServedView=" + mServedView 861 + " winFocus=" + view.hasWindowFocus()); 862 if (mServedView == view) { 863 ic = mServedInputConnection; 864 // The following code would auto-hide the IME if we end up 865 // with no more views with focus. This can happen, however, 866 // whenever we go into touch mode, so it ends up hiding 867 // at times when we don't really want it to. For now it 868 // seems better to just turn it all off. 869 if (false && view.hasWindowFocus()) { 870 mLastServedView = view; 871 Handler vh = view.getHandler(); 872 if (vh != null) { 873 // This will result in a call to checkFocus() below. 874 vh.removeMessages(ViewRoot.CHECK_FOCUS); 875 vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS, 876 view)); 877 } 878 } 879 } 880 } 881 882 if (ic != null) { 883 ic.finishComposingText(); 884 } 885 } 886 887 /** 888 * @hide 889 */ 890 public void checkFocus(View view) { 891 synchronized (mH) { 892 if (view != mLastServedView) { 893 return; 894 } 895 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView 896 + " last=" + mLastServedView); 897 if (mServedView == mLastServedView) { 898 finishInputLocked(); 899 // In this case, we used to have a focused view on the window, 900 // but no longer do. We should make sure the input method is 901 // no longer shown, since it serves no purpose. 902 closeCurrentInput(); 903 } 904 mLastServedView = null; 905 } 906 } 907 908 void closeCurrentInput() { 909 try { 910 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS); 911 } catch (RemoteException e) { 912 } 913 } 914 915 /** 916 * Called by ViewRoot the first time it gets window focus. 917 * @hide 918 */ 919 public void onWindowFocus(View rootView, View focusedView, int softInputMode, 920 boolean first, int windowFlags) { 921 boolean needStartInput = false; 922 synchronized (mH) { 923 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView 924 + " softInputMode=" + softInputMode 925 + " first=" + first + " flags=#" 926 + Integer.toHexString(windowFlags)); 927 if (mWindowFocusedView == null) { 928 focusInLocked(focusedView != null ? focusedView : rootView); 929 needStartInput = true; 930 } 931 } 932 933 if (needStartInput) { 934 startInputInner(); 935 } 936 937 synchronized (mH) { 938 try { 939 final boolean isTextEditor = focusedView != null && 940 focusedView.onCheckIsTextEditor(); 941 mService.windowGainedFocus(mClient, focusedView != null, 942 isTextEditor, softInputMode, first, windowFlags); 943 } catch (RemoteException e) { 944 } 945 } 946 } 947 948 /** @hide */ 949 public void startGettingWindowFocus() { 950 synchronized (mH) { 951 mWindowFocusedView = null; 952 } 953 } 954 955 /** 956 * Report the current selection range. 957 */ 958 public void updateSelection(View view, int selStart, int selEnd, 959 int candidatesStart, int candidatesEnd) { 960 synchronized (mH) { 961 if (mServedView != view || mCurrentTextBoxAttribute == null 962 || mCurMethod == null) { 963 return; 964 } 965 966 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd 967 || mCursorCandStart != candidatesStart 968 || mCursorCandEnd != candidatesEnd) { 969 if (DEBUG) Log.d(TAG, "updateSelection"); 970 971 try { 972 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod); 973 mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd, 974 selStart, selEnd, candidatesStart, candidatesEnd); 975 mCursorSelStart = selStart; 976 mCursorSelEnd = selEnd; 977 mCursorCandStart = candidatesStart; 978 mCursorCandEnd = candidatesEnd; 979 } catch (RemoteException e) { 980 Log.w(TAG, "IME died: " + mCurId, e); 981 } 982 } 983 } 984 } 985 986 /** 987 * Returns true if the current input method wants to watch the location 988 * of the input editor's cursor in its window. 989 */ 990 public boolean isWatchingCursor(View view) { 991 return false; 992 } 993 994 /** 995 * Report the current cursor location in its window. 996 */ 997 public void updateCursor(View view, int left, int top, int right, int bottom) { 998 synchronized (mH) { 999 if (mServedView != view || mCurrentTextBoxAttribute == null 1000 || mCurMethod == null) { 1001 return; 1002 } 1003 1004 mTmpCursorRect.set(left, top, right, bottom); 1005 if (!mCursorRect.equals(mTmpCursorRect)) { 1006 if (DEBUG) Log.d(TAG, "updateCursor"); 1007 1008 try { 1009 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); 1010 mCurMethod.updateCursor(mTmpCursorRect); 1011 mCursorRect.set(mTmpCursorRect); 1012 } catch (RemoteException e) { 1013 Log.w(TAG, "IME died: " + mCurId, e); 1014 } 1015 } 1016 } 1017 } 1018 1019 /** 1020 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) 1021 * InputMethodSession.appPrivateCommand()} on the current Input Method. 1022 * @param view Optional View that is sending the command, or null if 1023 * you want to send the command regardless of the view that is attached 1024 * to the input method. 1025 * @param action Name of the command to be performed. This <em>must</em> 1026 * be a scoped name, i.e. prefixed with a package name you own, so that 1027 * different developers will not create conflicting commands. 1028 * @param data Any data to include with the command. 1029 */ 1030 public void sendAppPrivateCommand(View view, String action, Bundle data) { 1031 synchronized (mH) { 1032 if ((view != null && mServedView != view) 1033 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1034 return; 1035 } 1036 try { 1037 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); 1038 mCurMethod.appPrivateCommand(action, data); 1039 } catch (RemoteException e) { 1040 Log.w(TAG, "IME died: " + mCurId, e); 1041 } 1042 } 1043 } 1044 1045 /** 1046 * Force switch to a new input method component. This can only be called 1047 * from the currently active input method, as validated by the given token. 1048 * @param token Supplies the identifying token given to an input method 1049 * when it was started, which allows it to perform this operation on 1050 * itself. 1051 * @param id The unique identifier for the new input method to be switched to. 1052 */ 1053 public void setInputMethod(IBinder token, String id) { 1054 try { 1055 mService.setInputMethod(token, id); 1056 } catch (RemoteException e) { 1057 throw new RuntimeException(e); 1058 } 1059 } 1060 1061 /** 1062 * Close/hide the input method's soft input area, so the user no longer 1063 * sees it or can interact with it. This can only be called 1064 * from the currently active input method, as validated by the given token. 1065 * 1066 * @param token Supplies the identifying token given to an input method 1067 * when it was started, which allows it to perform this operation on 1068 * itself. 1069 * @param flags Provides additional operating flags. Currently may be 1070 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 1071 */ 1072 public void hideSoftInputFromInputMethod(IBinder token, int flags) { 1073 try { 1074 mService.hideMySoftInput(token, flags); 1075 } catch (RemoteException e) { 1076 throw new RuntimeException(e); 1077 } 1078 } 1079 1080 /** 1081 * @hide 1082 */ 1083 public void dispatchKeyEvent(Context context, int seq, KeyEvent key, 1084 IInputMethodCallback callback) { 1085 synchronized (mH) { 1086 if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); 1087 1088 if (mCurMethod == null) { 1089 try { 1090 callback.finishedEvent(seq, false); 1091 } catch (RemoteException e) { 1092 } 1093 return; 1094 } 1095 1096 if (key.getAction() == KeyEvent.ACTION_DOWN 1097 && key.getKeyCode() == KeyEvent.KEYCODE_SYM) { 1098 showInputMethodPicker(); 1099 try { 1100 callback.finishedEvent(seq, true); 1101 } catch (RemoteException e) { 1102 } 1103 return; 1104 } 1105 try { 1106 if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); 1107 mCurMethod.dispatchKeyEvent(seq, key, callback); 1108 } catch (RemoteException e) { 1109 Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); 1110 try { 1111 callback.finishedEvent(seq, false); 1112 } catch (RemoteException ex) { 1113 } 1114 } 1115 } 1116 } 1117 1118 /** 1119 * @hide 1120 */ 1121 void dispatchTrackballEvent(Context context, int seq, MotionEvent motion, 1122 IInputMethodCallback callback) { 1123 synchronized (mH) { 1124 if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); 1125 1126 if (mCurMethod == null || mCurrentTextBoxAttribute == null) { 1127 try { 1128 callback.finishedEvent(seq, false); 1129 } catch (RemoteException e) { 1130 } 1131 return; 1132 } 1133 1134 try { 1135 if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); 1136 mCurMethod.dispatchTrackballEvent(seq, motion, callback); 1137 } catch (RemoteException e) { 1138 Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); 1139 try { 1140 callback.finishedEvent(seq, false); 1141 } catch (RemoteException ex) { 1142 } 1143 } 1144 } 1145 } 1146 1147 public void showInputMethodPicker() { 1148 synchronized (mH) { 1149 try { 1150 mService.showInputMethodPickerFromClient(mClient); 1151 } catch (RemoteException e) { 1152 Log.w(TAG, "IME died: " + mCurId, e); 1153 } 1154 } 1155 } 1156 1157 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { 1158 final Printer p = new PrintWriterPrinter(fout); 1159 p.println("Input method client state for " + this + ":"); 1160 1161 p.println(" mService=" + mService); 1162 p.println(" mMainLooper=" + mMainLooper); 1163 p.println(" mIInputContext=" + mIInputContext); 1164 p.println(" mActive=" + mActive 1165 + " mBindSequence=" + mBindSequence 1166 + " mCurId=" + mCurId); 1167 p.println(" mCurMethod=" + mCurMethod); 1168 p.println(" mServedView=" + mServedView); 1169 p.println(" mLastServedView=" + mLastServedView); 1170 p.println(" mServedConnecting=" + mServedConnecting); 1171 if (mCurrentTextBoxAttribute != null) { 1172 p.println(" mCurrentTextBoxAttribute:"); 1173 mCurrentTextBoxAttribute.dump(p, " "); 1174 } else { 1175 p.println(" mCurrentTextBoxAttribute: null"); 1176 } 1177 p.println(" mServedInputConnection=" + mServedInputConnection); 1178 p.println(" mCompletions=" + mCompletions); 1179 p.println(" mCursorRect=" + mCursorRect); 1180 p.println(" mCursorSelStart=" + mCursorSelStart 1181 + " mCursorSelEnd=" + mCursorSelEnd 1182 + " mCursorCandStart=" + mCursorCandStart 1183 + " mCursorCandEnd=" + mCursorCandEnd); 1184 } 1185} 1186