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