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