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