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