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