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