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