InputMethodManager.java revision 863fcd62171e55bc9f2105d9fb5877df982454d8
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.ViewAncestor; 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 final IInputMethodManager mService; 202 final Looper mMainLooper; 203 204 // For scheduling work on the main thread. This also serves as our 205 // global lock. 206 final H mH; 207 208 // Our generic input connection if the current target does not have its own. 209 final IInputContext mIInputContext; 210 211 /** 212 * True if this input method client is active, initially false. 213 */ 214 boolean mActive = false; 215 216 /** 217 * Set whenever this client becomes inactive, to know we need to reset 218 * state with the IME then next time we receive focus. 219 */ 220 boolean mHasBeenInactive = true; 221 222 /** 223 * As reported by IME through InputConnection. 224 */ 225 boolean mFullscreenMode; 226 227 // ----------------------------------------------------------- 228 229 /** 230 * This is the root view of the overall window that currently has input 231 * method focus. 232 */ 233 View mCurRootView; 234 /** 235 * This is the view that should currently be served by an input method, 236 * regardless of the state of setting that up. 237 */ 238 View mServedView; 239 /** 240 * This is then next view that will be served by the input method, when 241 * we get around to updating things. 242 */ 243 View mNextServedView; 244 /** 245 * True if we should restart input in the next served view, even if the 246 * view hasn't actually changed from the current serve view. 247 */ 248 boolean mNextServedNeedsStart; 249 /** 250 * This is set when we are in the process of connecting, to determine 251 * when we have actually finished. 252 */ 253 boolean mServedConnecting; 254 /** 255 * This is non-null when we have connected the served view; it holds 256 * the attributes that were last retrieved from the served view and given 257 * to the input connection. 258 */ 259 EditorInfo mCurrentTextBoxAttribute; 260 /** 261 * The InputConnection that was last retrieved from the served view. 262 */ 263 InputConnection mServedInputConnection; 264 /** 265 * The completions that were last provided by the served view. 266 */ 267 CompletionInfo[] mCompletions; 268 269 // Cursor position on the screen. 270 Rect mTmpCursorRect = new Rect(); 271 Rect mCursorRect = new Rect(); 272 int mCursorSelStart; 273 int mCursorSelEnd; 274 int mCursorCandStart; 275 int mCursorCandEnd; 276 277 // ----------------------------------------------------------- 278 279 /** 280 * Sequence number of this binding, as returned by the server. 281 */ 282 int mBindSequence = -1; 283 /** 284 * ID of the method we are bound to. 285 */ 286 String mCurId; 287 /** 288 * The actual instance of the method to make calls on it. 289 */ 290 IInputMethodSession mCurMethod; 291 292 // ----------------------------------------------------------- 293 294 static final int MSG_DUMP = 1; 295 static final int MSG_BIND = 2; 296 static final int MSG_UNBIND = 3; 297 static final int MSG_SET_ACTIVE = 4; 298 299 class H extends Handler { 300 H(Looper looper) { 301 super(looper); 302 } 303 304 @Override 305 public void handleMessage(Message msg) { 306 switch (msg.what) { 307 case MSG_DUMP: { 308 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; 309 try { 310 doDump((FileDescriptor)args.arg1, 311 (PrintWriter)args.arg2, (String[])args.arg3); 312 } catch (RuntimeException e) { 313 ((PrintWriter)args.arg2).println("Exception: " + e); 314 } 315 synchronized (args.arg4) { 316 ((CountDownLatch)args.arg4).countDown(); 317 } 318 return; 319 } 320 case MSG_BIND: { 321 final InputBindResult res = (InputBindResult)msg.obj; 322 synchronized (mH) { 323 if (mBindSequence < 0 || mBindSequence != res.sequence) { 324 Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence 325 + ", given seq=" + res.sequence); 326 return; 327 } 328 329 mCurMethod = res.method; 330 mCurId = res.id; 331 mBindSequence = res.sequence; 332 } 333 startInputInner(); 334 return; 335 } 336 case MSG_UNBIND: { 337 final int sequence = msg.arg1; 338 synchronized (mH) { 339 if (mBindSequence == sequence) { 340 if (false) { 341 // XXX the server has already unbound! 342 if (mCurMethod != null && mCurrentTextBoxAttribute != null) { 343 try { 344 mCurMethod.finishInput(); 345 } catch (RemoteException e) { 346 Log.w(TAG, "IME died: " + mCurId, e); 347 } 348 } 349 } 350 clearBindingLocked(); 351 352 // If we were actively using the last input method, then 353 // we would like to re-connect to the next input method. 354 if (mServedView != null && mServedView.isFocused()) { 355 mServedConnecting = true; 356 } 357 } 358 startInputInner(); 359 } 360 return; 361 } 362 case MSG_SET_ACTIVE: { 363 final boolean active = msg.arg1 != 0; 364 synchronized (mH) { 365 mActive = active; 366 mFullscreenMode = false; 367 if (!active) { 368 // Some other client has starting using the IME, so note 369 // that this happened and make sure our own editor's 370 // state is reset. 371 mHasBeenInactive = true; 372 try { 373 // Note that finishComposingText() is allowed to run 374 // even when we are not active. 375 mIInputContext.finishComposingText(); 376 } catch (RemoteException e) { 377 } 378 } 379 } 380 return; 381 } 382 } 383 } 384 } 385 386 class ControlledInputConnectionWrapper extends IInputConnectionWrapper { 387 public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) { 388 super(mainLooper, conn); 389 } 390 391 @Override 392 public boolean isActive() { 393 return mActive; 394 } 395 } 396 397 final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { 398 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 399 // No need to check for dump permission, since we only give this 400 // interface to the system. 401 402 CountDownLatch latch = new CountDownLatch(1); 403 HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs(); 404 sargs.arg1 = fd; 405 sargs.arg2 = fout; 406 sargs.arg3 = args; 407 sargs.arg4 = latch; 408 mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs)); 409 try { 410 if (!latch.await(5, TimeUnit.SECONDS)) { 411 fout.println("Timeout waiting for dump"); 412 } 413 } catch (InterruptedException e) { 414 fout.println("Interrupted waiting for dump"); 415 } 416 } 417 418 public void setUsingInputMethod(boolean state) { 419 } 420 421 public void onBindMethod(InputBindResult res) { 422 mH.sendMessage(mH.obtainMessage(MSG_BIND, res)); 423 } 424 425 public void onUnbindMethod(int sequence) { 426 mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0)); 427 } 428 429 public void setActive(boolean active) { 430 mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0)); 431 } 432 }; 433 434 final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); 435 436 InputMethodManager(IInputMethodManager service, Looper looper) { 437 mService = service; 438 mMainLooper = looper; 439 mH = new H(looper); 440 mIInputContext = new ControlledInputConnectionWrapper(looper, 441 mDummyInputConnection); 442 443 if (mInstance == null) { 444 mInstance = this; 445 } 446 } 447 448 /** 449 * Retrieve the global InputMethodManager instance, creating it if it 450 * doesn't already exist. 451 * @hide 452 */ 453 static public InputMethodManager getInstance(Context context) { 454 return getInstance(context.getMainLooper()); 455 } 456 457 /** 458 * Internally, the input method manager can't be context-dependent, so 459 * we have this here for the places that need it. 460 * @hide 461 */ 462 static public InputMethodManager getInstance(Looper mainLooper) { 463 synchronized (mInstanceSync) { 464 if (mInstance != null) { 465 return mInstance; 466 } 467 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); 468 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b); 469 mInstance = new InputMethodManager(service, mainLooper); 470 } 471 return mInstance; 472 } 473 474 /** 475 * Private optimization: retrieve the global InputMethodManager instance, 476 * if it exists. 477 * @hide 478 */ 479 static public InputMethodManager peekInstance() { 480 return mInstance; 481 } 482 483 /** @hide */ 484 public IInputMethodClient getClient() { 485 return mClient; 486 } 487 488 /** @hide */ 489 public IInputContext getInputContext() { 490 return mIInputContext; 491 } 492 493 public List<InputMethodInfo> getInputMethodList() { 494 try { 495 return mService.getInputMethodList(); 496 } catch (RemoteException e) { 497 throw new RuntimeException(e); 498 } 499 } 500 501 public List<InputMethodInfo> getEnabledInputMethodList() { 502 try { 503 return mService.getEnabledInputMethodList(); 504 } catch (RemoteException e) { 505 throw new RuntimeException(e); 506 } 507 } 508 509 /** 510 * Returns a list of enabled input method subtypes for the specified input method info. 511 * @param imi An input method info whose subtypes list will be returned. 512 * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly 513 * selected subtypes. If an input method info doesn't have enabled subtypes, the framework 514 * will implicitly enable subtypes according to the current system language. 515 */ 516 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi, 517 boolean allowsImplicitlySelectedSubtypes) { 518 try { 519 return mService.getEnabledInputMethodSubtypeList(imi, allowsImplicitlySelectedSubtypes); 520 } catch (RemoteException e) { 521 throw new RuntimeException(e); 522 } 523 } 524 525 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) { 526 try { 527 mService.updateStatusIcon(imeToken, packageName, iconId); 528 } catch (RemoteException e) { 529 throw new RuntimeException(e); 530 } 531 } 532 533 public void hideStatusIcon(IBinder imeToken) { 534 try { 535 mService.updateStatusIcon(imeToken, null, 0); 536 } catch (RemoteException e) { 537 throw new RuntimeException(e); 538 } 539 } 540 541 /** @hide */ 542 public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) { 543 try { 544 mService.setImeWindowStatus(imeToken, vis, backDisposition); 545 } catch (RemoteException e) { 546 throw new RuntimeException(e); 547 } 548 } 549 550 /** @hide */ 551 public void setFullscreenMode(boolean fullScreen) { 552 mFullscreenMode = fullScreen; 553 } 554 555 /** @hide */ 556 public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) { 557 try { 558 mService.registerSuggestionSpansForNotification(spans); 559 } catch (RemoteException e) { 560 throw new RuntimeException(e); 561 } 562 } 563 564 /** @hide */ 565 public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) { 566 try { 567 mService.notifySuggestionPicked(span, originalString, index); 568 } catch (RemoteException e) { 569 throw new RuntimeException(e); 570 } 571 } 572 573 /** 574 * Allows you to discover whether the attached input method is running 575 * in fullscreen mode. Return true if it is fullscreen, entirely covering 576 * your UI, else returns false. 577 */ 578 public boolean isFullscreenMode() { 579 return mFullscreenMode; 580 } 581 582 /** 583 * Return true if the given view is the currently active view for the 584 * input method. 585 */ 586 public boolean isActive(View view) { 587 checkFocus(); 588 synchronized (mH) { 589 return (mServedView == view 590 || (mServedView != null 591 && mServedView.checkInputConnectionProxy(view))) 592 && mCurrentTextBoxAttribute != null; 593 } 594 } 595 596 /** 597 * Return true if any view is currently active in the input method. 598 */ 599 public boolean isActive() { 600 checkFocus(); 601 synchronized (mH) { 602 return mServedView != null && mCurrentTextBoxAttribute != null; 603 } 604 } 605 606 /** 607 * Return true if the currently served view is accepting full text edits. 608 * If false, it has no input connection, so can only handle raw key events. 609 */ 610 public boolean isAcceptingText() { 611 checkFocus(); 612 return mServedInputConnection != null; 613 } 614 615 /** 616 * Reset all of the state associated with being bound to an input method. 617 */ 618 void clearBindingLocked() { 619 clearConnectionLocked(); 620 mBindSequence = -1; 621 mCurId = null; 622 mCurMethod = null; 623 } 624 625 /** 626 * Reset all of the state associated with a served view being connected 627 * to an input method 628 */ 629 void clearConnectionLocked() { 630 mCurrentTextBoxAttribute = null; 631 mServedInputConnection = null; 632 } 633 634 /** 635 * Disconnect any existing input connection, clearing the served view. 636 */ 637 void finishInputLocked() { 638 mNextServedView = null; 639 if (mServedView != null) { 640 if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView); 641 642 if (mCurrentTextBoxAttribute != null) { 643 try { 644 mService.finishInput(mClient); 645 } catch (RemoteException e) { 646 } 647 } 648 649 if (mServedInputConnection != null) { 650 // We need to tell the previously served view that it is no 651 // longer the input target, so it can reset its state. Schedule 652 // this call on its window's Handler so it will be on the correct 653 // thread and outside of our lock. 654 Handler vh = mServedView.getHandler(); 655 if (vh != null) { 656 // This will result in a call to reportFinishInputConnection() 657 // below. 658 vh.sendMessage(vh.obtainMessage(ViewAncestor.FINISH_INPUT_CONNECTION, 659 mServedInputConnection)); 660 } 661 } 662 663 mServedView = null; 664 mCompletions = null; 665 mServedConnecting = false; 666 clearConnectionLocked(); 667 } 668 } 669 670 /** 671 * Called from the FINISH_INPUT_CONNECTION message above. 672 * @hide 673 */ 674 public void reportFinishInputConnection(InputConnection ic) { 675 if (mServedInputConnection != ic) { 676 ic.finishComposingText(); 677 } 678 } 679 680 public void displayCompletions(View view, CompletionInfo[] completions) { 681 checkFocus(); 682 synchronized (mH) { 683 if (mServedView != view && (mServedView == null 684 || !mServedView.checkInputConnectionProxy(view))) { 685 return; 686 } 687 688 mCompletions = completions; 689 if (mCurMethod != null) { 690 try { 691 mCurMethod.displayCompletions(mCompletions); 692 } catch (RemoteException e) { 693 } 694 } 695 } 696 } 697 698 public void updateExtractedText(View view, int token, ExtractedText text) { 699 checkFocus(); 700 synchronized (mH) { 701 if (mServedView != view && (mServedView == null 702 || !mServedView.checkInputConnectionProxy(view))) { 703 return; 704 } 705 706 if (mCurMethod != null) { 707 try { 708 mCurMethod.updateExtractedText(token, text); 709 } catch (RemoteException e) { 710 } 711 } 712 } 713 } 714 715 /** 716 * Flag for {@link #showSoftInput} to indicate that this is an implicit 717 * request to show the input window, not as the result of a direct request 718 * by the user. The window may not be shown in this case. 719 */ 720 public static final int SHOW_IMPLICIT = 0x0001; 721 722 /** 723 * Flag for {@link #showSoftInput} to indicate that the user has forced 724 * the input method open (such as by long-pressing menu) so it should 725 * not be closed until they explicitly do so. 726 */ 727 public static final int SHOW_FORCED = 0x0002; 728 729 /** 730 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without 731 * a result receiver: explicitly request that the current input method's 732 * soft input area be shown to the user, if needed. 733 * 734 * @param view The currently focused view, which would like to receive 735 * soft keyboard input. 736 * @param flags Provides additional operating flags. Currently may be 737 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 738 */ 739 public boolean showSoftInput(View view, int flags) { 740 return showSoftInput(view, flags, null); 741 } 742 743 /** 744 * Flag for the {@link ResultReceiver} result code from 745 * {@link #showSoftInput(View, int, ResultReceiver)} and 746 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 747 * state of the soft input window was unchanged and remains shown. 748 */ 749 public static final int RESULT_UNCHANGED_SHOWN = 0; 750 751 /** 752 * Flag for the {@link ResultReceiver} result code from 753 * {@link #showSoftInput(View, int, ResultReceiver)} and 754 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 755 * state of the soft input window was unchanged and remains hidden. 756 */ 757 public static final int RESULT_UNCHANGED_HIDDEN = 1; 758 759 /** 760 * Flag for the {@link ResultReceiver} result code from 761 * {@link #showSoftInput(View, int, ResultReceiver)} and 762 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 763 * state of the soft input window changed from hidden to shown. 764 */ 765 public static final int RESULT_SHOWN = 2; 766 767 /** 768 * Flag for the {@link ResultReceiver} result code from 769 * {@link #showSoftInput(View, int, ResultReceiver)} and 770 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 771 * state of the soft input window changed from shown to hidden. 772 */ 773 public static final int RESULT_HIDDEN = 3; 774 775 /** 776 * Explicitly request that the current input method's soft input area be 777 * shown to the user, if needed. Call this if the user interacts with 778 * your view in such a way that they have expressed they would like to 779 * start performing input into it. 780 * 781 * @param view The currently focused view, which would like to receive 782 * soft keyboard input. 783 * @param flags Provides additional operating flags. Currently may be 784 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 785 * @param resultReceiver If non-null, this will be called by the IME when 786 * it has processed your request to tell you what it has done. The result 787 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 788 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 789 * {@link #RESULT_HIDDEN}. 790 */ 791 public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { 792 checkFocus(); 793 synchronized (mH) { 794 if (mServedView != view && (mServedView == null 795 || !mServedView.checkInputConnectionProxy(view))) { 796 return false; 797 } 798 799 try { 800 return mService.showSoftInput(mClient, flags, resultReceiver); 801 } catch (RemoteException e) { 802 } 803 804 return false; 805 } 806 } 807 808 /** @hide */ 809 public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) { 810 try { 811 mService.showSoftInput(mClient, flags, resultReceiver); 812 } catch (RemoteException e) { 813 } 814 } 815 816 /** 817 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 818 * input window should only be hidden if it was not explicitly shown 819 * by the user. 820 */ 821 public static final int HIDE_IMPLICIT_ONLY = 0x0001; 822 823 /** 824 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 825 * input window should normally be hidden, unless it was originally 826 * shown with {@link #SHOW_FORCED}. 827 */ 828 public static final int HIDE_NOT_ALWAYS = 0x0002; 829 830 /** 831 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} 832 * without a result: request to hide the soft input window from the 833 * context of the window that is currently accepting input. 834 * 835 * @param windowToken The token of the window that is making the request, 836 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 837 * @param flags Provides additional operating flags. Currently may be 838 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 839 */ 840 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) { 841 return hideSoftInputFromWindow(windowToken, flags, null); 842 } 843 844 /** 845 * Request to hide the soft input window from the context of the window 846 * that is currently accepting input. This should be called as a result 847 * of the user doing some actually than fairly explicitly requests to 848 * have the input window hidden. 849 * 850 * @param windowToken The token of the window that is making the request, 851 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 852 * @param flags Provides additional operating flags. Currently may be 853 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 854 * @param resultReceiver If non-null, this will be called by the IME when 855 * it has processed your request to tell you what it has done. The result 856 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 857 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 858 * {@link #RESULT_HIDDEN}. 859 */ 860 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, 861 ResultReceiver resultReceiver) { 862 checkFocus(); 863 synchronized (mH) { 864 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 865 return false; 866 } 867 868 try { 869 return mService.hideSoftInput(mClient, flags, resultReceiver); 870 } catch (RemoteException e) { 871 } 872 return false; 873 } 874 } 875 876 877 /** 878 * This method toggles the input method window display. 879 * If the input window is already displayed, it gets hidden. 880 * If not the input window will be displayed. 881 * @param windowToken The token of the window that is making the request, 882 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 883 * @param showFlags Provides additional operating flags. May be 884 * 0 or have the {@link #SHOW_IMPLICIT}, 885 * {@link #SHOW_FORCED} bit set. 886 * @param hideFlags Provides additional operating flags. May be 887 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 888 * {@link #HIDE_NOT_ALWAYS} bit set. 889 **/ 890 public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) { 891 synchronized (mH) { 892 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 893 return; 894 } 895 if (mCurMethod != null) { 896 try { 897 mCurMethod.toggleSoftInput(showFlags, hideFlags); 898 } catch (RemoteException e) { 899 } 900 } 901 } 902 } 903 904 /* 905 * This method toggles the input method window display. 906 * If the input window is already displayed, it gets hidden. 907 * If not the input window will be displayed. 908 * @param showFlags Provides additional operating flags. May be 909 * 0 or have the {@link #SHOW_IMPLICIT}, 910 * {@link #SHOW_FORCED} bit set. 911 * @param hideFlags Provides additional operating flags. May be 912 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 913 * {@link #HIDE_NOT_ALWAYS} bit set. 914 * @hide 915 */ 916 public void toggleSoftInput(int showFlags, int hideFlags) { 917 if (mCurMethod != null) { 918 try { 919 mCurMethod.toggleSoftInput(showFlags, hideFlags); 920 } catch (RemoteException e) { 921 } 922 } 923 } 924 925 /** 926 * If the input method is currently connected to the given view, 927 * restart it with its new contents. You should call this when the text 928 * within your view changes outside of the normal input method or key 929 * input flow, such as when an application calls TextView.setText(). 930 * 931 * @param view The view whose text has changed. 932 */ 933 public void restartInput(View view) { 934 checkFocus(); 935 synchronized (mH) { 936 if (mServedView != view && (mServedView == null 937 || !mServedView.checkInputConnectionProxy(view))) { 938 return; 939 } 940 941 mServedConnecting = true; 942 } 943 944 startInputInner(); 945 } 946 947 void startInputInner() { 948 final View view; 949 synchronized (mH) { 950 view = mServedView; 951 952 // Make sure we have a window token for the served view. 953 if (DEBUG) Log.v(TAG, "Starting input: view=" + view); 954 if (view == null) { 955 if (DEBUG) Log.v(TAG, "ABORT input: no served view!"); 956 return; 957 } 958 } 959 960 // Now we need to get an input connection from the served view. 961 // This is complicated in a couple ways: we can't be holding our lock 962 // when calling out to the view, and we need to make sure we call into 963 // the view on the same thread that is driving its view hierarchy. 964 Handler vh = view.getHandler(); 965 if (vh == null) { 966 // If the view doesn't have a handler, something has changed out 967 // from under us, so just bail. 968 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!"); 969 return; 970 } 971 if (vh.getLooper() != Looper.myLooper()) { 972 // The view is running on a different thread than our own, so 973 // we need to reschedule our work for over there. 974 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); 975 vh.post(new Runnable() { 976 public void run() { 977 startInputInner(); 978 } 979 }); 980 return; 981 } 982 983 // Okay we are now ready to call into the served view and have it 984 // do its stuff. 985 // Life is good: let's hook everything up! 986 EditorInfo tba = new EditorInfo(); 987 tba.packageName = view.getContext().getPackageName(); 988 tba.fieldId = view.getId(); 989 InputConnection ic = view.onCreateInputConnection(tba); 990 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic); 991 992 synchronized (mH) { 993 // Now that we are locked again, validate that our state hasn't 994 // changed. 995 if (mServedView != view || !mServedConnecting) { 996 // Something else happened, so abort. 997 if (DEBUG) Log.v(TAG, 998 "Starting input: finished by someone else (view=" 999 + mServedView + " conn=" + mServedConnecting + ")"); 1000 return; 1001 } 1002 1003 // If we already have a text box, then this view is already 1004 // connected so we want to restart it. 1005 final boolean initial = mCurrentTextBoxAttribute == null; 1006 1007 // Hook 'em up and let 'er rip. 1008 mCurrentTextBoxAttribute = tba; 1009 mServedConnecting = false; 1010 mServedInputConnection = ic; 1011 IInputContext servedContext; 1012 if (ic != null) { 1013 mCursorSelStart = tba.initialSelStart; 1014 mCursorSelEnd = tba.initialSelEnd; 1015 mCursorCandStart = -1; 1016 mCursorCandEnd = -1; 1017 mCursorRect.setEmpty(); 1018 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic); 1019 } else { 1020 servedContext = null; 1021 } 1022 1023 try { 1024 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" 1025 + ic + " tba=" + tba + " initial=" + initial); 1026 InputBindResult res = mService.startInput(mClient, 1027 servedContext, tba, initial, true); 1028 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); 1029 if (res != null) { 1030 if (res.id != null) { 1031 mBindSequence = res.sequence; 1032 mCurMethod = res.method; 1033 } else if (mCurMethod == null) { 1034 // This means there is no input method available. 1035 if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); 1036 return; 1037 } 1038 } 1039 if (mCurMethod != null && mCompletions != null) { 1040 try { 1041 mCurMethod.displayCompletions(mCompletions); 1042 } catch (RemoteException e) { 1043 } 1044 } 1045 } catch (RemoteException e) { 1046 Log.w(TAG, "IME died: " + mCurId, e); 1047 } 1048 } 1049 } 1050 1051 /** 1052 * When the focused window is dismissed, this method is called to finish the 1053 * input method started before. 1054 * @hide 1055 */ 1056 public void windowDismissed(IBinder appWindowToken) { 1057 checkFocus(); 1058 synchronized (mH) { 1059 if (mServedView != null && 1060 mServedView.getWindowToken() == appWindowToken) { 1061 finishInputLocked(); 1062 } 1063 } 1064 } 1065 1066 /** 1067 * Call this when a view receives focus. 1068 * @hide 1069 */ 1070 public void focusIn(View view) { 1071 synchronized (mH) { 1072 focusInLocked(view); 1073 } 1074 } 1075 1076 void focusInLocked(View view) { 1077 if (DEBUG) Log.v(TAG, "focusIn: " + view); 1078 1079 if (mCurRootView != view.getRootView()) { 1080 // This is a request from a window that isn't in the window with 1081 // IME focus, so ignore it. 1082 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring"); 1083 return; 1084 } 1085 1086 mNextServedView = view; 1087 scheduleCheckFocusLocked(view); 1088 } 1089 1090 /** 1091 * Call this when a view loses focus. 1092 * @hide 1093 */ 1094 public void focusOut(View view) { 1095 synchronized (mH) { 1096 if (DEBUG) Log.v(TAG, "focusOut: " + view 1097 + " mServedView=" + mServedView 1098 + " winFocus=" + view.hasWindowFocus()); 1099 if (mServedView != view) { 1100 // The following code would auto-hide the IME if we end up 1101 // with no more views with focus. This can happen, however, 1102 // whenever we go into touch mode, so it ends up hiding 1103 // at times when we don't really want it to. For now it 1104 // seems better to just turn it all off. 1105 if (false && view.hasWindowFocus()) { 1106 mNextServedView = null; 1107 scheduleCheckFocusLocked(view); 1108 } 1109 } 1110 } 1111 } 1112 1113 void scheduleCheckFocusLocked(View view) { 1114 Handler vh = view.getHandler(); 1115 if (vh != null && !vh.hasMessages(ViewAncestor.CHECK_FOCUS)) { 1116 // This will result in a call to checkFocus() below. 1117 vh.sendMessage(vh.obtainMessage(ViewAncestor.CHECK_FOCUS)); 1118 } 1119 } 1120 1121 /** 1122 * @hide 1123 */ 1124 public void checkFocus() { 1125 // This is called a lot, so short-circuit before locking. 1126 if (mServedView == mNextServedView && !mNextServedNeedsStart) { 1127 return; 1128 } 1129 1130 InputConnection ic = null; 1131 synchronized (mH) { 1132 if (mServedView == mNextServedView && !mNextServedNeedsStart) { 1133 return; 1134 } 1135 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView 1136 + " next=" + mNextServedView 1137 + " restart=" + mNextServedNeedsStart); 1138 1139 mNextServedNeedsStart = false; 1140 if (mNextServedView == null) { 1141 finishInputLocked(); 1142 // In this case, we used to have a focused view on the window, 1143 // but no longer do. We should make sure the input method is 1144 // no longer shown, since it serves no purpose. 1145 closeCurrentInput(); 1146 return; 1147 } 1148 1149 ic = mServedInputConnection; 1150 1151 mServedView = mNextServedView; 1152 mCurrentTextBoxAttribute = null; 1153 mCompletions = null; 1154 mServedConnecting = true; 1155 } 1156 1157 if (ic != null) { 1158 ic.finishComposingText(); 1159 } 1160 1161 startInputInner(); 1162 } 1163 1164 void closeCurrentInput() { 1165 try { 1166 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null); 1167 } catch (RemoteException e) { 1168 } 1169 } 1170 1171 /** 1172 * Called by ViewAncestor when its window gets input focus. 1173 * @hide 1174 */ 1175 public void onWindowFocus(View rootView, View focusedView, int softInputMode, 1176 boolean first, int windowFlags) { 1177 synchronized (mH) { 1178 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView 1179 + " softInputMode=" + softInputMode 1180 + " first=" + first + " flags=#" 1181 + Integer.toHexString(windowFlags)); 1182 if (mHasBeenInactive) { 1183 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh"); 1184 mHasBeenInactive = false; 1185 mNextServedNeedsStart = true; 1186 } 1187 focusInLocked(focusedView != null ? focusedView : rootView); 1188 } 1189 1190 checkFocus(); 1191 1192 synchronized (mH) { 1193 try { 1194 final boolean isTextEditor = focusedView != null && 1195 focusedView.onCheckIsTextEditor(); 1196 mService.windowGainedFocus(mClient, rootView.getWindowToken(), 1197 focusedView != null, isTextEditor, softInputMode, first, 1198 windowFlags); 1199 } catch (RemoteException e) { 1200 } 1201 } 1202 } 1203 1204 /** @hide */ 1205 public void startGettingWindowFocus(View rootView) { 1206 synchronized (mH) { 1207 mCurRootView = rootView; 1208 } 1209 } 1210 1211 /** 1212 * Report the current selection range. 1213 */ 1214 public void updateSelection(View view, int selStart, int selEnd, 1215 int candidatesStart, int candidatesEnd) { 1216 checkFocus(); 1217 synchronized (mH) { 1218 if ((mServedView != view && (mServedView == null 1219 || !mServedView.checkInputConnectionProxy(view))) 1220 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1221 return; 1222 } 1223 1224 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd 1225 || mCursorCandStart != candidatesStart 1226 || mCursorCandEnd != candidatesEnd) { 1227 if (DEBUG) Log.d(TAG, "updateSelection"); 1228 1229 try { 1230 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod); 1231 mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd, 1232 selStart, selEnd, candidatesStart, candidatesEnd); 1233 mCursorSelStart = selStart; 1234 mCursorSelEnd = selEnd; 1235 mCursorCandStart = candidatesStart; 1236 mCursorCandEnd = candidatesEnd; 1237 } catch (RemoteException e) { 1238 Log.w(TAG, "IME died: " + mCurId, e); 1239 } 1240 } 1241 } 1242 } 1243 1244 /** 1245 * Notify the event when the user tapped or clicked the text view. 1246 */ 1247 public void viewClicked(View view) { 1248 final boolean focusChanged = mServedView != mNextServedView; 1249 checkFocus(); 1250 synchronized (mH) { 1251 if ((mServedView != view && (mServedView == null 1252 || !mServedView.checkInputConnectionProxy(view))) 1253 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1254 return; 1255 } 1256 try { 1257 if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged); 1258 mCurMethod.viewClicked(focusChanged); 1259 } catch (RemoteException e) { 1260 Log.w(TAG, "IME died: " + mCurId, e); 1261 } 1262 } 1263 } 1264 1265 /** 1266 * Returns true if the current input method wants to watch the location 1267 * of the input editor's cursor in its window. 1268 */ 1269 public boolean isWatchingCursor(View view) { 1270 return false; 1271 } 1272 1273 /** 1274 * Report the current cursor location in its window. 1275 */ 1276 public void updateCursor(View view, int left, int top, int right, int bottom) { 1277 checkFocus(); 1278 synchronized (mH) { 1279 if ((mServedView != view && (mServedView == null 1280 || !mServedView.checkInputConnectionProxy(view))) 1281 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1282 return; 1283 } 1284 1285 mTmpCursorRect.set(left, top, right, bottom); 1286 if (!mCursorRect.equals(mTmpCursorRect)) { 1287 if (DEBUG) Log.d(TAG, "updateCursor"); 1288 1289 try { 1290 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); 1291 mCurMethod.updateCursor(mTmpCursorRect); 1292 mCursorRect.set(mTmpCursorRect); 1293 } catch (RemoteException e) { 1294 Log.w(TAG, "IME died: " + mCurId, e); 1295 } 1296 } 1297 } 1298 } 1299 1300 /** 1301 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) 1302 * InputMethodSession.appPrivateCommand()} on the current Input Method. 1303 * @param view Optional View that is sending the command, or null if 1304 * you want to send the command regardless of the view that is attached 1305 * to the input method. 1306 * @param action Name of the command to be performed. This <em>must</em> 1307 * be a scoped name, i.e. prefixed with a package name you own, so that 1308 * different developers will not create conflicting commands. 1309 * @param data Any data to include with the command. 1310 */ 1311 public void sendAppPrivateCommand(View view, String action, Bundle data) { 1312 checkFocus(); 1313 synchronized (mH) { 1314 if ((mServedView != view && (mServedView == null 1315 || !mServedView.checkInputConnectionProxy(view))) 1316 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1317 return; 1318 } 1319 try { 1320 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); 1321 mCurMethod.appPrivateCommand(action, data); 1322 } catch (RemoteException e) { 1323 Log.w(TAG, "IME died: " + mCurId, e); 1324 } 1325 } 1326 } 1327 1328 /** 1329 * Force switch to a new input method component. This can only be called 1330 * from an application or a service which has a token of the currently active input method. 1331 * @param token Supplies the identifying token given to an input method 1332 * when it was started, which allows it to perform this operation on 1333 * itself. 1334 * @param id The unique identifier for the new input method to be switched to. 1335 */ 1336 public void setInputMethod(IBinder token, String id) { 1337 try { 1338 mService.setInputMethod(token, id); 1339 } catch (RemoteException e) { 1340 throw new RuntimeException(e); 1341 } 1342 } 1343 1344 /** 1345 * Force switch to a new input method and subtype. This can only be called 1346 * from an application or a service which has a token of the currently active input method. 1347 * @param token Supplies the identifying token given to an input method 1348 * when it was started, which allows it to perform this operation on 1349 * itself. 1350 * @param id The unique identifier for the new input method to be switched to. 1351 * @param subtype The new subtype of the new input method to be switched to. 1352 */ 1353 public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) { 1354 try { 1355 mService.setInputMethodAndSubtype(token, id, subtype); 1356 } catch (RemoteException e) { 1357 throw new RuntimeException(e); 1358 } 1359 } 1360 1361 /** 1362 * Close/hide the input method's soft input area, so the user no longer 1363 * sees it or can interact with it. This can only be called 1364 * from the currently active input method, as validated by the given token. 1365 * 1366 * @param token Supplies the identifying token given to an input method 1367 * when it was started, which allows it to perform this operation on 1368 * itself. 1369 * @param flags Provides additional operating flags. Currently may be 1370 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 1371 * {@link #HIDE_NOT_ALWAYS} bit set. 1372 */ 1373 public void hideSoftInputFromInputMethod(IBinder token, int flags) { 1374 try { 1375 mService.hideMySoftInput(token, flags); 1376 } catch (RemoteException e) { 1377 throw new RuntimeException(e); 1378 } 1379 } 1380 1381 /** 1382 * Show the input method's soft input area, so the user 1383 * sees the input method window and can interact with it. 1384 * This can only be called from the currently active input method, 1385 * as validated by the given token. 1386 * 1387 * @param token Supplies the identifying token given to an input method 1388 * when it was started, which allows it to perform this operation on 1389 * itself. 1390 * @param flags Provides additional operating flags. Currently may be 1391 * 0 or have the {@link #SHOW_IMPLICIT} or 1392 * {@link #SHOW_FORCED} bit set. 1393 */ 1394 public void showSoftInputFromInputMethod(IBinder token, int flags) { 1395 try { 1396 mService.showMySoftInput(token, flags); 1397 } catch (RemoteException e) { 1398 throw new RuntimeException(e); 1399 } 1400 } 1401 1402 /** 1403 * @hide 1404 */ 1405 public void dispatchKeyEvent(Context context, int seq, KeyEvent key, 1406 IInputMethodCallback callback) { 1407 synchronized (mH) { 1408 if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); 1409 1410 if (mCurMethod == null) { 1411 try { 1412 callback.finishedEvent(seq, false); 1413 } catch (RemoteException e) { 1414 } 1415 return; 1416 } 1417 1418 if (key.getAction() == KeyEvent.ACTION_DOWN 1419 && key.getKeyCode() == KeyEvent.KEYCODE_SYM) { 1420 showInputMethodPicker(); 1421 try { 1422 callback.finishedEvent(seq, true); 1423 } catch (RemoteException e) { 1424 } 1425 return; 1426 } 1427 try { 1428 if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); 1429 mCurMethod.dispatchKeyEvent(seq, key, callback); 1430 } catch (RemoteException e) { 1431 Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); 1432 try { 1433 callback.finishedEvent(seq, false); 1434 } catch (RemoteException ex) { 1435 } 1436 } 1437 } 1438 } 1439 1440 /** 1441 * @hide 1442 */ 1443 void dispatchTrackballEvent(Context context, int seq, MotionEvent motion, 1444 IInputMethodCallback callback) { 1445 synchronized (mH) { 1446 if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); 1447 1448 if (mCurMethod == null || mCurrentTextBoxAttribute == null) { 1449 try { 1450 callback.finishedEvent(seq, false); 1451 } catch (RemoteException e) { 1452 } 1453 return; 1454 } 1455 1456 try { 1457 if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); 1458 mCurMethod.dispatchTrackballEvent(seq, motion, callback); 1459 } catch (RemoteException e) { 1460 Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); 1461 try { 1462 callback.finishedEvent(seq, false); 1463 } catch (RemoteException ex) { 1464 } 1465 } 1466 } 1467 } 1468 1469 public void showInputMethodPicker() { 1470 synchronized (mH) { 1471 try { 1472 mService.showInputMethodPickerFromClient(mClient); 1473 } catch (RemoteException e) { 1474 Log.w(TAG, "IME died: " + mCurId, e); 1475 } 1476 } 1477 } 1478 1479 /** 1480 * Show the settings for enabling subtypes of the specified input method. 1481 * @param imiId An input method, whose subtypes settings will be shown. If imiId is null, 1482 * subtypes of all input methods will be shown. 1483 */ 1484 public void showInputMethodAndSubtypeEnabler(String imiId) { 1485 synchronized (mH) { 1486 try { 1487 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId); 1488 } catch (RemoteException e) { 1489 Log.w(TAG, "IME died: " + mCurId, e); 1490 } 1491 } 1492 } 1493 1494 /** 1495 * Returns the current input method subtype. This subtype is one of the subtypes in 1496 * the current input method. This method returns null when the current input method doesn't 1497 * have any input method subtype. 1498 */ 1499 public InputMethodSubtype getCurrentInputMethodSubtype() { 1500 synchronized (mH) { 1501 try { 1502 return mService.getCurrentInputMethodSubtype(); 1503 } catch (RemoteException e) { 1504 Log.w(TAG, "IME died: " + mCurId, e); 1505 return null; 1506 } 1507 } 1508 } 1509 1510 /** 1511 * Switch to a new input method subtype of the current input method. 1512 * @param subtype A new input method subtype to switch. 1513 * @return true if the current subtype was successfully switched. When the specified subtype is 1514 * null, this method returns false. 1515 */ 1516 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { 1517 synchronized (mH) { 1518 try { 1519 return mService.setCurrentInputMethodSubtype(subtype); 1520 } catch (RemoteException e) { 1521 Log.w(TAG, "IME died: " + mCurId, e); 1522 return false; 1523 } 1524 } 1525 } 1526 1527 /** 1528 * Returns a map of all shortcut input method info and their subtypes. 1529 */ 1530 public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() { 1531 synchronized (mH) { 1532 HashMap<InputMethodInfo, List<InputMethodSubtype>> ret = 1533 new HashMap<InputMethodInfo, List<InputMethodSubtype>>(); 1534 try { 1535 // TODO: We should change the return type from List<Object> to List<Parcelable> 1536 List<Object> info = mService.getShortcutInputMethodsAndSubtypes(); 1537 // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list 1538 ArrayList<InputMethodSubtype> subtypes = null; 1539 final int N = info.size(); 1540 if (info != null && N > 0) { 1541 for (int i = 0; i < N; ++i) { 1542 Object o = info.get(i); 1543 if (o instanceof InputMethodInfo) { 1544 if (ret.containsKey(o)) { 1545 Log.e(TAG, "IMI list already contains the same InputMethod."); 1546 break; 1547 } 1548 subtypes = new ArrayList<InputMethodSubtype>(); 1549 ret.put((InputMethodInfo)o, subtypes); 1550 } else if (subtypes != null && o instanceof InputMethodSubtype) { 1551 subtypes.add((InputMethodSubtype)o); 1552 } 1553 } 1554 } 1555 } catch (RemoteException e) { 1556 Log.w(TAG, "IME died: " + mCurId, e); 1557 } 1558 return ret; 1559 } 1560 } 1561 1562 /** 1563 * Force switch to the last used input method and subtype. If the last input method didn't have 1564 * any subtypes, the framework will simply switch to the last input method with no subtype 1565 * specified. 1566 * @param imeToken Supplies the identifying token given to an input method when it was started, 1567 * which allows it to perform this operation on itself. 1568 * @return true if the current input method and subtype was successfully switched to the last 1569 * used input method and subtype. 1570 */ 1571 public boolean switchToLastInputMethod(IBinder imeToken) { 1572 synchronized (mH) { 1573 try { 1574 return mService.switchToLastInputMethod(imeToken); 1575 } catch (RemoteException e) { 1576 Log.w(TAG, "IME died: " + mCurId, e); 1577 return false; 1578 } 1579 } 1580 } 1581 1582 /** 1583 * Set additional input method subtypes. 1584 * @param imeToken Supplies the identifying token given to an input method. 1585 * @param subtypes subtypes will be added as additional subtypes of the current input method. 1586 * @return true if the additional input method subtypes are successfully added. 1587 */ 1588 public boolean setAdditionalInputMethodSubtypes( 1589 IBinder imeToken, InputMethodSubtype[] subtypes) { 1590 synchronized (mH) { 1591 try { 1592 return mService.setAdditionalInputMethodSubtypes(imeToken, subtypes); 1593 } catch (RemoteException e) { 1594 Log.w(TAG, "IME died: " + mCurId, e); 1595 return false; 1596 } 1597 } 1598 } 1599 1600 public InputMethodSubtype getLastInputMethodSubtype() { 1601 synchronized (mH) { 1602 try { 1603 return mService.getLastInputMethodSubtype(); 1604 } catch (RemoteException e) { 1605 Log.w(TAG, "IME died: " + mCurId, e); 1606 return null; 1607 } 1608 } 1609 } 1610 1611 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { 1612 final Printer p = new PrintWriterPrinter(fout); 1613 p.println("Input method client state for " + this + ":"); 1614 1615 p.println(" mService=" + mService); 1616 p.println(" mMainLooper=" + mMainLooper); 1617 p.println(" mIInputContext=" + mIInputContext); 1618 p.println(" mActive=" + mActive 1619 + " mHasBeenInactive=" + mHasBeenInactive 1620 + " mBindSequence=" + mBindSequence 1621 + " mCurId=" + mCurId); 1622 p.println(" mCurMethod=" + mCurMethod); 1623 p.println(" mCurRootView=" + mCurRootView); 1624 p.println(" mServedView=" + mServedView); 1625 p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart 1626 + " mNextServedView=" + mNextServedView); 1627 p.println(" mServedConnecting=" + mServedConnecting); 1628 if (mCurrentTextBoxAttribute != null) { 1629 p.println(" mCurrentTextBoxAttribute:"); 1630 mCurrentTextBoxAttribute.dump(p, " "); 1631 } else { 1632 p.println(" mCurrentTextBoxAttribute: null"); 1633 } 1634 p.println(" mServedInputConnection=" + mServedInputConnection); 1635 p.println(" mCompletions=" + mCompletions); 1636 p.println(" mCursorRect=" + mCursorRect); 1637 p.println(" mCursorSelStart=" + mCursorSelStart 1638 + " mCursorSelEnd=" + mCursorSelEnd 1639 + " mCursorCandStart=" + mCursorCandStart 1640 + " mCursorCandEnd=" + mCursorCandEnd); 1641 } 1642} 1643