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