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