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