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