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