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