InputMethodManager.java revision c6cc0f8c19d9eccf408a443fa2bf668af261dcd0
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.ViewAncestor;
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    /**
509     * Returns a list of enabled input method subtypes for the specified input method info.
510     * @param imi An input method info whose subtypes list will be returned.
511     * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly
512     * selected subtypes. If an input method info doesn't have enabled subtypes, the framework
513     * will implicitly enable subtypes according to the current system language.
514     */
515    public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
516            boolean allowsImplicitlySelectedSubtypes) {
517        try {
518            return mService.getEnabledInputMethodSubtypeList(imi, allowsImplicitlySelectedSubtypes);
519        } catch (RemoteException e) {
520            throw new RuntimeException(e);
521        }
522    }
523
524    public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
525        try {
526            mService.updateStatusIcon(imeToken, packageName, iconId);
527        } catch (RemoteException e) {
528            throw new RuntimeException(e);
529        }
530    }
531
532    public void hideStatusIcon(IBinder imeToken) {
533        try {
534            mService.updateStatusIcon(imeToken, null, 0);
535        } catch (RemoteException e) {
536            throw new RuntimeException(e);
537        }
538    }
539
540    /** @hide */
541    public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
542        try {
543            mService.setImeWindowStatus(imeToken, vis, backDisposition);
544        } catch (RemoteException e) {
545            throw new RuntimeException(e);
546        }
547    }
548
549    /** @hide */
550    public void setFullscreenMode(boolean fullScreen) {
551        mFullscreenMode = fullScreen;
552    }
553
554    /**
555     * Allows you to discover whether the attached input method is running
556     * in fullscreen mode.  Return true if it is fullscreen, entirely covering
557     * your UI, else returns false.
558     */
559    public boolean isFullscreenMode() {
560        return mFullscreenMode;
561    }
562
563    /**
564     * Return true if the given view is the currently active view for the
565     * input method.
566     */
567    public boolean isActive(View view) {
568        checkFocus();
569        synchronized (mH) {
570            return (mServedView == view
571                    || (mServedView != null
572                            && mServedView.checkInputConnectionProxy(view)))
573                    && mCurrentTextBoxAttribute != null;
574        }
575    }
576
577    /**
578     * Return true if any view is currently active in the input method.
579     */
580    public boolean isActive() {
581        checkFocus();
582        synchronized (mH) {
583            return mServedView != null && mCurrentTextBoxAttribute != null;
584        }
585    }
586
587    /**
588     * Return true if the currently served view is accepting full text edits.
589     * If false, it has no input connection, so can only handle raw key events.
590     */
591    public boolean isAcceptingText() {
592        checkFocus();
593        return mServedInputConnection != null;
594    }
595
596    /**
597     * Reset all of the state associated with being bound to an input method.
598     */
599    void clearBindingLocked() {
600        clearConnectionLocked();
601        mBindSequence = -1;
602        mCurId = null;
603        mCurMethod = null;
604    }
605
606    /**
607     * Reset all of the state associated with a served view being connected
608     * to an input method
609     */
610    void clearConnectionLocked() {
611        mCurrentTextBoxAttribute = null;
612        mServedInputConnection = null;
613    }
614
615    /**
616     * Disconnect any existing input connection, clearing the served view.
617     */
618    void finishInputLocked() {
619        mNextServedView = null;
620        if (mServedView != null) {
621            if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
622
623            if (mCurrentTextBoxAttribute != null) {
624                try {
625                    mService.finishInput(mClient);
626                } catch (RemoteException e) {
627                }
628            }
629
630            if (mServedInputConnection != null) {
631                // We need to tell the previously served view that it is no
632                // longer the input target, so it can reset its state.  Schedule
633                // this call on its window's Handler so it will be on the correct
634                // thread and outside of our lock.
635                Handler vh = mServedView.getHandler();
636                if (vh != null) {
637                    // This will result in a call to reportFinishInputConnection()
638                    // below.
639                    vh.sendMessage(vh.obtainMessage(ViewAncestor.FINISH_INPUT_CONNECTION,
640                            mServedInputConnection));
641                }
642            }
643
644            mServedView = null;
645            mCompletions = null;
646            mServedConnecting = false;
647            clearConnectionLocked();
648        }
649    }
650
651    /**
652     * Called from the FINISH_INPUT_CONNECTION message above.
653     * @hide
654     */
655    public void reportFinishInputConnection(InputConnection ic) {
656        if (mServedInputConnection != ic) {
657            ic.finishComposingText();
658        }
659    }
660
661    public void displayCompletions(View view, CompletionInfo[] completions) {
662        checkFocus();
663        synchronized (mH) {
664            if (mServedView != view && (mServedView == null
665                            || !mServedView.checkInputConnectionProxy(view))) {
666                return;
667            }
668
669            mCompletions = completions;
670            if (mCurMethod != null) {
671                try {
672                    mCurMethod.displayCompletions(mCompletions);
673                } catch (RemoteException e) {
674                }
675            }
676        }
677    }
678
679    public void updateExtractedText(View view, int token, ExtractedText text) {
680        checkFocus();
681        synchronized (mH) {
682            if (mServedView != view && (mServedView == null
683                    || !mServedView.checkInputConnectionProxy(view))) {
684                return;
685            }
686
687            if (mCurMethod != null) {
688                try {
689                    mCurMethod.updateExtractedText(token, text);
690                } catch (RemoteException e) {
691                }
692            }
693        }
694    }
695
696    /**
697     * Flag for {@link #showSoftInput} to indicate that this is an implicit
698     * request to show the input window, not as the result of a direct request
699     * by the user.  The window may not be shown in this case.
700     */
701    public static final int SHOW_IMPLICIT = 0x0001;
702
703    /**
704     * Flag for {@link #showSoftInput} to indicate that the user has forced
705     * the input method open (such as by long-pressing menu) so it should
706     * not be closed until they explicitly do so.
707     */
708    public static final int SHOW_FORCED = 0x0002;
709
710    /**
711     * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
712     * a result receiver: explicitly request that the current input method's
713     * soft input area be shown to the user, if needed.
714     *
715     * @param view The currently focused view, which would like to receive
716     * soft keyboard input.
717     * @param flags Provides additional operating flags.  Currently may be
718     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
719     */
720    public boolean showSoftInput(View view, int flags) {
721        return showSoftInput(view, flags, null);
722    }
723
724    /**
725     * Flag for the {@link ResultReceiver} result code from
726     * {@link #showSoftInput(View, int, ResultReceiver)} and
727     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
728     * state of the soft input window was unchanged and remains shown.
729     */
730    public static final int RESULT_UNCHANGED_SHOWN = 0;
731
732    /**
733     * Flag for the {@link ResultReceiver} result code from
734     * {@link #showSoftInput(View, int, ResultReceiver)} and
735     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
736     * state of the soft input window was unchanged and remains hidden.
737     */
738    public static final int RESULT_UNCHANGED_HIDDEN = 1;
739
740    /**
741     * Flag for the {@link ResultReceiver} result code from
742     * {@link #showSoftInput(View, int, ResultReceiver)} and
743     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
744     * state of the soft input window changed from hidden to shown.
745     */
746    public static final int RESULT_SHOWN = 2;
747
748    /**
749     * Flag for the {@link ResultReceiver} result code from
750     * {@link #showSoftInput(View, int, ResultReceiver)} and
751     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
752     * state of the soft input window changed from shown to hidden.
753     */
754    public static final int RESULT_HIDDEN = 3;
755
756    /**
757     * Explicitly request that the current input method's soft input area be
758     * shown to the user, if needed.  Call this if the user interacts with
759     * your view in such a way that they have expressed they would like to
760     * start performing input into it.
761     *
762     * @param view The currently focused view, which would like to receive
763     * soft keyboard input.
764     * @param flags Provides additional operating flags.  Currently may be
765     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
766     * @param resultReceiver If non-null, this will be called by the IME when
767     * it has processed your request to tell you what it has done.  The result
768     * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
769     * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
770     * {@link #RESULT_HIDDEN}.
771     */
772    public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
773        checkFocus();
774        synchronized (mH) {
775            if (mServedView != view && (mServedView == null
776                    || !mServedView.checkInputConnectionProxy(view))) {
777                return false;
778            }
779
780            try {
781                return mService.showSoftInput(mClient, flags, resultReceiver);
782            } catch (RemoteException e) {
783            }
784
785            return false;
786        }
787    }
788
789    /** @hide */
790    public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
791        try {
792            mService.showSoftInput(mClient, flags, resultReceiver);
793        } catch (RemoteException e) {
794        }
795    }
796
797    /**
798     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
799     * input window should only be hidden if it was not explicitly shown
800     * by the user.
801     */
802    public static final int HIDE_IMPLICIT_ONLY = 0x0001;
803
804    /**
805     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
806     * input window should normally be hidden, unless it was originally
807     * shown with {@link #SHOW_FORCED}.
808     */
809    public static final int HIDE_NOT_ALWAYS = 0x0002;
810
811    /**
812     * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}
813     * without a result: request to hide the soft input window from the
814     * context of the window that is currently accepting input.
815     *
816     * @param windowToken The token of the window that is making the request,
817     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
818     * @param flags Provides additional operating flags.  Currently may be
819     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
820     */
821    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
822        return hideSoftInputFromWindow(windowToken, flags, null);
823    }
824
825    /**
826     * Request to hide the soft input window from the context of the window
827     * that is currently accepting input.  This should be called as a result
828     * of the user doing some actually than fairly explicitly requests to
829     * have the input window hidden.
830     *
831     * @param windowToken The token of the window that is making the request,
832     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
833     * @param flags Provides additional operating flags.  Currently may be
834     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
835     * @param resultReceiver If non-null, this will be called by the IME when
836     * it has processed your request to tell you what it has done.  The result
837     * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
838     * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
839     * {@link #RESULT_HIDDEN}.
840     */
841    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
842            ResultReceiver resultReceiver) {
843        checkFocus();
844        synchronized (mH) {
845            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
846                return false;
847            }
848
849            try {
850                return mService.hideSoftInput(mClient, flags, resultReceiver);
851            } catch (RemoteException e) {
852            }
853            return false;
854        }
855    }
856
857
858    /**
859     * This method toggles the input method window display.
860     * If the input window is already displayed, it gets hidden.
861     * If not the input window will be displayed.
862     * @param windowToken The token of the window that is making the request,
863     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
864     * @param showFlags Provides additional operating flags.  May be
865     * 0 or have the {@link #SHOW_IMPLICIT},
866     * {@link #SHOW_FORCED} bit set.
867     * @param hideFlags Provides additional operating flags.  May be
868     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
869     * {@link #HIDE_NOT_ALWAYS} bit set.
870     **/
871    public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
872        synchronized (mH) {
873            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
874                return;
875            }
876            if (mCurMethod != null) {
877                try {
878                    mCurMethod.toggleSoftInput(showFlags, hideFlags);
879                } catch (RemoteException e) {
880                }
881            }
882        }
883    }
884
885    /*
886     * This method toggles the input method window display.
887     * If the input window is already displayed, it gets hidden.
888     * If not the input window will be displayed.
889     * @param showFlags Provides additional operating flags.  May be
890     * 0 or have the {@link #SHOW_IMPLICIT},
891     * {@link #SHOW_FORCED} bit set.
892     * @param hideFlags Provides additional operating flags.  May be
893     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
894     * {@link #HIDE_NOT_ALWAYS} bit set.
895     * @hide
896     */
897    public void toggleSoftInput(int showFlags, int hideFlags) {
898        if (mCurMethod != null) {
899            try {
900                mCurMethod.toggleSoftInput(showFlags, hideFlags);
901            } catch (RemoteException e) {
902            }
903        }
904    }
905
906    /**
907     * If the input method is currently connected to the given view,
908     * restart it with its new contents.  You should call this when the text
909     * within your view changes outside of the normal input method or key
910     * input flow, such as when an application calls TextView.setText().
911     *
912     * @param view The view whose text has changed.
913     */
914    public void restartInput(View view) {
915        checkFocus();
916        synchronized (mH) {
917            if (mServedView != view && (mServedView == null
918                    || !mServedView.checkInputConnectionProxy(view))) {
919                return;
920            }
921
922            mServedConnecting = true;
923        }
924
925        startInputInner();
926    }
927
928    void startInputInner() {
929        final View view;
930        synchronized (mH) {
931            view = mServedView;
932
933            // Make sure we have a window token for the served view.
934            if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
935            if (view == null) {
936                if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
937                return;
938            }
939        }
940
941        // Now we need to get an input connection from the served view.
942        // This is complicated in a couple ways: we can't be holding our lock
943        // when calling out to the view, and we need to make sure we call into
944        // the view on the same thread that is driving its view hierarchy.
945        Handler vh = view.getHandler();
946        if (vh == null) {
947            // If the view doesn't have a handler, something has changed out
948            // from under us, so just bail.
949            if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
950            return;
951        }
952        if (vh.getLooper() != Looper.myLooper()) {
953            // The view is running on a different thread than our own, so
954            // we need to reschedule our work for over there.
955            if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
956            vh.post(new Runnable() {
957                public void run() {
958                    startInputInner();
959                }
960            });
961            return;
962        }
963
964        // Okay we are now ready to call into the served view and have it
965        // do its stuff.
966        // Life is good: let's hook everything up!
967        EditorInfo tba = new EditorInfo();
968        tba.packageName = view.getContext().getPackageName();
969        tba.fieldId = view.getId();
970        InputConnection ic = view.onCreateInputConnection(tba);
971        if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
972
973        synchronized (mH) {
974            // Now that we are locked again, validate that our state hasn't
975            // changed.
976            if (mServedView != view || !mServedConnecting) {
977                // Something else happened, so abort.
978                if (DEBUG) Log.v(TAG,
979                        "Starting input: finished by someone else (view="
980                        + mServedView + " conn=" + mServedConnecting + ")");
981                return;
982            }
983
984            // If we already have a text box, then this view is already
985            // connected so we want to restart it.
986            final boolean initial = mCurrentTextBoxAttribute == null;
987
988            // Hook 'em up and let 'er rip.
989            mCurrentTextBoxAttribute = tba;
990            mServedConnecting = false;
991            mServedInputConnection = ic;
992            IInputContext servedContext;
993            if (ic != null) {
994                mCursorSelStart = tba.initialSelStart;
995                mCursorSelEnd = tba.initialSelEnd;
996                mCursorCandStart = -1;
997                mCursorCandEnd = -1;
998                mCursorRect.setEmpty();
999                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
1000            } else {
1001                servedContext = null;
1002            }
1003
1004            try {
1005                if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
1006                        + ic + " tba=" + tba + " initial=" + initial);
1007                InputBindResult res = mService.startInput(mClient,
1008                        servedContext, tba, initial, mCurMethod == null);
1009                if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
1010                if (res != null) {
1011                    if (res.id != null) {
1012                        mBindSequence = res.sequence;
1013                        mCurMethod = res.method;
1014                    } else {
1015                        // This means there is no input method available.
1016                        if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1017                        return;
1018                    }
1019                }
1020                if (mCurMethod != null && mCompletions != null) {
1021                    try {
1022                        mCurMethod.displayCompletions(mCompletions);
1023                    } catch (RemoteException e) {
1024                    }
1025                }
1026            } catch (RemoteException e) {
1027                Log.w(TAG, "IME died: " + mCurId, e);
1028            }
1029        }
1030    }
1031
1032    /**
1033     * When the focused window is dismissed, this method is called to finish the
1034     * input method started before.
1035     * @hide
1036     */
1037    public void windowDismissed(IBinder appWindowToken) {
1038        checkFocus();
1039        synchronized (mH) {
1040            if (mServedView != null &&
1041                    mServedView.getWindowToken() == appWindowToken) {
1042                finishInputLocked();
1043            }
1044        }
1045    }
1046
1047    /**
1048     * Call this when a view receives focus.
1049     * @hide
1050     */
1051    public void focusIn(View view) {
1052        synchronized (mH) {
1053            focusInLocked(view);
1054        }
1055    }
1056
1057    void focusInLocked(View view) {
1058        if (DEBUG) Log.v(TAG, "focusIn: " + view);
1059
1060        if (mCurRootView != view.getRootView()) {
1061            // This is a request from a window that isn't in the window with
1062            // IME focus, so ignore it.
1063            if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
1064            return;
1065        }
1066
1067        mNextServedView = view;
1068        scheduleCheckFocusLocked(view);
1069    }
1070
1071    /**
1072     * Call this when a view loses focus.
1073     * @hide
1074     */
1075    public void focusOut(View view) {
1076        synchronized (mH) {
1077            if (DEBUG) Log.v(TAG, "focusOut: " + view
1078                    + " mServedView=" + mServedView
1079                    + " winFocus=" + view.hasWindowFocus());
1080            if (mServedView != view) {
1081                // The following code would auto-hide the IME if we end up
1082                // with no more views with focus.  This can happen, however,
1083                // whenever we go into touch mode, so it ends up hiding
1084                // at times when we don't really want it to.  For now it
1085                // seems better to just turn it all off.
1086                if (false && view.hasWindowFocus()) {
1087                    mNextServedView = null;
1088                    scheduleCheckFocusLocked(view);
1089                }
1090            }
1091        }
1092    }
1093
1094    void scheduleCheckFocusLocked(View view) {
1095        Handler vh = view.getHandler();
1096        if (vh != null && !vh.hasMessages(ViewAncestor.CHECK_FOCUS)) {
1097            // This will result in a call to checkFocus() below.
1098            vh.sendMessage(vh.obtainMessage(ViewAncestor.CHECK_FOCUS));
1099        }
1100    }
1101
1102    /**
1103     * @hide
1104     */
1105    public void checkFocus() {
1106        // This is called a lot, so short-circuit before locking.
1107        if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1108            return;
1109        }
1110
1111        InputConnection ic = null;
1112        synchronized (mH) {
1113            if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1114                return;
1115            }
1116            if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1117                    + " next=" + mNextServedView
1118                    + " restart=" + mNextServedNeedsStart);
1119
1120            mNextServedNeedsStart = false;
1121            if (mNextServedView == null) {
1122                finishInputLocked();
1123                // In this case, we used to have a focused view on the window,
1124                // but no longer do.  We should make sure the input method is
1125                // no longer shown, since it serves no purpose.
1126                closeCurrentInput();
1127                return;
1128            }
1129
1130            ic = mServedInputConnection;
1131
1132            mServedView = mNextServedView;
1133            mCurrentTextBoxAttribute = null;
1134            mCompletions = null;
1135            mServedConnecting = true;
1136        }
1137
1138        if (ic != null) {
1139            ic.finishComposingText();
1140        }
1141
1142        startInputInner();
1143    }
1144
1145    void closeCurrentInput() {
1146        try {
1147            mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
1148        } catch (RemoteException e) {
1149        }
1150    }
1151
1152    /**
1153     * Called by ViewAncestor when its window gets input focus.
1154     * @hide
1155     */
1156    public void onWindowFocus(View rootView, View focusedView, int softInputMode,
1157            boolean first, int windowFlags) {
1158        synchronized (mH) {
1159            if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1160                    + " softInputMode=" + softInputMode
1161                    + " first=" + first + " flags=#"
1162                    + Integer.toHexString(windowFlags));
1163            if (mHasBeenInactive) {
1164                if (DEBUG) Log.v(TAG, "Has been inactive!  Starting fresh");
1165                mHasBeenInactive = false;
1166                mNextServedNeedsStart = true;
1167            }
1168            focusInLocked(focusedView != null ? focusedView : rootView);
1169        }
1170
1171        checkFocus();
1172
1173        synchronized (mH) {
1174            try {
1175                final boolean isTextEditor = focusedView != null &&
1176                        focusedView.onCheckIsTextEditor();
1177                mService.windowGainedFocus(mClient, rootView.getWindowToken(),
1178                        focusedView != null, isTextEditor, softInputMode, first,
1179                        windowFlags);
1180            } catch (RemoteException e) {
1181            }
1182        }
1183    }
1184
1185    /** @hide */
1186    public void startGettingWindowFocus(View rootView) {
1187        synchronized (mH) {
1188            mCurRootView = rootView;
1189        }
1190    }
1191
1192    /**
1193     * Report the current selection range.
1194     */
1195    public void updateSelection(View view, int selStart, int selEnd,
1196            int candidatesStart, int candidatesEnd) {
1197        checkFocus();
1198        synchronized (mH) {
1199            if ((mServedView != view && (mServedView == null
1200                        || !mServedView.checkInputConnectionProxy(view)))
1201                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1202                return;
1203            }
1204
1205            if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1206                    || mCursorCandStart != candidatesStart
1207                    || mCursorCandEnd != candidatesEnd) {
1208                if (DEBUG) Log.d(TAG, "updateSelection");
1209
1210                try {
1211                    if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
1212                    mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
1213                            selStart, selEnd, candidatesStart, candidatesEnd);
1214                    mCursorSelStart = selStart;
1215                    mCursorSelEnd = selEnd;
1216                    mCursorCandStart = candidatesStart;
1217                    mCursorCandEnd = candidatesEnd;
1218                } catch (RemoteException e) {
1219                    Log.w(TAG, "IME died: " + mCurId, e);
1220                }
1221            }
1222        }
1223    }
1224
1225    /**
1226     * Returns true if the current input method wants to watch the location
1227     * of the input editor's cursor in its window.
1228     */
1229    public boolean isWatchingCursor(View view) {
1230        return false;
1231    }
1232
1233    /**
1234     * Report the current cursor location in its window.
1235     */
1236    public void updateCursor(View view, int left, int top, int right, int bottom) {
1237        checkFocus();
1238        synchronized (mH) {
1239            if ((mServedView != view && (mServedView == null
1240                        || !mServedView.checkInputConnectionProxy(view)))
1241                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1242                return;
1243            }
1244
1245            mTmpCursorRect.set(left, top, right, bottom);
1246            if (!mCursorRect.equals(mTmpCursorRect)) {
1247                if (DEBUG) Log.d(TAG, "updateCursor");
1248
1249                try {
1250                    if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1251                    mCurMethod.updateCursor(mTmpCursorRect);
1252                    mCursorRect.set(mTmpCursorRect);
1253                } catch (RemoteException e) {
1254                    Log.w(TAG, "IME died: " + mCurId, e);
1255                }
1256            }
1257        }
1258    }
1259
1260    /**
1261     * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1262     * InputMethodSession.appPrivateCommand()} on the current Input Method.
1263     * @param view Optional View that is sending the command, or null if
1264     * you want to send the command regardless of the view that is attached
1265     * to the input method.
1266     * @param action Name of the command to be performed.  This <em>must</em>
1267     * be a scoped name, i.e. prefixed with a package name you own, so that
1268     * different developers will not create conflicting commands.
1269     * @param data Any data to include with the command.
1270     */
1271    public void sendAppPrivateCommand(View view, String action, Bundle data) {
1272        checkFocus();
1273        synchronized (mH) {
1274            if ((mServedView != view && (mServedView == null
1275                        || !mServedView.checkInputConnectionProxy(view)))
1276                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1277                return;
1278            }
1279            try {
1280                if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1281                mCurMethod.appPrivateCommand(action, data);
1282            } catch (RemoteException e) {
1283                Log.w(TAG, "IME died: " + mCurId, e);
1284            }
1285        }
1286    }
1287
1288    /**
1289     * Force switch to a new input method component. This can only be called
1290     * from an application or a service which has a token of the currently active input method.
1291     * @param token Supplies the identifying token given to an input method
1292     * when it was started, which allows it to perform this operation on
1293     * itself.
1294     * @param id The unique identifier for the new input method to be switched to.
1295     */
1296    public void setInputMethod(IBinder token, String id) {
1297        try {
1298            mService.setInputMethod(token, id);
1299        } catch (RemoteException e) {
1300            throw new RuntimeException(e);
1301        }
1302    }
1303
1304    /**
1305     * Force switch to a new input method and subtype. This can only be called
1306     * from an application or a service which has a token of the currently active input method.
1307     * @param token Supplies the identifying token given to an input method
1308     * when it was started, which allows it to perform this operation on
1309     * itself.
1310     * @param id The unique identifier for the new input method to be switched to.
1311     * @param subtype The new subtype of the new input method to be switched to.
1312     */
1313    public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
1314        try {
1315            mService.setInputMethodAndSubtype(token, id, subtype);
1316        } catch (RemoteException e) {
1317            throw new RuntimeException(e);
1318        }
1319    }
1320
1321    /**
1322     * Close/hide the input method's soft input area, so the user no longer
1323     * sees it or can interact with it.  This can only be called
1324     * from the currently active input method, as validated by the given token.
1325     *
1326     * @param token Supplies the identifying token given to an input method
1327     * when it was started, which allows it to perform this operation on
1328     * itself.
1329     * @param flags Provides additional operating flags.  Currently may be
1330     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1331     * {@link #HIDE_NOT_ALWAYS} bit set.
1332     */
1333    public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1334        try {
1335            mService.hideMySoftInput(token, flags);
1336        } catch (RemoteException e) {
1337            throw new RuntimeException(e);
1338        }
1339    }
1340
1341    /**
1342     * Show the input method's soft input area, so the user
1343     * sees the input method window and can interact with it.
1344     * This can only be called from the currently active input method,
1345     * as validated by the given token.
1346     *
1347     * @param token Supplies the identifying token given to an input method
1348     * when it was started, which allows it to perform this operation on
1349     * itself.
1350     * @param flags Provides additional operating flags.  Currently may be
1351     * 0 or have the {@link #SHOW_IMPLICIT} or
1352     * {@link #SHOW_FORCED} bit set.
1353     */
1354    public void showSoftInputFromInputMethod(IBinder token, int flags) {
1355        try {
1356            mService.showMySoftInput(token, flags);
1357        } catch (RemoteException e) {
1358            throw new RuntimeException(e);
1359        }
1360    }
1361
1362    /**
1363     * @hide
1364     */
1365    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
1366            IInputMethodCallback callback) {
1367        synchronized (mH) {
1368            if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
1369
1370            if (mCurMethod == null) {
1371                try {
1372                    callback.finishedEvent(seq, false);
1373                } catch (RemoteException e) {
1374                }
1375                return;
1376            }
1377
1378            if (key.getAction() == KeyEvent.ACTION_DOWN
1379                    && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
1380                showInputMethodPicker();
1381                try {
1382                    callback.finishedEvent(seq, true);
1383                } catch (RemoteException e) {
1384                }
1385                return;
1386            }
1387            try {
1388                if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
1389                mCurMethod.dispatchKeyEvent(seq, key, callback);
1390            } catch (RemoteException e) {
1391                Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
1392                try {
1393                    callback.finishedEvent(seq, false);
1394                } catch (RemoteException ex) {
1395                }
1396            }
1397        }
1398    }
1399
1400    /**
1401     * @hide
1402     */
1403    void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
1404            IInputMethodCallback callback) {
1405        synchronized (mH) {
1406            if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
1407
1408            if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
1409                try {
1410                    callback.finishedEvent(seq, false);
1411                } catch (RemoteException e) {
1412                }
1413                return;
1414            }
1415
1416            try {
1417                if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
1418                mCurMethod.dispatchTrackballEvent(seq, motion, callback);
1419            } catch (RemoteException e) {
1420                Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
1421                try {
1422                    callback.finishedEvent(seq, false);
1423                } catch (RemoteException ex) {
1424                }
1425            }
1426        }
1427    }
1428
1429    public void showInputMethodPicker() {
1430        synchronized (mH) {
1431            try {
1432                mService.showInputMethodPickerFromClient(mClient);
1433            } catch (RemoteException e) {
1434                Log.w(TAG, "IME died: " + mCurId, e);
1435            }
1436        }
1437    }
1438
1439    /**
1440     * Show the settings for enabling subtypes of the specified input method.
1441     * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
1442     * subtypes of all input methods will be shown.
1443     */
1444    public void showInputMethodAndSubtypeEnabler(String imiId) {
1445        synchronized (mH) {
1446            try {
1447                mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
1448            } catch (RemoteException e) {
1449                Log.w(TAG, "IME died: " + mCurId, e);
1450            }
1451        }
1452    }
1453
1454    /**
1455     * Returns the current input method subtype. This subtype is one of the subtypes in
1456     * the current input method. This method returns null when the current input method doesn't
1457     * have any input method subtype.
1458     */
1459    public InputMethodSubtype getCurrentInputMethodSubtype() {
1460        synchronized (mH) {
1461            try {
1462                return mService.getCurrentInputMethodSubtype();
1463            } catch (RemoteException e) {
1464                Log.w(TAG, "IME died: " + mCurId, e);
1465                return null;
1466            }
1467        }
1468    }
1469
1470    /**
1471     * Switch to a new input method subtype of the current input method.
1472     * @param subtype A new input method subtype to switch.
1473     * @return true if the current subtype was successfully switched. When the specified subtype is
1474     * null, this method returns false.
1475     */
1476    public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
1477        synchronized (mH) {
1478            try {
1479                return mService.setCurrentInputMethodSubtype(subtype);
1480            } catch (RemoteException e) {
1481                Log.w(TAG, "IME died: " + mCurId, e);
1482                return false;
1483            }
1484        }
1485    }
1486
1487    /**
1488     * Returns a map of all shortcut input method info and their subtypes.
1489     */
1490    public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
1491        synchronized (mH) {
1492            HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
1493                    new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
1494            try {
1495                // TODO: We should change the return type from List<Object> to List<Parcelable>
1496                List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
1497                // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
1498                ArrayList<InputMethodSubtype> subtypes = null;
1499                final int N = info.size();
1500                if (info != null && N > 0) {
1501                    for (int i = 0; i < N; ++i) {
1502                        Object o = info.get(i);
1503                        if (o instanceof InputMethodInfo) {
1504                            if (ret.containsKey(o)) {
1505                                Log.e(TAG, "IMI list already contains the same InputMethod.");
1506                                break;
1507                            }
1508                            subtypes = new ArrayList<InputMethodSubtype>();
1509                            ret.put((InputMethodInfo)o, subtypes);
1510                        } else if (subtypes != null && o instanceof InputMethodSubtype) {
1511                            subtypes.add((InputMethodSubtype)o);
1512                        }
1513                    }
1514                }
1515            } catch (RemoteException e) {
1516                Log.w(TAG, "IME died: " + mCurId, e);
1517            }
1518            return ret;
1519        }
1520    }
1521
1522    /**
1523     * Force switch to the last used input method and subtype. If the last input method didn't have
1524     * any subtypes, the framework will simply switch to the last input method with no subtype
1525     * specified.
1526     * @param imeToken Supplies the identifying token given to an input method when it was started,
1527     * which allows it to perform this operation on itself.
1528     * @return true if the current input method and subtype was successfully switched to the last
1529     * used input method and subtype.
1530     */
1531    public boolean switchToLastInputMethod(IBinder imeToken) {
1532        synchronized (mH) {
1533            try {
1534                return mService.switchToLastInputMethod(imeToken);
1535            } catch (RemoteException e) {
1536                Log.w(TAG, "IME died: " + mCurId, e);
1537                return false;
1538            }
1539        }
1540    }
1541
1542    public InputMethodSubtype getLastInputMethodSubtype() {
1543        synchronized (mH) {
1544            try {
1545                return mService.getLastInputMethodSubtype();
1546            } catch (RemoteException e) {
1547                Log.w(TAG, "IME died: " + mCurId, e);
1548                return null;
1549            }
1550        }
1551    }
1552
1553    void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1554        final Printer p = new PrintWriterPrinter(fout);
1555        p.println("Input method client state for " + this + ":");
1556
1557        p.println("  mService=" + mService);
1558        p.println("  mMainLooper=" + mMainLooper);
1559        p.println("  mIInputContext=" + mIInputContext);
1560        p.println("  mActive=" + mActive
1561                + " mHasBeenInactive=" + mHasBeenInactive
1562                + " mBindSequence=" + mBindSequence
1563                + " mCurId=" + mCurId);
1564        p.println("  mCurMethod=" + mCurMethod);
1565        p.println("  mCurRootView=" + mCurRootView);
1566        p.println("  mServedView=" + mServedView);
1567        p.println("  mNextServedNeedsStart=" + mNextServedNeedsStart
1568                + " mNextServedView=" + mNextServedView);
1569        p.println("  mServedConnecting=" + mServedConnecting);
1570        if (mCurrentTextBoxAttribute != null) {
1571            p.println("  mCurrentTextBoxAttribute:");
1572            mCurrentTextBoxAttribute.dump(p, "    ");
1573        } else {
1574            p.println("  mCurrentTextBoxAttribute: null");
1575        }
1576        p.println("  mServedInputConnection=" + mServedInputConnection);
1577        p.println("  mCompletions=" + mCompletions);
1578        p.println("  mCursorRect=" + mCursorRect);
1579        p.println("  mCursorSelStart=" + mCursorSelStart
1580                + " mCursorSelEnd=" + mCursorSelEnd
1581                + " mCursorCandStart=" + mCursorCandStart
1582                + " mCursorCandEnd=" + mCursorCandEnd);
1583    }
1584}
1585