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