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