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