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