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