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