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 17 package android.view.inputmethod; 18 19 import android.content.Context; 20 import android.graphics.Rect; 21 import android.os.Bundle; 22 import android.os.Handler; 23 import android.os.IBinder; 24 import android.os.Looper; 25 import android.os.Message; 26 import android.os.RemoteException; 27 import android.os.ResultReceiver; 28 import android.os.ServiceManager; 29 import android.util.Log; 30 import android.util.PrintWriterPrinter; 31 import android.util.Printer; 32 import android.view.KeyEvent; 33 import android.view.MotionEvent; 34 import android.view.View; 35 import android.view.ViewRoot; 36 37 import com.android.internal.os.HandlerCaller; 38 import com.android.internal.view.IInputConnectionWrapper; 39 import com.android.internal.view.IInputContext; 40 import com.android.internal.view.IInputMethodCallback; 41 import com.android.internal.view.IInputMethodClient; 42 import com.android.internal.view.IInputMethodManager; 43 import com.android.internal.view.IInputMethodSession; 44 import com.android.internal.view.InputBindResult; 45 46 import java.io.FileDescriptor; 47 import java.io.PrintWriter; 48 import java.util.List; 49 import java.util.concurrent.CountDownLatch; 50 import 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 */ 190 public 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 return getInstance(context.getMainLooper()); 450 } 451 452 /** 453 * Internally, the input method manager can't be context-dependent, so 454 * we have this here for the places that need it. 455 * @hide 456 */ 457 static public InputMethodManager getInstance(Looper mainLooper) { 458 synchronized (mInstanceSync) { 459 if (mInstance != null) { 460 return mInstance; 461 } 462 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); 463 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b); 464 mInstance = new InputMethodManager(service, mainLooper); 465 } 466 return mInstance; 467 } 468 469 /** 470 * Private optimization: retrieve the global InputMethodManager instance, 471 * if it exists. 472 * @hide 473 */ 474 static public InputMethodManager peekInstance() { 475 return mInstance; 476 } 477 478 /** @hide */ 479 public IInputMethodClient getClient() { 480 return mClient; 481 } 482 483 /** @hide */ 484 public IInputContext getInputContext() { 485 return mIInputContext; 486 } 487 488 public List<InputMethodInfo> getInputMethodList() { 489 try { 490 return mService.getInputMethodList(); 491 } catch (RemoteException e) { 492 throw new RuntimeException(e); 493 } 494 } 495 496 public List<InputMethodInfo> getEnabledInputMethodList() { 497 try { 498 return mService.getEnabledInputMethodList(); 499 } catch (RemoteException e) { 500 throw new RuntimeException(e); 501 } 502 } 503 504 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) { 505 try { 506 mService.updateStatusIcon(imeToken, packageName, iconId); 507 } catch (RemoteException e) { 508 throw new RuntimeException(e); 509 } 510 } 511 512 public void hideStatusIcon(IBinder imeToken) { 513 try { 514 mService.updateStatusIcon(imeToken, null, 0); 515 } catch (RemoteException e) { 516 throw new RuntimeException(e); 517 } 518 } 519 520 /** @hide */ 521 public void setFullscreenMode(boolean fullScreen) { 522 mFullscreenMode = fullScreen; 523 } 524 525 /** 526 * Allows you to discover whether the attached input method is running 527 * in fullscreen mode. Return true if it is fullscreen, entirely covering 528 * your UI, else returns false. 529 */ 530 public boolean isFullscreenMode() { 531 return mFullscreenMode; 532 } 533 534 /** 535 * Return true if the given view is the currently active view for the 536 * input method. 537 */ 538 public boolean isActive(View view) { 539 checkFocus(); 540 synchronized (mH) { 541 return (mServedView == view 542 || (mServedView != null 543 && mServedView.checkInputConnectionProxy(view))) 544 && mCurrentTextBoxAttribute != null; 545 } 546 } 547 548 /** 549 * Return true if any view is currently active in the input method. 550 */ 551 public boolean isActive() { 552 checkFocus(); 553 synchronized (mH) { 554 return mServedView != null && mCurrentTextBoxAttribute != null; 555 } 556 } 557 558 /** 559 * Return true if the currently served view is accepting full text edits. 560 * If false, it has no input connection, so can only handle raw key events. 561 */ 562 public boolean isAcceptingText() { 563 checkFocus(); 564 return mServedInputConnection != null; 565 } 566 567 /** 568 * Reset all of the state associated with being bound to an input method. 569 */ 570 void clearBindingLocked() { 571 clearConnectionLocked(); 572 mBindSequence = -1; 573 mCurId = null; 574 mCurMethod = null; 575 } 576 577 /** 578 * Reset all of the state associated with a served view being connected 579 * to an input method 580 */ 581 void clearConnectionLocked() { 582 mCurrentTextBoxAttribute = null; 583 mServedInputConnection = null; 584 } 585 586 /** 587 * Disconnect any existing input connection, clearing the served view. 588 */ 589 void finishInputLocked() { 590 mNextServedView = null; 591 if (mServedView != null) { 592 if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView); 593 594 if (mCurrentTextBoxAttribute != null) { 595 try { 596 mService.finishInput(mClient); 597 } catch (RemoteException e) { 598 } 599 } 600 601 if (mServedInputConnection != null) { 602 // We need to tell the previously served view that it is no 603 // longer the input target, so it can reset its state. Schedule 604 // this call on its window's Handler so it will be on the correct 605 // thread and outside of our lock. 606 Handler vh = mServedView.getHandler(); 607 if (vh != null) { 608 // This will result in a call to reportFinishInputConnection() 609 // below. 610 vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION, 611 mServedInputConnection)); 612 } 613 } 614 615 mServedView = null; 616 mCompletions = null; 617 mServedConnecting = false; 618 clearConnectionLocked(); 619 } 620 } 621 622 /** 623 * Called from the FINISH_INPUT_CONNECTION message above. 624 * @hide 625 */ 626 public void reportFinishInputConnection(InputConnection ic) { 627 if (mServedInputConnection != ic) { 628 ic.finishComposingText(); 629 } 630 } 631 632 public void displayCompletions(View view, CompletionInfo[] completions) { 633 checkFocus(); 634 synchronized (mH) { 635 if (mServedView != view && (mServedView == null 636 || !mServedView.checkInputConnectionProxy(view))) { 637 return; 638 } 639 640 mCompletions = completions; 641 if (mCurMethod != null) { 642 try { 643 mCurMethod.displayCompletions(mCompletions); 644 } catch (RemoteException e) { 645 } 646 } 647 } 648 } 649 650 public void updateExtractedText(View view, int token, ExtractedText text) { 651 checkFocus(); 652 synchronized (mH) { 653 if (mServedView != view && (mServedView == null 654 || !mServedView.checkInputConnectionProxy(view))) { 655 return; 656 } 657 658 if (mCurMethod != null) { 659 try { 660 mCurMethod.updateExtractedText(token, text); 661 } catch (RemoteException e) { 662 } 663 } 664 } 665 } 666 667 /** 668 * Flag for {@link #showSoftInput} to indicate that this is an implicit 669 * request to show the input window, not as the result of a direct request 670 * by the user. The window may not be shown in this case. 671 */ 672 public static final int SHOW_IMPLICIT = 0x0001; 673 674 /** 675 * Flag for {@link #showSoftInput} to indicate that the user has forced 676 * the input method open (such as by long-pressing menu) so it should 677 * not be closed until they explicitly do so. 678 */ 679 public static final int SHOW_FORCED = 0x0002; 680 681 /** 682 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without 683 * a result receiver: explicitly request that the current input method's 684 * soft input area be shown to the user, if needed. 685 * 686 * @param view The currently focused view, which would like to receive 687 * soft keyboard input. 688 * @param flags Provides additional operating flags. Currently may be 689 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 690 */ 691 public boolean showSoftInput(View view, int flags) { 692 return showSoftInput(view, flags, null); 693 } 694 695 /** 696 * Flag for the {@link ResultReceiver} result code from 697 * {@link #showSoftInput(View, int, ResultReceiver)} and 698 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 699 * state of the soft input window was unchanged and remains shown. 700 */ 701 public static final int RESULT_UNCHANGED_SHOWN = 0; 702 703 /** 704 * Flag for the {@link ResultReceiver} result code from 705 * {@link #showSoftInput(View, int, ResultReceiver)} and 706 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 707 * state of the soft input window was unchanged and remains hidden. 708 */ 709 public static final int RESULT_UNCHANGED_HIDDEN = 1; 710 711 /** 712 * Flag for the {@link ResultReceiver} result code from 713 * {@link #showSoftInput(View, int, ResultReceiver)} and 714 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 715 * state of the soft input window changed from hidden to shown. 716 */ 717 public static final int RESULT_SHOWN = 2; 718 719 /** 720 * Flag for the {@link ResultReceiver} result code from 721 * {@link #showSoftInput(View, int, ResultReceiver)} and 722 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 723 * state of the soft input window changed from shown to hidden. 724 */ 725 public static final int RESULT_HIDDEN = 3; 726 727 /** 728 * Explicitly request that the current input method's soft input area be 729 * shown to the user, if needed. Call this if the user interacts with 730 * your view in such a way that they have expressed they would like to 731 * start performing input into it. 732 * 733 * @param view The currently focused view, which would like to receive 734 * soft keyboard input. 735 * @param flags Provides additional operating flags. Currently may be 736 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 737 * @param resultReceiver If non-null, this will be called by the IME when 738 * it has processed your request to tell you what it has done. The result 739 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 740 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 741 * {@link #RESULT_HIDDEN}. 742 */ 743 public boolean showSoftInput(View view, int flags, 744 ResultReceiver resultReceiver) { 745 checkFocus(); 746 synchronized (mH) { 747 if (mServedView != view && (mServedView == null 748 || !mServedView.checkInputConnectionProxy(view))) { 749 return false; 750 } 751 752 try { 753 return mService.showSoftInput(mClient, flags, resultReceiver); 754 } catch (RemoteException e) { 755 } 756 757 return false; 758 } 759 } 760 761 /** @hide */ 762 public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) { 763 try { 764 mService.showSoftInput(mClient, flags, resultReceiver); 765 } catch (RemoteException e) { 766 } 767 } 768 769 /** 770 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 771 * input window should only be hidden if it was not explicitly shown 772 * by the user. 773 */ 774 public static final int HIDE_IMPLICIT_ONLY = 0x0001; 775 776 /** 777 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 778 * input window should normally be hidden, unless it was originally 779 * shown with {@link #SHOW_FORCED}. 780 */ 781 public static final int HIDE_NOT_ALWAYS = 0x0002; 782 783 /** 784 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver) 785 * without a result: request to hide the soft input window from the 786 * context of the window that is currently accepting input. 787 * 788 * @param windowToken The token of the window that is making the request, 789 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 790 * @param flags Provides additional operating flags. Currently may be 791 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 792 */ 793 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) { 794 return hideSoftInputFromWindow(windowToken, flags, null); 795 } 796 797 /** 798 * Request to hide the soft input window from the context of the window 799 * that is currently accepting input. This should be called as a result 800 * of the user doing some actually than fairly explicitly requests to 801 * have the input window hidden. 802 * 803 * @param windowToken The token of the window that is making the request, 804 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 805 * @param flags Provides additional operating flags. Currently may be 806 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 807 * @param resultReceiver If non-null, this will be called by the IME when 808 * it has processed your request to tell you what it has done. The result 809 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 810 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 811 * {@link #RESULT_HIDDEN}. 812 */ 813 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, 814 ResultReceiver resultReceiver) { 815 checkFocus(); 816 synchronized (mH) { 817 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 818 return false; 819 } 820 821 try { 822 return mService.hideSoftInput(mClient, flags, resultReceiver); 823 } catch (RemoteException e) { 824 } 825 return false; 826 } 827 } 828 829 830 /** 831 * This method toggles the input method window display. 832 * If the input window is already displayed, it gets hidden. 833 * If not the input window will be displayed. 834 * @param windowToken The token of the window that is making the request, 835 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 836 * @param showFlags Provides additional operating flags. May be 837 * 0 or have the {@link #SHOW_IMPLICIT}, 838 * {@link #SHOW_FORCED} bit set. 839 * @param hideFlags Provides additional operating flags. May be 840 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 841 * {@link #HIDE_NOT_ALWAYS} bit set. 842 **/ 843 public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) { 844 synchronized (mH) { 845 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 846 return; 847 } 848 if (mCurMethod != null) { 849 try { 850 mCurMethod.toggleSoftInput(showFlags, hideFlags); 851 } catch (RemoteException e) { 852 } 853 } 854 } 855 } 856 857 /* 858 * This method toggles the input method window display. 859 * If the input window is already displayed, it gets hidden. 860 * If not the input window will be displayed. 861 * @param showFlags Provides additional operating flags. May be 862 * 0 or have the {@link #SHOW_IMPLICIT}, 863 * {@link #SHOW_FORCED} bit set. 864 * @param hideFlags Provides additional operating flags. May be 865 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 866 * {@link #HIDE_NOT_ALWAYS} bit set. 867 * @hide 868 */ 869 public void toggleSoftInput(int showFlags, int hideFlags) { 870 if (mCurMethod != null) { 871 try { 872 mCurMethod.toggleSoftInput(showFlags, hideFlags); 873 } catch (RemoteException e) { 874 } 875 } 876 } 877 878 /** 879 * If the input method is currently connected to the given view, 880 * restart it with its new contents. You should call this when the text 881 * within your view changes outside of the normal input method or key 882 * input flow, such as when an application calls TextView.setText(). 883 * 884 * @param view The view whose text has changed. 885 */ 886 public void restartInput(View view) { 887 checkFocus(); 888 synchronized (mH) { 889 if (mServedView != view && (mServedView == null 890 || !mServedView.checkInputConnectionProxy(view))) { 891 return; 892 } 893 894 mServedConnecting = true; 895 } 896 897 startInputInner(); 898 } 899 900 void startInputInner() { 901 final View view; 902 synchronized (mH) { 903 view = mServedView; 904 905 // Make sure we have a window token for the served view. 906 if (DEBUG) Log.v(TAG, "Starting input: view=" + view); 907 if (view == null) { 908 if (DEBUG) Log.v(TAG, "ABORT input: no served view!"); 909 return; 910 } 911 } 912 913 // Now we need to get an input connection from the served view. 914 // This is complicated in a couple ways: we can't be holding our lock 915 // when calling out to the view, and we need to make sure we call into 916 // the view on the same thread that is driving its view hierarchy. 917 Handler vh = view.getHandler(); 918 if (vh == null) { 919 // If the view doesn't have a handler, something has changed out 920 // from under us, so just bail. 921 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!"); 922 return; 923 } 924 if (vh.getLooper() != Looper.myLooper()) { 925 // The view is running on a different thread than our own, so 926 // we need to reschedule our work for over there. 927 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); 928 vh.post(new Runnable() { 929 public void run() { 930 startInputInner(); 931 } 932 }); 933 return; 934 } 935 936 // Okay we are now ready to call into the served view and have it 937 // do its stuff. 938 // Life is good: let's hook everything up! 939 EditorInfo tba = new EditorInfo(); 940 tba.packageName = view.getContext().getPackageName(); 941 tba.fieldId = view.getId(); 942 InputConnection ic = view.onCreateInputConnection(tba); 943 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic); 944 945 synchronized (mH) { 946 // Now that we are locked again, validate that our state hasn't 947 // changed. 948 if (mServedView != view || !mServedConnecting) { 949 // Something else happened, so abort. 950 if (DEBUG) Log.v(TAG, 951 "Starting input: finished by someone else (view=" 952 + mServedView + " conn=" + mServedConnecting + ")"); 953 return; 954 } 955 956 // If we already have a text box, then this view is already 957 // connected so we want to restart it. 958 final boolean initial = mCurrentTextBoxAttribute == null; 959 960 // Hook 'em up and let 'er rip. 961 mCurrentTextBoxAttribute = tba; 962 mServedConnecting = false; 963 mServedInputConnection = ic; 964 IInputContext servedContext; 965 if (ic != null) { 966 mCursorSelStart = tba.initialSelStart; 967 mCursorSelEnd = tba.initialSelEnd; 968 mCursorCandStart = -1; 969 mCursorCandEnd = -1; 970 mCursorRect.setEmpty(); 971 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic); 972 } else { 973 servedContext = null; 974 } 975 976 try { 977 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" 978 + ic + " tba=" + tba + " initial=" + initial); 979 InputBindResult res = mService.startInput(mClient, 980 servedContext, tba, initial, mCurMethod == null); 981 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); 982 if (res != null) { 983 if (res.id != null) { 984 mBindSequence = res.sequence; 985 mCurMethod = res.method; 986 } else { 987 // This means there is no input method available. 988 if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); 989 return; 990 } 991 } 992 if (mCurMethod != null && mCompletions != null) { 993 try { 994 mCurMethod.displayCompletions(mCompletions); 995 } catch (RemoteException e) { 996 } 997 } 998 } catch (RemoteException e) { 999 Log.w(TAG, "IME died: " + mCurId, e); 1000 } 1001 } 1002 } 1003 1004 /** 1005 * When the focused window is dismissed, this method is called to finish the 1006 * input method started before. 1007 * @hide 1008 */ 1009 public void windowDismissed(IBinder appWindowToken) { 1010 checkFocus(); 1011 synchronized (mH) { 1012 if (mServedView != null && 1013 mServedView.getWindowToken() == appWindowToken) { 1014 finishInputLocked(); 1015 } 1016 } 1017 } 1018 1019 /** 1020 * Call this when a view receives focus. 1021 * @hide 1022 */ 1023 public void focusIn(View view) { 1024 synchronized (mH) { 1025 focusInLocked(view); 1026 } 1027 } 1028 1029 void focusInLocked(View view) { 1030 if (DEBUG) Log.v(TAG, "focusIn: " + view); 1031 1032 if (mCurRootView != view.getRootView()) { 1033 // This is a request from a window that isn't in the window with 1034 // IME focus, so ignore it. 1035 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring"); 1036 return; 1037 } 1038 1039 mNextServedView = view; 1040 scheduleCheckFocusLocked(view); 1041 } 1042 1043 /** 1044 * Call this when a view loses focus. 1045 * @hide 1046 */ 1047 public void focusOut(View view) { 1048 synchronized (mH) { 1049 if (DEBUG) Log.v(TAG, "focusOut: " + view 1050 + " mServedView=" + mServedView 1051 + " winFocus=" + view.hasWindowFocus()); 1052 if (mServedView != view) { 1053 // The following code would auto-hide the IME if we end up 1054 // with no more views with focus. This can happen, however, 1055 // whenever we go into touch mode, so it ends up hiding 1056 // at times when we don't really want it to. For now it 1057 // seems better to just turn it all off. 1058 if (false && view.hasWindowFocus()) { 1059 mNextServedView = null; 1060 scheduleCheckFocusLocked(view); 1061 } 1062 } 1063 } 1064 } 1065 1066 void scheduleCheckFocusLocked(View view) { 1067 Handler vh = view.getHandler(); 1068 if (vh != null && !vh.hasMessages(ViewRoot.CHECK_FOCUS)) { 1069 // This will result in a call to checkFocus() below. 1070 vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS)); 1071 } 1072 } 1073 1074 /** 1075 * @hide 1076 */ 1077 public void checkFocus() { 1078 // This is called a lot, so short-circuit before locking. 1079 if (mServedView == mNextServedView && !mNextServedNeedsStart) { 1080 return; 1081 } 1082 1083 InputConnection ic = null; 1084 synchronized (mH) { 1085 if (mServedView == mNextServedView && !mNextServedNeedsStart) { 1086 return; 1087 } 1088 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView 1089 + " next=" + mNextServedView 1090 + " restart=" + mNextServedNeedsStart); 1091 1092 mNextServedNeedsStart = false; 1093 if (mNextServedView == null) { 1094 finishInputLocked(); 1095 // In this case, we used to have a focused view on the window, 1096 // but no longer do. We should make sure the input method is 1097 // no longer shown, since it serves no purpose. 1098 closeCurrentInput(); 1099 return; 1100 } 1101 1102 ic = mServedInputConnection; 1103 1104 mServedView = mNextServedView; 1105 mCurrentTextBoxAttribute = null; 1106 mCompletions = null; 1107 mServedConnecting = true; 1108 } 1109 1110 if (ic != null) { 1111 ic.finishComposingText(); 1112 } 1113 1114 startInputInner(); 1115 } 1116 1117 void closeCurrentInput() { 1118 try { 1119 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null); 1120 } catch (RemoteException e) { 1121 } 1122 } 1123 1124 /** 1125 * Called by ViewRoot when its window gets input focus. 1126 * @hide 1127 */ 1128 public void onWindowFocus(View rootView, View focusedView, int softInputMode, 1129 boolean first, int windowFlags) { 1130 synchronized (mH) { 1131 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView 1132 + " softInputMode=" + softInputMode 1133 + " first=" + first + " flags=#" 1134 + Integer.toHexString(windowFlags)); 1135 if (mHasBeenInactive) { 1136 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh"); 1137 mHasBeenInactive = false; 1138 mNextServedNeedsStart = true; 1139 } 1140 focusInLocked(focusedView != null ? focusedView : rootView); 1141 } 1142 1143 checkFocus(); 1144 1145 synchronized (mH) { 1146 try { 1147 final boolean isTextEditor = focusedView != null && 1148 focusedView.onCheckIsTextEditor(); 1149 mService.windowGainedFocus(mClient, rootView.getWindowToken(), 1150 focusedView != null, isTextEditor, softInputMode, first, 1151 windowFlags); 1152 } catch (RemoteException e) { 1153 } 1154 } 1155 } 1156 1157 /** @hide */ 1158 public void startGettingWindowFocus(View rootView) { 1159 synchronized (mH) { 1160 mCurRootView = rootView; 1161 } 1162 } 1163 1164 /** 1165 * Report the current selection range. 1166 */ 1167 public void updateSelection(View view, int selStart, int selEnd, 1168 int candidatesStart, int candidatesEnd) { 1169 checkFocus(); 1170 synchronized (mH) { 1171 if ((mServedView != view && (mServedView == null 1172 || !mServedView.checkInputConnectionProxy(view))) 1173 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1174 return; 1175 } 1176 1177 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd 1178 || mCursorCandStart != candidatesStart 1179 || mCursorCandEnd != candidatesEnd) { 1180 if (DEBUG) Log.d(TAG, "updateSelection"); 1181 1182 try { 1183 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod); 1184 mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd, 1185 selStart, selEnd, candidatesStart, candidatesEnd); 1186 mCursorSelStart = selStart; 1187 mCursorSelEnd = selEnd; 1188 mCursorCandStart = candidatesStart; 1189 mCursorCandEnd = candidatesEnd; 1190 } catch (RemoteException e) { 1191 Log.w(TAG, "IME died: " + mCurId, e); 1192 } 1193 } 1194 } 1195 } 1196 1197 /** 1198 * Returns true if the current input method wants to watch the location 1199 * of the input editor's cursor in its window. 1200 */ 1201 public boolean isWatchingCursor(View view) { 1202 return false; 1203 } 1204 1205 /** 1206 * Report the current cursor location in its window. 1207 */ 1208 public void updateCursor(View view, int left, int top, int right, int bottom) { 1209 checkFocus(); 1210 synchronized (mH) { 1211 if ((mServedView != view && (mServedView == null 1212 || !mServedView.checkInputConnectionProxy(view))) 1213 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1214 return; 1215 } 1216 1217 mTmpCursorRect.set(left, top, right, bottom); 1218 if (!mCursorRect.equals(mTmpCursorRect)) { 1219 if (DEBUG) Log.d(TAG, "updateCursor"); 1220 1221 try { 1222 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); 1223 mCurMethod.updateCursor(mTmpCursorRect); 1224 mCursorRect.set(mTmpCursorRect); 1225 } catch (RemoteException e) { 1226 Log.w(TAG, "IME died: " + mCurId, e); 1227 } 1228 } 1229 } 1230 } 1231 1232 /** 1233 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) 1234 * InputMethodSession.appPrivateCommand()} on the current Input Method. 1235 * @param view Optional View that is sending the command, or null if 1236 * you want to send the command regardless of the view that is attached 1237 * to the input method. 1238 * @param action Name of the command to be performed. This <em>must</em> 1239 * be a scoped name, i.e. prefixed with a package name you own, so that 1240 * different developers will not create conflicting commands. 1241 * @param data Any data to include with the command. 1242 */ 1243 public void sendAppPrivateCommand(View view, String action, Bundle data) { 1244 checkFocus(); 1245 synchronized (mH) { 1246 if ((mServedView != view && (mServedView == null 1247 || !mServedView.checkInputConnectionProxy(view))) 1248 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1249 return; 1250 } 1251 try { 1252 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); 1253 mCurMethod.appPrivateCommand(action, data); 1254 } catch (RemoteException e) { 1255 Log.w(TAG, "IME died: " + mCurId, e); 1256 } 1257 } 1258 } 1259 1260 /** 1261 * Force switch to a new input method component. This can only be called 1262 * from the currently active input method, as validated by the given token. 1263 * @param token Supplies the identifying token given to an input method 1264 * when it was started, which allows it to perform this operation on 1265 * itself. 1266 * @param id The unique identifier for the new input method to be switched to. 1267 */ 1268 public void setInputMethod(IBinder token, String id) { 1269 try { 1270 mService.setInputMethod(token, id); 1271 } catch (RemoteException e) { 1272 throw new RuntimeException(e); 1273 } 1274 } 1275 1276 /** 1277 * Close/hide the input method's soft input area, so the user no longer 1278 * sees it or can interact with it. This can only be called 1279 * from the currently active input method, as validated by the given token. 1280 * 1281 * @param token Supplies the identifying token given to an input method 1282 * when it was started, which allows it to perform this operation on 1283 * itself. 1284 * @param flags Provides additional operating flags. Currently may be 1285 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 1286 * {@link #HIDE_NOT_ALWAYS} bit set. 1287 */ 1288 public void hideSoftInputFromInputMethod(IBinder token, int flags) { 1289 try { 1290 mService.hideMySoftInput(token, flags); 1291 } catch (RemoteException e) { 1292 throw new RuntimeException(e); 1293 } 1294 } 1295 1296 /** 1297 * Show the input method's soft input area, so the user 1298 * sees the input method window and can interact with it. 1299 * This can only be called from the currently active input method, 1300 * as validated by the given token. 1301 * 1302 * @param token Supplies the identifying token given to an input method 1303 * when it was started, which allows it to perform this operation on 1304 * itself. 1305 * @param flags Provides additional operating flags. Currently may be 1306 * 0 or have the {@link #SHOW_IMPLICIT} or 1307 * {@link #SHOW_FORCED} bit set. 1308 */ 1309 public void showSoftInputFromInputMethod(IBinder token, int flags) { 1310 try { 1311 mService.showMySoftInput(token, flags); 1312 } catch (RemoteException e) { 1313 throw new RuntimeException(e); 1314 } 1315 } 1316 1317 /** 1318 * @hide 1319 */ 1320 public void dispatchKeyEvent(Context context, int seq, KeyEvent key, 1321 IInputMethodCallback callback) { 1322 synchronized (mH) { 1323 if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); 1324 1325 if (mCurMethod == null) { 1326 try { 1327 callback.finishedEvent(seq, false); 1328 } catch (RemoteException e) { 1329 } 1330 return; 1331 } 1332 1333 if (key.getAction() == KeyEvent.ACTION_DOWN 1334 && key.getKeyCode() == KeyEvent.KEYCODE_SYM) { 1335 showInputMethodPicker(); 1336 try { 1337 callback.finishedEvent(seq, true); 1338 } catch (RemoteException e) { 1339 } 1340 return; 1341 } 1342 try { 1343 if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); 1344 mCurMethod.dispatchKeyEvent(seq, key, callback); 1345 } catch (RemoteException e) { 1346 Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); 1347 try { 1348 callback.finishedEvent(seq, false); 1349 } catch (RemoteException ex) { 1350 } 1351 } 1352 } 1353 } 1354 1355 /** 1356 * @hide 1357 */ 1358 void dispatchTrackballEvent(Context context, int seq, MotionEvent motion, 1359 IInputMethodCallback callback) { 1360 synchronized (mH) { 1361 if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); 1362 1363 if (mCurMethod == null || mCurrentTextBoxAttribute == null) { 1364 try { 1365 callback.finishedEvent(seq, false); 1366 } catch (RemoteException e) { 1367 } 1368 return; 1369 } 1370 1371 try { 1372 if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); 1373 mCurMethod.dispatchTrackballEvent(seq, motion, callback); 1374 } catch (RemoteException e) { 1375 Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); 1376 try { 1377 callback.finishedEvent(seq, false); 1378 } catch (RemoteException ex) { 1379 } 1380 } 1381 } 1382 } 1383 1384 public void showInputMethodPicker() { 1385 synchronized (mH) { 1386 try { 1387 mService.showInputMethodPickerFromClient(mClient); 1388 } catch (RemoteException e) { 1389 Log.w(TAG, "IME died: " + mCurId, e); 1390 } 1391 } 1392 } 1393 1394 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { 1395 final Printer p = new PrintWriterPrinter(fout); 1396 p.println("Input method client state for " + this + ":"); 1397 1398 p.println(" mService=" + mService); 1399 p.println(" mMainLooper=" + mMainLooper); 1400 p.println(" mIInputContext=" + mIInputContext); 1401 p.println(" mActive=" + mActive 1402 + " mHasBeenInactive=" + mHasBeenInactive 1403 + " mBindSequence=" + mBindSequence 1404 + " mCurId=" + mCurId); 1405 p.println(" mCurMethod=" + mCurMethod); 1406 p.println(" mCurRootView=" + mCurRootView); 1407 p.println(" mServedView=" + mServedView); 1408 p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart 1409 + " mNextServedView=" + mNextServedView); 1410 p.println(" mServedConnecting=" + mServedConnecting); 1411 if (mCurrentTextBoxAttribute != null) { 1412 p.println(" mCurrentTextBoxAttribute:"); 1413 mCurrentTextBoxAttribute.dump(p, " "); 1414 } else { 1415 p.println(" mCurrentTextBoxAttribute: null"); 1416 } 1417 p.println(" mServedInputConnection=" + mServedInputConnection); 1418 p.println(" mCompletions=" + mCompletions); 1419 p.println(" mCursorRect=" + mCursorRect); 1420 p.println(" mCursorSelStart=" + mCursorSelStart 1421 + " mCursorSelEnd=" + mCursorSelEnd 1422 + " mCursorCandStart=" + mCursorCandStart 1423 + " mCursorCandEnd=" + mCursorCandEnd); 1424 } 1425 } 1426