InputMethodManager.java revision 4e4569dab5c75804b01a19b2d6e6101b445c1c68
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 android.content.Context;
20import android.graphics.Rect;
21import android.os.Bundle;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.Looper;
25import android.os.Message;
26import android.os.Parcelable;
27import android.os.RemoteException;
28import android.os.ResultReceiver;
29import android.os.ServiceManager;
30import android.util.Log;
31import android.util.Pair;
32import android.util.PrintWriterPrinter;
33import android.util.Printer;
34import android.view.KeyEvent;
35import android.view.MotionEvent;
36import android.view.View;
37import android.view.ViewRoot;
38import android.view.inputmethod.InputMethodSubtype;
39
40import com.android.internal.os.HandlerCaller;
41import com.android.internal.view.IInputConnectionWrapper;
42import com.android.internal.view.IInputContext;
43import com.android.internal.view.IInputMethodCallback;
44import com.android.internal.view.IInputMethodClient;
45import com.android.internal.view.IInputMethodManager;
46import com.android.internal.view.IInputMethodSession;
47import com.android.internal.view.InputBindResult;
48
49import java.io.FileDescriptor;
50import java.io.PrintWriter;
51import java.util.ArrayList;
52import java.util.List;
53import java.util.concurrent.CountDownLatch;
54import java.util.concurrent.TimeUnit;
55
56/**
57 * Central system API to the overall input method framework (IMF) architecture,
58 * which arbitrates interaction between applications and the current input method.
59 * You can retrieve an instance of this interface with
60 * {@link Context#getSystemService(String) Context.getSystemService()}.
61 *
62 * <p>Topics covered here:
63 * <ol>
64 * <li><a href="#ArchitectureOverview">Architecture Overview</a>
65 * </ol>
66 *
67 * <a name="ArchitectureOverview"></a>
68 * <h3>Architecture Overview</h3>
69 *
70 * <p>There are three primary parties involved in the input method
71 * framework (IMF) architecture:</p>
72 *
73 * <ul>
74 * <li> The <strong>input method manager</strong> as expressed by this class
75 * is the central point of the system that manages interaction between all
76 * other parts.  It is expressed as the client-side API here which exists
77 * in each application context and communicates with a global system service
78 * that manages the interaction across all processes.
79 * <li> An <strong>input method (IME)</strong> implements a particular
80 * interaction model allowing the user to generate text.  The system binds
81 * to the current input method that is use, causing it to be created and run,
82 * and tells it when to hide and show its UI.  Only one IME is running at a time.
83 * <li> Multiple <strong>client applications</strong> arbitrate with the input
84 * method manager for input focus and control over the state of the IME.  Only
85 * one such client is ever active (working with the IME) at a time.
86 * </ul>
87 *
88 *
89 * <a name="Applications"></a>
90 * <h3>Applications</h3>
91 *
92 * <p>In most cases, applications that are using the standard
93 * {@link android.widget.TextView} or its subclasses will have little they need
94 * to do to work well with soft input methods.  The main things you need to
95 * be aware of are:</p>
96 *
97 * <ul>
98 * <li> Properly set the {@link android.R.attr#inputType} if your editable
99 * text views, so that the input method will have enough context to help the
100 * user in entering text into them.
101 * <li> Deal well with losing screen space when the input method is
102 * displayed.  Ideally an application should handle its window being resized
103 * smaller, but it can rely on the system performing panning of the window
104 * if needed.  You should set the {@link android.R.attr#windowSoftInputMode}
105 * attribute on your activity or the corresponding values on windows you
106 * create to help the system determine whether to pan or resize (it will
107 * try to determine this automatically but may get it wrong).
108 * <li> You can also control the preferred soft input state (open, closed, etc)
109 * for your window using the same {@link android.R.attr#windowSoftInputMode}
110 * attribute.
111 * </ul>
112 *
113 * <p>More finer-grained control is available through the APIs here to directly
114 * interact with the IMF and its IME -- either showing or hiding the input
115 * area, letting the user pick an input method, etc.</p>
116 *
117 * <p>For the rare people amongst us writing their own text editors, you
118 * will need to implement {@link android.view.View#onCreateInputConnection}
119 * to return a new instance of your own {@link InputConnection} interface
120 * allowing the IME to interact with your editor.</p>
121 *
122 *
123 * <a name="InputMethods"></a>
124 * <h3>Input Methods</h3>
125 *
126 * <p>An input method (IME) is implemented
127 * as a {@link android.app.Service}, typically deriving from
128 * {@link android.inputmethodservice.InputMethodService}.  It must provide
129 * the core {@link InputMethod} interface, though this is normally handled by
130 * {@link android.inputmethodservice.InputMethodService} and implementors will
131 * only need to deal with the higher-level API there.</p>
132 *
133 * See the {@link android.inputmethodservice.InputMethodService} class for
134 * more information on implementing IMEs.
135 *
136 *
137 * <a name="Security"></a>
138 * <h3>Security</h3>
139 *
140 * <p>There are a lot of security issues associated with input methods,
141 * since they essentially have freedom to completely drive the UI and monitor
142 * everything the user enters.  The Android input method framework also allows
143 * arbitrary third party IMEs, so care must be taken to restrict their
144 * selection and interactions.</p>
145 *
146 * <p>Here are some key points about the security architecture behind the
147 * IMF:</p>
148 *
149 * <ul>
150 * <li> <p>Only the system is allowed to directly access an IME's
151 * {@link InputMethod} interface, via the
152 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission.  This is
153 * enforced in the system by not binding to an input method service that does
154 * not require this permission, so the system can guarantee no other untrusted
155 * clients are accessing the current input method outside of its control.</p>
156 *
157 * <li> <p>There may be many client processes of the IMF, but only one may
158 * be active at a time.  The inactive clients can not interact with key
159 * parts of the IMF through the mechanisms described below.</p>
160 *
161 * <li> <p>Clients of an input method are only given access to its
162 * {@link InputMethodSession} interface.  One instance of this interface is
163 * created for each client, and only calls from the session associated with
164 * the active client will be processed by the current IME.  This is enforced
165 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
166 * IMEs, but must be explicitly handled by an IME that is customizing the
167 * raw {@link InputMethodSession} implementation.</p>
168 *
169 * <li> <p>Only the active client's {@link InputConnection} will accept
170 * operations.  The IMF tells each client process whether it is active, and
171 * the framework enforces that in inactive processes calls on to the current
172 * InputConnection will be ignored.  This ensures that the current IME can
173 * only deliver events and text edits to the UI that the user sees as
174 * being in focus.</p>
175 *
176 * <li> <p>An IME can never interact with an {@link InputConnection} while
177 * the screen is off.  This is enforced by making all clients inactive while
178 * the screen is off, and prevents bad IMEs from driving the UI when the user
179 * can not be aware of its behavior.</p>
180 *
181 * <li> <p>A client application can ask that the system let the user pick a
182 * new IME, but can not programmatically switch to one itself.  This avoids
183 * malicious applications from switching the user to their own IME, which
184 * remains running when the user navigates away to another application.  An
185 * IME, on the other hand, <em>is</em> allowed to programmatically switch
186 * the system to another IME, since it already has full control of user
187 * input.</p>
188 *
189 * <li> <p>The user must explicitly enable a new IME in settings before
190 * they can switch to it, to confirm with the system that they know about it
191 * and want to make it available for use.</p>
192 * </ul>
193 */
194public final class InputMethodManager {
195    static final boolean DEBUG = false;
196    static final String TAG = "InputMethodManager";
197
198    static final Object mInstanceSync = new Object();
199    static InputMethodManager mInstance;
200
201    final IInputMethodManager mService;
202    final Looper mMainLooper;
203
204    // For scheduling work on the main thread.  This also serves as our
205    // global lock.
206    final H mH;
207
208    // Our generic input connection if the current target does not have its own.
209    final IInputContext mIInputContext;
210
211    /**
212     * True if this input method client is active, initially false.
213     */
214    boolean mActive = false;
215
216    /**
217     * Set whenever this client becomes inactive, to know we need to reset
218     * state with the IME then next time we receive focus.
219     */
220    boolean mHasBeenInactive = true;
221
222    /**
223     * As reported by IME through InputConnection.
224     */
225    boolean mFullscreenMode;
226
227    // -----------------------------------------------------------
228
229    /**
230     * This is the root view of the overall window that currently has input
231     * method focus.
232     */
233    View mCurRootView;
234    /**
235     * This is the view that should currently be served by an input method,
236     * regardless of the state of setting that up.
237     */
238    View mServedView;
239    /**
240     * This is then next view that will be served by the input method, when
241     * we get around to updating things.
242     */
243    View mNextServedView;
244    /**
245     * True if we should restart input in the next served view, even if the
246     * view hasn't actually changed from the current serve view.
247     */
248    boolean mNextServedNeedsStart;
249    /**
250     * This is set when we are in the process of connecting, to determine
251     * when we have actually finished.
252     */
253    boolean mServedConnecting;
254    /**
255     * This is non-null when we have connected the served view; it holds
256     * the attributes that were last retrieved from the served view and given
257     * to the input connection.
258     */
259    EditorInfo mCurrentTextBoxAttribute;
260    /**
261     * The InputConnection that was last retrieved from the served view.
262     */
263    InputConnection mServedInputConnection;
264    /**
265     * The completions that were last provided by the served view.
266     */
267    CompletionInfo[] mCompletions;
268
269    // Cursor position on the screen.
270    Rect mTmpCursorRect = new Rect();
271    Rect mCursorRect = new Rect();
272    int mCursorSelStart;
273    int mCursorSelEnd;
274    int mCursorCandStart;
275    int mCursorCandEnd;
276
277    // -----------------------------------------------------------
278
279    /**
280     * Sequence number of this binding, as returned by the server.
281     */
282    int mBindSequence = -1;
283    /**
284     * ID of the method we are bound to.
285     */
286    String mCurId;
287    /**
288     * The actual instance of the method to make calls on it.
289     */
290    IInputMethodSession mCurMethod;
291
292    // -----------------------------------------------------------
293
294    static final int MSG_DUMP = 1;
295    static final int MSG_BIND = 2;
296    static final int MSG_UNBIND = 3;
297    static final int MSG_SET_ACTIVE = 4;
298
299    class H extends Handler {
300        H(Looper looper) {
301            super(looper);
302        }
303
304        @Override
305        public void handleMessage(Message msg) {
306            switch (msg.what) {
307                case MSG_DUMP: {
308                    HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
309                    try {
310                        doDump((FileDescriptor)args.arg1,
311                                (PrintWriter)args.arg2, (String[])args.arg3);
312                    } catch (RuntimeException e) {
313                        ((PrintWriter)args.arg2).println("Exception: " + e);
314                    }
315                    synchronized (args.arg4) {
316                        ((CountDownLatch)args.arg4).countDown();
317                    }
318                    return;
319                }
320                case MSG_BIND: {
321                    final InputBindResult res = (InputBindResult)msg.obj;
322                    synchronized (mH) {
323                        if (mBindSequence < 0 || mBindSequence != res.sequence) {
324                            Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
325                                    + ", given seq=" + res.sequence);
326                            return;
327                        }
328
329                        mCurMethod = res.method;
330                        mCurId = res.id;
331                        mBindSequence = res.sequence;
332                    }
333                    startInputInner();
334                    return;
335                }
336                case MSG_UNBIND: {
337                    final int sequence = msg.arg1;
338                    synchronized (mH) {
339                        if (mBindSequence == sequence) {
340                            if (false) {
341                                // XXX the server has already unbound!
342                                if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
343                                    try {
344                                        mCurMethod.finishInput();
345                                    } catch (RemoteException e) {
346                                        Log.w(TAG, "IME died: " + mCurId, e);
347                                    }
348                                }
349                            }
350                            clearBindingLocked();
351
352                            // If we were actively using the last input method, then
353                            // we would like to re-connect to the next input method.
354                            if (mServedView != null && mServedView.isFocused()) {
355                                mServedConnecting = true;
356                            }
357                        }
358                        startInputInner();
359                    }
360                    return;
361                }
362                case MSG_SET_ACTIVE: {
363                    final boolean active = msg.arg1 != 0;
364                    synchronized (mH) {
365                        mActive = active;
366                        mFullscreenMode = false;
367                        if (!active) {
368                            // Some other client has starting using the IME, so note
369                            // that this happened and make sure our own editor's
370                            // state is reset.
371                            mHasBeenInactive = true;
372                            try {
373                                // Note that finishComposingText() is allowed to run
374                                // even when we are not active.
375                                mIInputContext.finishComposingText();
376                            } catch (RemoteException e) {
377                            }
378                        }
379                    }
380                    return;
381                }
382            }
383        }
384    }
385
386    class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
387        public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
388            super(mainLooper, conn);
389        }
390
391        public boolean isActive() {
392            return mActive;
393        }
394    }
395
396    final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
397        @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
398            // No need to check for dump permission, since we only give this
399            // interface to the system.
400
401            CountDownLatch latch = new CountDownLatch(1);
402            HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs();
403            sargs.arg1 = fd;
404            sargs.arg2 = fout;
405            sargs.arg3 = args;
406            sargs.arg4 = latch;
407            mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
408            try {
409                if (!latch.await(5, TimeUnit.SECONDS)) {
410                    fout.println("Timeout waiting for dump");
411                }
412            } catch (InterruptedException e) {
413                fout.println("Interrupted waiting for dump");
414            }
415        }
416
417        public void setUsingInputMethod(boolean state) {
418        }
419
420        public void onBindMethod(InputBindResult res) {
421            mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
422        }
423
424        public void onUnbindMethod(int sequence) {
425            mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
426        }
427
428        public void setActive(boolean active) {
429            mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
430        }
431    };
432
433    final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
434
435    InputMethodManager(IInputMethodManager service, Looper looper) {
436        mService = service;
437        mMainLooper = looper;
438        mH = new H(looper);
439        mIInputContext = new ControlledInputConnectionWrapper(looper,
440                mDummyInputConnection);
441
442        if (mInstance == null) {
443            mInstance = this;
444        }
445    }
446
447    /**
448     * Retrieve the global InputMethodManager instance, creating it if it
449     * doesn't already exist.
450     * @hide
451     */
452    static public InputMethodManager getInstance(Context context) {
453        return getInstance(context.getMainLooper());
454    }
455
456    /**
457     * Internally, the input method manager can't be context-dependent, so
458     * we have this here for the places that need it.
459     * @hide
460     */
461    static public InputMethodManager getInstance(Looper mainLooper) {
462        synchronized (mInstanceSync) {
463            if (mInstance != null) {
464                return mInstance;
465            }
466            IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
467            IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
468            mInstance = new InputMethodManager(service, mainLooper);
469        }
470        return mInstance;
471    }
472
473    /**
474     * Private optimization: retrieve the global InputMethodManager instance,
475     * if it exists.
476     * @hide
477     */
478    static public InputMethodManager peekInstance() {
479        return mInstance;
480    }
481
482    /** @hide */
483    public IInputMethodClient getClient() {
484        return mClient;
485    }
486
487    /** @hide */
488    public IInputContext getInputContext() {
489        return mIInputContext;
490    }
491
492    public List<InputMethodInfo> getInputMethodList() {
493        try {
494            return mService.getInputMethodList();
495        } catch (RemoteException e) {
496            throw new RuntimeException(e);
497        }
498    }
499
500    public List<InputMethodInfo> getEnabledInputMethodList() {
501        try {
502            return mService.getEnabledInputMethodList();
503        } catch (RemoteException e) {
504            throw new RuntimeException(e);
505        }
506    }
507
508    public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi) {
509        try {
510            return mService.getEnabledInputMethodSubtypeList(imi);
511        } catch (RemoteException e) {
512            throw new RuntimeException(e);
513        }
514    }
515
516    public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
517        try {
518            mService.updateStatusIcon(imeToken, packageName, iconId);
519        } catch (RemoteException e) {
520            throw new RuntimeException(e);
521        }
522    }
523
524    public void hideStatusIcon(IBinder imeToken) {
525        try {
526            mService.updateStatusIcon(imeToken, null, 0);
527        } catch (RemoteException e) {
528            throw new RuntimeException(e);
529        }
530    }
531
532    /** @hide */
533    public void setIMEButtonVisible(IBinder imeToken, boolean visible) {
534        try {
535            mService.setIMEButtonVisible(imeToken, visible);
536        } catch (RemoteException e) {
537            throw new RuntimeException(e);
538        }
539    }
540
541    /** @hide */
542    public void setFullscreenMode(boolean fullScreen) {
543        mFullscreenMode = fullScreen;
544    }
545
546    /**
547     * Allows you to discover whether the attached input method is running
548     * in fullscreen mode.  Return true if it is fullscreen, entirely covering
549     * your UI, else returns false.
550     */
551    public boolean isFullscreenMode() {
552        return mFullscreenMode;
553    }
554
555    /**
556     * Return true if the given view is the currently active view for the
557     * input method.
558     */
559    public boolean isActive(View view) {
560        checkFocus();
561        synchronized (mH) {
562            return (mServedView == view
563                    || (mServedView != null
564                            && mServedView.checkInputConnectionProxy(view)))
565                    && mCurrentTextBoxAttribute != null;
566        }
567    }
568
569    /**
570     * Return true if any view is currently active in the input method.
571     */
572    public boolean isActive() {
573        checkFocus();
574        synchronized (mH) {
575            return mServedView != null && mCurrentTextBoxAttribute != null;
576        }
577    }
578
579    /**
580     * Return true if the currently served view is accepting full text edits.
581     * If false, it has no input connection, so can only handle raw key events.
582     */
583    public boolean isAcceptingText() {
584        checkFocus();
585        return mServedInputConnection != null;
586    }
587
588    /**
589     * Reset all of the state associated with being bound to an input method.
590     */
591    void clearBindingLocked() {
592        clearConnectionLocked();
593        mBindSequence = -1;
594        mCurId = null;
595        mCurMethod = null;
596    }
597
598    /**
599     * Reset all of the state associated with a served view being connected
600     * to an input method
601     */
602    void clearConnectionLocked() {
603        mCurrentTextBoxAttribute = null;
604        mServedInputConnection = null;
605    }
606
607    /**
608     * Disconnect any existing input connection, clearing the served view.
609     */
610    void finishInputLocked() {
611        mNextServedView = null;
612        if (mServedView != null) {
613            if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
614
615            if (mCurrentTextBoxAttribute != null) {
616                try {
617                    mService.finishInput(mClient);
618                } catch (RemoteException e) {
619                }
620            }
621
622            if (mServedInputConnection != null) {
623                // We need to tell the previously served view that it is no
624                // longer the input target, so it can reset its state.  Schedule
625                // this call on its window's Handler so it will be on the correct
626                // thread and outside of our lock.
627                Handler vh = mServedView.getHandler();
628                if (vh != null) {
629                    // This will result in a call to reportFinishInputConnection()
630                    // below.
631                    vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION,
632                            mServedInputConnection));
633                }
634            }
635
636            mServedView = null;
637            mCompletions = null;
638            mServedConnecting = false;
639            clearConnectionLocked();
640        }
641    }
642
643    /**
644     * Called from the FINISH_INPUT_CONNECTION message above.
645     * @hide
646     */
647    public void reportFinishInputConnection(InputConnection ic) {
648        if (mServedInputConnection != ic) {
649            ic.finishComposingText();
650        }
651    }
652
653    public void displayCompletions(View view, CompletionInfo[] completions) {
654        checkFocus();
655        synchronized (mH) {
656            if (mServedView != view && (mServedView == null
657                            || !mServedView.checkInputConnectionProxy(view))) {
658                return;
659            }
660
661            mCompletions = completions;
662            if (mCurMethod != null) {
663                try {
664                    mCurMethod.displayCompletions(mCompletions);
665                } catch (RemoteException e) {
666                }
667            }
668        }
669    }
670
671    public void updateExtractedText(View view, int token, ExtractedText text) {
672        checkFocus();
673        synchronized (mH) {
674            if (mServedView != view && (mServedView == null
675                    || !mServedView.checkInputConnectionProxy(view))) {
676                return;
677            }
678
679            if (mCurMethod != null) {
680                try {
681                    mCurMethod.updateExtractedText(token, text);
682                } catch (RemoteException e) {
683                }
684            }
685        }
686    }
687
688    /**
689     * Flag for {@link #showSoftInput} to indicate that this is an implicit
690     * request to show the input window, not as the result of a direct request
691     * by the user.  The window may not be shown in this case.
692     */
693    public static final int SHOW_IMPLICIT = 0x0001;
694
695    /**
696     * Flag for {@link #showSoftInput} to indicate that the user has forced
697     * the input method open (such as by long-pressing menu) so it should
698     * not be closed until they explicitly do so.
699     */
700    public static final int SHOW_FORCED = 0x0002;
701
702    /**
703     * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
704     * a result receiver: explicitly request that the current input method's
705     * soft input area be shown to the user, if needed.
706     *
707     * @param view The currently focused view, which would like to receive
708     * soft keyboard input.
709     * @param flags Provides additional operating flags.  Currently may be
710     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
711     */
712    public boolean showSoftInput(View view, int flags) {
713        return showSoftInput(view, flags, null);
714    }
715
716    /**
717     * Flag for the {@link ResultReceiver} result code from
718     * {@link #showSoftInput(View, int, ResultReceiver)} and
719     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
720     * state of the soft input window was unchanged and remains shown.
721     */
722    public static final int RESULT_UNCHANGED_SHOWN = 0;
723
724    /**
725     * Flag for the {@link ResultReceiver} result code from
726     * {@link #showSoftInput(View, int, ResultReceiver)} and
727     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
728     * state of the soft input window was unchanged and remains hidden.
729     */
730    public static final int RESULT_UNCHANGED_HIDDEN = 1;
731
732    /**
733     * Flag for the {@link ResultReceiver} result code from
734     * {@link #showSoftInput(View, int, ResultReceiver)} and
735     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
736     * state of the soft input window changed from hidden to shown.
737     */
738    public static final int RESULT_SHOWN = 2;
739
740    /**
741     * Flag for the {@link ResultReceiver} result code from
742     * {@link #showSoftInput(View, int, ResultReceiver)} and
743     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
744     * state of the soft input window changed from shown to hidden.
745     */
746    public static final int RESULT_HIDDEN = 3;
747
748    /**
749     * Explicitly request that the current input method's soft input area be
750     * shown to the user, if needed.  Call this if the user interacts with
751     * your view in such a way that they have expressed they would like to
752     * start performing input into it.
753     *
754     * @param view The currently focused view, which would like to receive
755     * soft keyboard input.
756     * @param flags Provides additional operating flags.  Currently may be
757     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
758     * @param resultReceiver If non-null, this will be called by the IME when
759     * it has processed your request to tell you what it has done.  The result
760     * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
761     * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
762     * {@link #RESULT_HIDDEN}.
763     */
764    public boolean showSoftInput(View view, int flags,
765            ResultReceiver resultReceiver) {
766        checkFocus();
767        synchronized (mH) {
768            if (mServedView != view && (mServedView == null
769                    || !mServedView.checkInputConnectionProxy(view))) {
770                return false;
771            }
772
773            try {
774                return mService.showSoftInput(mClient, flags, resultReceiver);
775            } catch (RemoteException e) {
776            }
777
778            return false;
779        }
780    }
781
782    /** @hide */
783    public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
784        try {
785            mService.showSoftInput(mClient, flags, resultReceiver);
786        } catch (RemoteException e) {
787        }
788    }
789
790    /**
791     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
792     * input window should only be hidden if it was not explicitly shown
793     * by the user.
794     */
795    public static final int HIDE_IMPLICIT_ONLY = 0x0001;
796
797    /**
798     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
799     * input window should normally be hidden, unless it was originally
800     * shown with {@link #SHOW_FORCED}.
801     */
802    public static final int HIDE_NOT_ALWAYS = 0x0002;
803
804    /**
805     * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)
806     * without a result: request to hide the soft input window from the
807     * context of the window that is currently accepting input.
808     *
809     * @param windowToken The token of the window that is making the request,
810     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
811     * @param flags Provides additional operating flags.  Currently may be
812     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
813     */
814    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
815        return hideSoftInputFromWindow(windowToken, flags, null);
816    }
817
818    /**
819     * Request to hide the soft input window from the context of the window
820     * that is currently accepting input.  This should be called as a result
821     * of the user doing some actually than fairly explicitly requests to
822     * have the input window hidden.
823     *
824     * @param windowToken The token of the window that is making the request,
825     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
826     * @param flags Provides additional operating flags.  Currently may be
827     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
828     * @param resultReceiver If non-null, this will be called by the IME when
829     * it has processed your request to tell you what it has done.  The result
830     * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
831     * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
832     * {@link #RESULT_HIDDEN}.
833     */
834    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
835            ResultReceiver resultReceiver) {
836        checkFocus();
837        synchronized (mH) {
838            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
839                return false;
840            }
841
842            try {
843                return mService.hideSoftInput(mClient, flags, resultReceiver);
844            } catch (RemoteException e) {
845            }
846            return false;
847        }
848    }
849
850
851    /**
852     * This method toggles the input method window display.
853     * If the input window is already displayed, it gets hidden.
854     * If not the input window will be displayed.
855     * @param windowToken The token of the window that is making the request,
856     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
857     * @param showFlags Provides additional operating flags.  May be
858     * 0 or have the {@link #SHOW_IMPLICIT},
859     * {@link #SHOW_FORCED} bit set.
860     * @param hideFlags Provides additional operating flags.  May be
861     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
862     * {@link #HIDE_NOT_ALWAYS} bit set.
863     **/
864    public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
865        synchronized (mH) {
866            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
867                return;
868            }
869            if (mCurMethod != null) {
870                try {
871                    mCurMethod.toggleSoftInput(showFlags, hideFlags);
872                } catch (RemoteException e) {
873                }
874            }
875        }
876    }
877
878    /*
879     * This method toggles the input method window display.
880     * If the input window is already displayed, it gets hidden.
881     * If not the input window will be displayed.
882     * @param showFlags Provides additional operating flags.  May be
883     * 0 or have the {@link #SHOW_IMPLICIT},
884     * {@link #SHOW_FORCED} bit set.
885     * @param hideFlags Provides additional operating flags.  May be
886     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
887     * {@link #HIDE_NOT_ALWAYS} bit set.
888     * @hide
889     */
890    public void toggleSoftInput(int showFlags, int hideFlags) {
891        if (mCurMethod != null) {
892            try {
893                mCurMethod.toggleSoftInput(showFlags, hideFlags);
894            } catch (RemoteException e) {
895            }
896        }
897    }
898
899    /**
900     * If the input method is currently connected to the given view,
901     * restart it with its new contents.  You should call this when the text
902     * within your view changes outside of the normal input method or key
903     * input flow, such as when an application calls TextView.setText().
904     *
905     * @param view The view whose text has changed.
906     */
907    public void restartInput(View view) {
908        checkFocus();
909        synchronized (mH) {
910            if (mServedView != view && (mServedView == null
911                    || !mServedView.checkInputConnectionProxy(view))) {
912                return;
913            }
914
915            mServedConnecting = true;
916        }
917
918        startInputInner();
919    }
920
921    void startInputInner() {
922        final View view;
923        synchronized (mH) {
924            view = mServedView;
925
926            // Make sure we have a window token for the served view.
927            if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
928            if (view == null) {
929                if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
930                return;
931            }
932        }
933
934        // Now we need to get an input connection from the served view.
935        // This is complicated in a couple ways: we can't be holding our lock
936        // when calling out to the view, and we need to make sure we call into
937        // the view on the same thread that is driving its view hierarchy.
938        Handler vh = view.getHandler();
939        if (vh == null) {
940            // If the view doesn't have a handler, something has changed out
941            // from under us, so just bail.
942            if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
943            return;
944        }
945        if (vh.getLooper() != Looper.myLooper()) {
946            // The view is running on a different thread than our own, so
947            // we need to reschedule our work for over there.
948            if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
949            vh.post(new Runnable() {
950                public void run() {
951                    startInputInner();
952                }
953            });
954            return;
955        }
956
957        // Okay we are now ready to call into the served view and have it
958        // do its stuff.
959        // Life is good: let's hook everything up!
960        EditorInfo tba = new EditorInfo();
961        tba.packageName = view.getContext().getPackageName();
962        tba.fieldId = view.getId();
963        InputConnection ic = view.onCreateInputConnection(tba);
964        if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
965
966        synchronized (mH) {
967            // Now that we are locked again, validate that our state hasn't
968            // changed.
969            if (mServedView != view || !mServedConnecting) {
970                // Something else happened, so abort.
971                if (DEBUG) Log.v(TAG,
972                        "Starting input: finished by someone else (view="
973                        + mServedView + " conn=" + mServedConnecting + ")");
974                return;
975            }
976
977            // If we already have a text box, then this view is already
978            // connected so we want to restart it.
979            final boolean initial = mCurrentTextBoxAttribute == null;
980
981            // Hook 'em up and let 'er rip.
982            mCurrentTextBoxAttribute = tba;
983            mServedConnecting = false;
984            mServedInputConnection = ic;
985            IInputContext servedContext;
986            if (ic != null) {
987                mCursorSelStart = tba.initialSelStart;
988                mCursorSelEnd = tba.initialSelEnd;
989                mCursorCandStart = -1;
990                mCursorCandEnd = -1;
991                mCursorRect.setEmpty();
992                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
993            } else {
994                servedContext = null;
995            }
996
997            try {
998                if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
999                        + ic + " tba=" + tba + " initial=" + initial);
1000                InputBindResult res = mService.startInput(mClient,
1001                        servedContext, tba, initial, mCurMethod == null);
1002                if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
1003                if (res != null) {
1004                    if (res.id != null) {
1005                        mBindSequence = res.sequence;
1006                        mCurMethod = res.method;
1007                    } else {
1008                        // This means there is no input method available.
1009                        if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1010                        return;
1011                    }
1012                }
1013                if (mCurMethod != null && mCompletions != null) {
1014                    try {
1015                        mCurMethod.displayCompletions(mCompletions);
1016                    } catch (RemoteException e) {
1017                    }
1018                }
1019            } catch (RemoteException e) {
1020                Log.w(TAG, "IME died: " + mCurId, e);
1021            }
1022        }
1023    }
1024
1025    /**
1026     * When the focused window is dismissed, this method is called to finish the
1027     * input method started before.
1028     * @hide
1029     */
1030    public void windowDismissed(IBinder appWindowToken) {
1031        checkFocus();
1032        synchronized (mH) {
1033            if (mServedView != null &&
1034                    mServedView.getWindowToken() == appWindowToken) {
1035                finishInputLocked();
1036            }
1037        }
1038    }
1039
1040    /**
1041     * Call this when a view receives focus.
1042     * @hide
1043     */
1044    public void focusIn(View view) {
1045        synchronized (mH) {
1046            focusInLocked(view);
1047        }
1048    }
1049
1050    void focusInLocked(View view) {
1051        if (DEBUG) Log.v(TAG, "focusIn: " + view);
1052
1053        if (mCurRootView != view.getRootView()) {
1054            // This is a request from a window that isn't in the window with
1055            // IME focus, so ignore it.
1056            if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
1057            return;
1058        }
1059
1060        mNextServedView = view;
1061        scheduleCheckFocusLocked(view);
1062    }
1063
1064    /**
1065     * Call this when a view loses focus.
1066     * @hide
1067     */
1068    public void focusOut(View view) {
1069        synchronized (mH) {
1070            if (DEBUG) Log.v(TAG, "focusOut: " + view
1071                    + " mServedView=" + mServedView
1072                    + " winFocus=" + view.hasWindowFocus());
1073            if (mServedView != view) {
1074                // The following code would auto-hide the IME if we end up
1075                // with no more views with focus.  This can happen, however,
1076                // whenever we go into touch mode, so it ends up hiding
1077                // at times when we don't really want it to.  For now it
1078                // seems better to just turn it all off.
1079                if (false && view.hasWindowFocus()) {
1080                    mNextServedView = null;
1081                    scheduleCheckFocusLocked(view);
1082                }
1083            }
1084        }
1085    }
1086
1087    void scheduleCheckFocusLocked(View view) {
1088        Handler vh = view.getHandler();
1089        if (vh != null && !vh.hasMessages(ViewRoot.CHECK_FOCUS)) {
1090            // This will result in a call to checkFocus() below.
1091            vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS));
1092        }
1093    }
1094
1095    /**
1096     * @hide
1097     */
1098    public void checkFocus() {
1099        // This is called a lot, so short-circuit before locking.
1100        if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1101            return;
1102        }
1103
1104        InputConnection ic = null;
1105        synchronized (mH) {
1106            if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1107                return;
1108            }
1109            if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1110                    + " next=" + mNextServedView
1111                    + " restart=" + mNextServedNeedsStart);
1112
1113            mNextServedNeedsStart = false;
1114            if (mNextServedView == null) {
1115                finishInputLocked();
1116                // In this case, we used to have a focused view on the window,
1117                // but no longer do.  We should make sure the input method is
1118                // no longer shown, since it serves no purpose.
1119                closeCurrentInput();
1120                return;
1121            }
1122
1123            ic = mServedInputConnection;
1124
1125            mServedView = mNextServedView;
1126            mCurrentTextBoxAttribute = null;
1127            mCompletions = null;
1128            mServedConnecting = true;
1129        }
1130
1131        if (ic != null) {
1132            ic.finishComposingText();
1133        }
1134
1135        startInputInner();
1136    }
1137
1138    void closeCurrentInput() {
1139        try {
1140            mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
1141        } catch (RemoteException e) {
1142        }
1143    }
1144
1145    /**
1146     * Called by ViewRoot when its window gets input focus.
1147     * @hide
1148     */
1149    public void onWindowFocus(View rootView, View focusedView, int softInputMode,
1150            boolean first, int windowFlags) {
1151        synchronized (mH) {
1152            if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1153                    + " softInputMode=" + softInputMode
1154                    + " first=" + first + " flags=#"
1155                    + Integer.toHexString(windowFlags));
1156            if (mHasBeenInactive) {
1157                if (DEBUG) Log.v(TAG, "Has been inactive!  Starting fresh");
1158                mHasBeenInactive = false;
1159                mNextServedNeedsStart = true;
1160            }
1161            focusInLocked(focusedView != null ? focusedView : rootView);
1162        }
1163
1164        checkFocus();
1165
1166        synchronized (mH) {
1167            try {
1168                final boolean isTextEditor = focusedView != null &&
1169                        focusedView.onCheckIsTextEditor();
1170                mService.windowGainedFocus(mClient, rootView.getWindowToken(),
1171                        focusedView != null, isTextEditor, softInputMode, first,
1172                        windowFlags);
1173            } catch (RemoteException e) {
1174            }
1175        }
1176    }
1177
1178    /** @hide */
1179    public void startGettingWindowFocus(View rootView) {
1180        synchronized (mH) {
1181            mCurRootView = rootView;
1182        }
1183    }
1184
1185    /**
1186     * Report the current selection range.
1187     */
1188    public void updateSelection(View view, int selStart, int selEnd,
1189            int candidatesStart, int candidatesEnd) {
1190        checkFocus();
1191        synchronized (mH) {
1192            if ((mServedView != view && (mServedView == null
1193                        || !mServedView.checkInputConnectionProxy(view)))
1194                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1195                return;
1196            }
1197
1198            if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1199                    || mCursorCandStart != candidatesStart
1200                    || mCursorCandEnd != candidatesEnd) {
1201                if (DEBUG) Log.d(TAG, "updateSelection");
1202
1203                try {
1204                    if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
1205                    mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
1206                            selStart, selEnd, candidatesStart, candidatesEnd);
1207                    mCursorSelStart = selStart;
1208                    mCursorSelEnd = selEnd;
1209                    mCursorCandStart = candidatesStart;
1210                    mCursorCandEnd = candidatesEnd;
1211                } catch (RemoteException e) {
1212                    Log.w(TAG, "IME died: " + mCurId, e);
1213                }
1214            }
1215        }
1216    }
1217
1218    /**
1219     * Returns true if the current input method wants to watch the location
1220     * of the input editor's cursor in its window.
1221     */
1222    public boolean isWatchingCursor(View view) {
1223        return false;
1224    }
1225
1226    /**
1227     * Report the current cursor location in its window.
1228     */
1229    public void updateCursor(View view, int left, int top, int right, int bottom) {
1230        checkFocus();
1231        synchronized (mH) {
1232            if ((mServedView != view && (mServedView == null
1233                        || !mServedView.checkInputConnectionProxy(view)))
1234                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1235                return;
1236            }
1237
1238            mTmpCursorRect.set(left, top, right, bottom);
1239            if (!mCursorRect.equals(mTmpCursorRect)) {
1240                if (DEBUG) Log.d(TAG, "updateCursor");
1241
1242                try {
1243                    if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1244                    mCurMethod.updateCursor(mTmpCursorRect);
1245                    mCursorRect.set(mTmpCursorRect);
1246                } catch (RemoteException e) {
1247                    Log.w(TAG, "IME died: " + mCurId, e);
1248                }
1249            }
1250        }
1251    }
1252
1253    /**
1254     * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1255     * InputMethodSession.appPrivateCommand()} on the current Input Method.
1256     * @param view Optional View that is sending the command, or null if
1257     * you want to send the command regardless of the view that is attached
1258     * to the input method.
1259     * @param action Name of the command to be performed.  This <em>must</em>
1260     * be a scoped name, i.e. prefixed with a package name you own, so that
1261     * different developers will not create conflicting commands.
1262     * @param data Any data to include with the command.
1263     */
1264    public void sendAppPrivateCommand(View view, String action, Bundle data) {
1265        checkFocus();
1266        synchronized (mH) {
1267            if ((mServedView != view && (mServedView == null
1268                        || !mServedView.checkInputConnectionProxy(view)))
1269                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1270                return;
1271            }
1272            try {
1273                if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1274                mCurMethod.appPrivateCommand(action, data);
1275            } catch (RemoteException e) {
1276                Log.w(TAG, "IME died: " + mCurId, e);
1277            }
1278        }
1279    }
1280
1281    /**
1282     * Force switch to a new input method component.  This can only be called
1283     * from the currently active input method, as validated by the given token.
1284     * @param token Supplies the identifying token given to an input method
1285     * when it was started, which allows it to perform this operation on
1286     * itself.
1287     * @param id The unique identifier for the new input method to be switched to.
1288     */
1289    public void setInputMethod(IBinder token, String id) {
1290        try {
1291            mService.setInputMethod(token, id);
1292        } catch (RemoteException e) {
1293            throw new RuntimeException(e);
1294        }
1295    }
1296
1297    /**
1298     * Close/hide the input method's soft input area, so the user no longer
1299     * sees it or can interact with it.  This can only be called
1300     * from the currently active input method, as validated by the given token.
1301     *
1302     * @param token Supplies the identifying token given to an input method
1303     * when it was started, which allows it to perform this operation on
1304     * itself.
1305     * @param flags Provides additional operating flags.  Currently may be
1306     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1307     * {@link #HIDE_NOT_ALWAYS} bit set.
1308     */
1309    public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1310        try {
1311            mService.hideMySoftInput(token, flags);
1312        } catch (RemoteException e) {
1313            throw new RuntimeException(e);
1314        }
1315    }
1316
1317    /**
1318     * Show the input method's soft input area, so the user
1319     * sees the input method window and can interact with it.
1320     * This can only be called from the currently active input method,
1321     * as validated by the given token.
1322     *
1323     * @param token Supplies the identifying token given to an input method
1324     * when it was started, which allows it to perform this operation on
1325     * itself.
1326     * @param flags Provides additional operating flags.  Currently may be
1327     * 0 or have the {@link #SHOW_IMPLICIT} or
1328     * {@link #SHOW_FORCED} bit set.
1329     */
1330    public void showSoftInputFromInputMethod(IBinder token, int flags) {
1331        try {
1332            mService.showMySoftInput(token, flags);
1333        } catch (RemoteException e) {
1334            throw new RuntimeException(e);
1335        }
1336    }
1337
1338    /**
1339     * @hide
1340     */
1341    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
1342            IInputMethodCallback callback) {
1343        synchronized (mH) {
1344            if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
1345
1346            if (mCurMethod == null) {
1347                try {
1348                    callback.finishedEvent(seq, false);
1349                } catch (RemoteException e) {
1350                }
1351                return;
1352            }
1353
1354            if (key.getAction() == KeyEvent.ACTION_DOWN
1355                    && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
1356                showInputMethodPicker();
1357                try {
1358                    callback.finishedEvent(seq, true);
1359                } catch (RemoteException e) {
1360                }
1361                return;
1362            }
1363            try {
1364                if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
1365                mCurMethod.dispatchKeyEvent(seq, key, callback);
1366            } catch (RemoteException e) {
1367                Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
1368                try {
1369                    callback.finishedEvent(seq, false);
1370                } catch (RemoteException ex) {
1371                }
1372            }
1373        }
1374    }
1375
1376    /**
1377     * @hide
1378     */
1379    void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
1380            IInputMethodCallback callback) {
1381        synchronized (mH) {
1382            if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
1383
1384            if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
1385                try {
1386                    callback.finishedEvent(seq, false);
1387                } catch (RemoteException e) {
1388                }
1389                return;
1390            }
1391
1392            try {
1393                if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
1394                mCurMethod.dispatchTrackballEvent(seq, motion, callback);
1395            } catch (RemoteException e) {
1396                Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
1397                try {
1398                    callback.finishedEvent(seq, false);
1399                } catch (RemoteException ex) {
1400                }
1401            }
1402        }
1403    }
1404
1405    public void showInputMethodPicker() {
1406        synchronized (mH) {
1407            try {
1408                mService.showInputMethodPickerFromClient(mClient);
1409            } catch (RemoteException e) {
1410                Log.w(TAG, "IME died: " + mCurId, e);
1411            }
1412        }
1413    }
1414
1415    public void showInputMethodSubtypePicker() {
1416        synchronized (mH) {
1417            try {
1418                mService.showInputMethodSubtypePickerFromClient(mClient);
1419            } catch (RemoteException e) {
1420                Log.w(TAG, "IME died: " + mCurId, e);
1421            }
1422        }
1423    }
1424
1425    public void showInputMethodAndSubtypeEnabler(String topId) {
1426        synchronized (mH) {
1427            try {
1428                mService.showInputMethodAndSubtypeEnablerFromClient(mClient, topId);
1429            } catch (RemoteException e) {
1430                Log.w(TAG, "IME died: " + mCurId, e);
1431            }
1432        }
1433    }
1434
1435    public InputMethodSubtype getCurrentInputMethodSubtype() {
1436        synchronized (mH) {
1437            try {
1438                return mService.getCurrentInputMethodSubtype();
1439            } catch (RemoteException e) {
1440                Log.w(TAG, "IME died: " + mCurId, e);
1441                return null;
1442            }
1443        }
1444    }
1445
1446    public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
1447        synchronized (mH) {
1448            try {
1449                return mService.setCurrentInputMethodSubtype(subtype);
1450            } catch (RemoteException e) {
1451                Log.w(TAG, "IME died: " + mCurId, e);
1452                return false;
1453            }
1454        }
1455    }
1456
1457    public List<Pair<InputMethodInfo, InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
1458        synchronized (mH) {
1459            List<Pair<InputMethodInfo, InputMethodSubtype>> ret =
1460                    new ArrayList<Pair<InputMethodInfo, InputMethodSubtype>>();
1461            try {
1462                // TODO: We should change the return type from List<Object> to List<Parcelable>
1463                List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
1464                // "info" has imi1, subtype1, imi2, subtype2, imi3, subtype3,..... in the list
1465                Object imi;
1466                Object subtype;
1467                if (info != null && info.size() > 0) {
1468                    final int N = info.size();
1469                    if (N % 2 == 0) {
1470                        for (int i = 0; i < N;) {
1471                            if ((imi = info.get(i++)) instanceof InputMethodInfo) {
1472                                subtype = info.get(i++);
1473                                ret.add(new Pair<InputMethodInfo, InputMethodSubtype> (
1474                                        (InputMethodInfo)imi,
1475                                        (subtype instanceof InputMethodSubtype) ?
1476                                                (InputMethodSubtype)subtype : null));
1477                            }
1478                        }
1479                    } else {
1480                        Log.w(TAG, "The size of list was illegal.");
1481                    }
1482                }
1483            } catch (RemoteException e) {
1484                Log.w(TAG, "IME died: " + mCurId, e);
1485            }
1486            return ret;
1487        }
1488    }
1489    public boolean switchToLastInputMethod(IBinder imeToken) {
1490        synchronized (mH) {
1491            try {
1492                return mService.switchToLastInputMethod(imeToken);
1493            } catch (RemoteException e) {
1494                Log.w(TAG, "IME died: " + mCurId, e);
1495                return false;
1496            }
1497        }
1498    }
1499
1500    void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1501        final Printer p = new PrintWriterPrinter(fout);
1502        p.println("Input method client state for " + this + ":");
1503
1504        p.println("  mService=" + mService);
1505        p.println("  mMainLooper=" + mMainLooper);
1506        p.println("  mIInputContext=" + mIInputContext);
1507        p.println("  mActive=" + mActive
1508                + " mHasBeenInactive=" + mHasBeenInactive
1509                + " mBindSequence=" + mBindSequence
1510                + " mCurId=" + mCurId);
1511        p.println("  mCurMethod=" + mCurMethod);
1512        p.println("  mCurRootView=" + mCurRootView);
1513        p.println("  mServedView=" + mServedView);
1514        p.println("  mNextServedNeedsStart=" + mNextServedNeedsStart
1515                + " mNextServedView=" + mNextServedView);
1516        p.println("  mServedConnecting=" + mServedConnecting);
1517        if (mCurrentTextBoxAttribute != null) {
1518            p.println("  mCurrentTextBoxAttribute:");
1519            mCurrentTextBoxAttribute.dump(p, "    ");
1520        } else {
1521            p.println("  mCurrentTextBoxAttribute: null");
1522        }
1523        p.println("  mServedInputConnection=" + mServedInputConnection);
1524        p.println("  mCompletions=" + mCompletions);
1525        p.println("  mCursorRect=" + mCursorRect);
1526        p.println("  mCursorSelStart=" + mCursorSelStart
1527                + " mCursorSelEnd=" + mCursorSelEnd
1528                + " mCursorCandStart=" + mCursorCandStart
1529                + " mCursorCandEnd=" + mCursorCandEnd);
1530    }
1531}
1532