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