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