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