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