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