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