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