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, false); 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 return; 925 } 926 927 // Okay we are now ready to call into the served view and have it 928 // do its stuff. 929 // Life is good: let's hook everything up! 930 EditorInfo tba = new EditorInfo(); 931 tba.packageName = view.getContext().getPackageName(); 932 tba.fieldId = view.getId(); 933 InputConnection ic = view.onCreateInputConnection(tba); 934 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic); 935 936 synchronized (mH) { 937 // Now that we are locked again, validate that our state hasn't 938 // changed. 939 if (mServedView != view || !mServedConnecting) { 940 // Something else happened, so abort. 941 if (DEBUG) Log.v(TAG, 942 "Starting input: finished by someone else (view=" 943 + mServedView + " conn=" + mServedConnecting + ")"); 944 return; 945 } 946 947 // If we already have a text box, then this view is already 948 // connected so we want to restart it. 949 final boolean initial = mCurrentTextBoxAttribute == null; 950 951 // Hook 'em up and let 'er rip. 952 mCurrentTextBoxAttribute = tba; 953 mServedConnecting = false; 954 mServedInputConnection = ic; 955 IInputContext servedContext; 956 if (ic != null) { 957 mCursorSelStart = tba.initialSelStart; 958 mCursorSelEnd = tba.initialSelEnd; 959 mCursorCandStart = -1; 960 mCursorCandEnd = -1; 961 mCursorRect.setEmpty(); 962 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic); 963 } else { 964 servedContext = null; 965 } 966 967 try { 968 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" 969 + ic + " tba=" + tba + " initial=" + initial); 970 InputBindResult res = mService.startInput(mClient, 971 servedContext, tba, initial, mCurMethod == null); 972 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); 973 if (res != null) { 974 if (res.id != null) { 975 mBindSequence = res.sequence; 976 mCurMethod = res.method; 977 } else { 978 // This means there is no input method available. 979 if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); 980 return; 981 } 982 } 983 if (mCurMethod != null && mCompletions != null) { 984 try { 985 mCurMethod.displayCompletions(mCompletions); 986 } catch (RemoteException e) { 987 } 988 } 989 } catch (RemoteException e) { 990 Log.w(TAG, "IME died: " + mCurId, e); 991 } 992 } 993 } 994 995 /** 996 * When the focused window is dismissed, this method is called to finish the 997 * input method started before. 998 * @hide 999 */ 1000 public void windowDismissed(IBinder appWindowToken) { 1001 checkFocus(); 1002 synchronized (mH) { 1003 if (mServedView != null && 1004 mServedView.getWindowToken() == appWindowToken) { 1005 finishInputLocked(); 1006 } 1007 } 1008 } 1009 1010 /** 1011 * Call this when a view receives focus. 1012 * @hide 1013 */ 1014 public void focusIn(View view) { 1015 synchronized (mH) { 1016 focusInLocked(view); 1017 } 1018 } 1019 1020 void focusInLocked(View view) { 1021 if (DEBUG) Log.v(TAG, "focusIn: " + view); 1022 1023 if (mCurRootView != view.getRootView()) { 1024 // This is a request from a window that isn't in the window with 1025 // IME focus, so ignore it. 1026 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring"); 1027 return; 1028 } 1029 1030 mNextServedView = view; 1031 scheduleCheckFocusLocked(view); 1032 } 1033 1034 /** 1035 * Call this when a view loses focus. 1036 * @hide 1037 */ 1038 public void focusOut(View view) { 1039 synchronized (mH) { 1040 if (DEBUG) Log.v(TAG, "focusOut: " + view 1041 + " mServedView=" + mServedView 1042 + " winFocus=" + view.hasWindowFocus()); 1043 if (mServedView != view) { 1044 // The following code would auto-hide the IME if we end up 1045 // with no more views with focus. This can happen, however, 1046 // whenever we go into touch mode, so it ends up hiding 1047 // at times when we don't really want it to. For now it 1048 // seems better to just turn it all off. 1049 if (false && view.hasWindowFocus()) { 1050 mNextServedView = null; 1051 scheduleCheckFocusLocked(view); 1052 } 1053 } 1054 } 1055 } 1056 1057 void scheduleCheckFocusLocked(View view) { 1058 Handler vh = view.getHandler(); 1059 if (vh != null && !vh.hasMessages(ViewRoot.CHECK_FOCUS)) { 1060 // This will result in a call to checkFocus() below. 1061 vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS)); 1062 } 1063 } 1064 1065 /** 1066 * @hide 1067 */ 1068 public void checkFocus() { 1069 // This is called a lot, so short-circuit before locking. 1070 if (mServedView == mNextServedView && !mNextServedNeedsStart) { 1071 return; 1072 } 1073 1074 InputConnection ic = null; 1075 synchronized (mH) { 1076 if (mServedView == mNextServedView && !mNextServedNeedsStart) { 1077 return; 1078 } 1079 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView 1080 + " next=" + mNextServedView 1081 + " restart=" + mNextServedNeedsStart); 1082 1083 mNextServedNeedsStart = false; 1084 if (mNextServedView == null) { 1085 finishInputLocked(); 1086 // In this case, we used to have a focused view on the window, 1087 // but no longer do. We should make sure the input method is 1088 // no longer shown, since it serves no purpose. 1089 closeCurrentInput(); 1090 return; 1091 } 1092 1093 ic = mServedInputConnection; 1094 1095 mServedView = mNextServedView; 1096 mCurrentTextBoxAttribute = null; 1097 mCompletions = null; 1098 mServedConnecting = true; 1099 } 1100 1101 if (ic != null) { 1102 ic.finishComposingText(); 1103 } 1104 1105 startInputInner(); 1106 } 1107 1108 void closeCurrentInput() { 1109 try { 1110 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null); 1111 } catch (RemoteException e) { 1112 } 1113 } 1114 1115 /** 1116 * Called by ViewRoot when its window gets input focus. 1117 * @hide 1118 */ 1119 public void onWindowFocus(View rootView, View focusedView, int softInputMode, 1120 boolean first, int windowFlags) { 1121 synchronized (mH) { 1122 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView 1123 + " softInputMode=" + softInputMode 1124 + " first=" + first + " flags=#" 1125 + Integer.toHexString(windowFlags)); 1126 if (mHasBeenInactive) { 1127 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh"); 1128 mHasBeenInactive = false; 1129 mNextServedNeedsStart = true; 1130 } 1131 focusInLocked(focusedView != null ? focusedView : rootView); 1132 } 1133 1134 checkFocus(); 1135 1136 synchronized (mH) { 1137 try { 1138 final boolean isTextEditor = focusedView != null && 1139 focusedView.onCheckIsTextEditor(); 1140 mService.windowGainedFocus(mClient, rootView.getWindowToken(), 1141 focusedView != null, isTextEditor, softInputMode, first, 1142 windowFlags); 1143 } catch (RemoteException e) { 1144 } 1145 } 1146 } 1147 1148 /** @hide */ 1149 public void startGettingWindowFocus(View rootView) { 1150 synchronized (mH) { 1151 mCurRootView = rootView; 1152 } 1153 } 1154 1155 /** 1156 * Report the current selection range. 1157 */ 1158 public void updateSelection(View view, int selStart, int selEnd, 1159 int candidatesStart, int candidatesEnd) { 1160 checkFocus(); 1161 synchronized (mH) { 1162 if ((mServedView != view && (mServedView == null 1163 || !mServedView.checkInputConnectionProxy(view))) 1164 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1165 return; 1166 } 1167 1168 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd 1169 || mCursorCandStart != candidatesStart 1170 || mCursorCandEnd != candidatesEnd) { 1171 if (DEBUG) Log.d(TAG, "updateSelection"); 1172 1173 try { 1174 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod); 1175 mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd, 1176 selStart, selEnd, candidatesStart, candidatesEnd); 1177 mCursorSelStart = selStart; 1178 mCursorSelEnd = selEnd; 1179 mCursorCandStart = candidatesStart; 1180 mCursorCandEnd = candidatesEnd; 1181 } catch (RemoteException e) { 1182 Log.w(TAG, "IME died: " + mCurId, e); 1183 } 1184 } 1185 } 1186 } 1187 1188 /** 1189 * Returns true if the current input method wants to watch the location 1190 * of the input editor's cursor in its window. 1191 */ 1192 public boolean isWatchingCursor(View view) { 1193 return false; 1194 } 1195 1196 /** 1197 * Report the current cursor location in its window. 1198 */ 1199 public void updateCursor(View view, int left, int top, int right, int bottom) { 1200 checkFocus(); 1201 synchronized (mH) { 1202 if ((mServedView != view && (mServedView == null 1203 || !mServedView.checkInputConnectionProxy(view))) 1204 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1205 return; 1206 } 1207 1208 mTmpCursorRect.set(left, top, right, bottom); 1209 if (!mCursorRect.equals(mTmpCursorRect)) { 1210 if (DEBUG) Log.d(TAG, "updateCursor"); 1211 1212 try { 1213 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); 1214 mCurMethod.updateCursor(mTmpCursorRect); 1215 mCursorRect.set(mTmpCursorRect); 1216 } catch (RemoteException e) { 1217 Log.w(TAG, "IME died: " + mCurId, e); 1218 } 1219 } 1220 } 1221 } 1222 1223 /** 1224 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) 1225 * InputMethodSession.appPrivateCommand()} on the current Input Method. 1226 * @param view Optional View that is sending the command, or null if 1227 * you want to send the command regardless of the view that is attached 1228 * to the input method. 1229 * @param action Name of the command to be performed. This <em>must</em> 1230 * be a scoped name, i.e. prefixed with a package name you own, so that 1231 * different developers will not create conflicting commands. 1232 * @param data Any data to include with the command. 1233 */ 1234 public void sendAppPrivateCommand(View view, String action, Bundle data) { 1235 checkFocus(); 1236 synchronized (mH) { 1237 if ((mServedView != view && (mServedView == null 1238 || !mServedView.checkInputConnectionProxy(view))) 1239 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1240 return; 1241 } 1242 try { 1243 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); 1244 mCurMethod.appPrivateCommand(action, data); 1245 } catch (RemoteException e) { 1246 Log.w(TAG, "IME died: " + mCurId, e); 1247 } 1248 } 1249 } 1250 1251 /** 1252 * Force switch to a new input method component. This can only be called 1253 * from the currently active input method, as validated by the given token. 1254 * @param token Supplies the identifying token given to an input method 1255 * when it was started, which allows it to perform this operation on 1256 * itself. 1257 * @param id The unique identifier for the new input method to be switched to. 1258 */ 1259 public void setInputMethod(IBinder token, String id) { 1260 try { 1261 mService.setInputMethod(token, id); 1262 } catch (RemoteException e) { 1263 throw new RuntimeException(e); 1264 } 1265 } 1266 1267 /** 1268 * Close/hide the input method's soft input area, so the user no longer 1269 * sees it or can interact with it. This can only be called 1270 * from the currently active input method, as validated by the given token. 1271 * 1272 * @param token Supplies the identifying token given to an input method 1273 * when it was started, which allows it to perform this operation on 1274 * itself. 1275 * @param flags Provides additional operating flags. Currently may be 1276 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 1277 * {@link #HIDE_NOT_ALWAYS} bit set. 1278 */ 1279 public void hideSoftInputFromInputMethod(IBinder token, int flags) { 1280 try { 1281 mService.hideMySoftInput(token, flags); 1282 } catch (RemoteException e) { 1283 throw new RuntimeException(e); 1284 } 1285 } 1286 1287 /** 1288 * Show the input method's soft input area, so the user 1289 * sees the input method window and can interact with it. 1290 * This can only be called from the currently active input method, 1291 * as validated by the given token. 1292 * 1293 * @param token Supplies the identifying token given to an input method 1294 * when it was started, which allows it to perform this operation on 1295 * itself. 1296 * @param flags Provides additional operating flags. Currently may be 1297 * 0 or have the {@link #SHOW_IMPLICIT} or 1298 * {@link #SHOW_FORCED} bit set. 1299 */ 1300 public void showSoftInputFromInputMethod(IBinder token, int flags) { 1301 try { 1302 mService.showMySoftInput(token, flags); 1303 } catch (RemoteException e) { 1304 throw new RuntimeException(e); 1305 } 1306 } 1307 1308 /** 1309 * @hide 1310 */ 1311 public void dispatchKeyEvent(Context context, int seq, KeyEvent key, 1312 IInputMethodCallback callback) { 1313 synchronized (mH) { 1314 if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); 1315 1316 if (mCurMethod == null) { 1317 try { 1318 callback.finishedEvent(seq, false); 1319 } catch (RemoteException e) { 1320 } 1321 return; 1322 } 1323 1324 if (key.getAction() == KeyEvent.ACTION_DOWN 1325 && key.getKeyCode() == KeyEvent.KEYCODE_SYM) { 1326 showInputMethodPicker(); 1327 try { 1328 callback.finishedEvent(seq, true); 1329 } catch (RemoteException e) { 1330 } 1331 return; 1332 } 1333 try { 1334 if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); 1335 mCurMethod.dispatchKeyEvent(seq, key, callback); 1336 } catch (RemoteException e) { 1337 Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); 1338 try { 1339 callback.finishedEvent(seq, false); 1340 } catch (RemoteException ex) { 1341 } 1342 } 1343 } 1344 } 1345 1346 /** 1347 * @hide 1348 */ 1349 void dispatchTrackballEvent(Context context, int seq, MotionEvent motion, 1350 IInputMethodCallback callback) { 1351 synchronized (mH) { 1352 if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); 1353 1354 if (mCurMethod == null || mCurrentTextBoxAttribute == null) { 1355 try { 1356 callback.finishedEvent(seq, false); 1357 } catch (RemoteException e) { 1358 } 1359 return; 1360 } 1361 1362 try { 1363 if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); 1364 mCurMethod.dispatchTrackballEvent(seq, motion, callback); 1365 } catch (RemoteException e) { 1366 Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); 1367 try { 1368 callback.finishedEvent(seq, false); 1369 } catch (RemoteException ex) { 1370 } 1371 } 1372 } 1373 } 1374 1375 public void showInputMethodPicker() { 1376 synchronized (mH) { 1377 try { 1378 mService.showInputMethodPickerFromClient(mClient); 1379 } catch (RemoteException e) { 1380 Log.w(TAG, "IME died: " + mCurId, e); 1381 } 1382 } 1383 } 1384 1385 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { 1386 final Printer p = new PrintWriterPrinter(fout); 1387 p.println("Input method client state for " + this + ":"); 1388 1389 p.println(" mService=" + mService); 1390 p.println(" mMainLooper=" + mMainLooper); 1391 p.println(" mIInputContext=" + mIInputContext); 1392 p.println(" mActive=" + mActive 1393 + " mHasBeenInactive=" + mHasBeenInactive 1394 + " mBindSequence=" + mBindSequence 1395 + " mCurId=" + mCurId); 1396 p.println(" mCurMethod=" + mCurMethod); 1397 p.println(" mCurRootView=" + mCurRootView); 1398 p.println(" mServedView=" + mServedView); 1399 p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart 1400 + " mNextServedView=" + mNextServedView); 1401 p.println(" mServedConnecting=" + mServedConnecting); 1402 if (mCurrentTextBoxAttribute != null) { 1403 p.println(" mCurrentTextBoxAttribute:"); 1404 mCurrentTextBoxAttribute.dump(p, " "); 1405 } else { 1406 p.println(" mCurrentTextBoxAttribute: null"); 1407 } 1408 p.println(" mServedInputConnection=" + mServedInputConnection); 1409 p.println(" mCompletions=" + mCompletions); 1410 p.println(" mCursorRect=" + mCursorRect); 1411 p.println(" mCursorSelStart=" + mCursorSelStart 1412 + " mCursorSelEnd=" + mCursorSelEnd 1413 + " mCursorCandStart=" + mCursorCandStart 1414 + " mCursorCandEnd=" + mCursorCandEnd); 1415 } 1416} 1417