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