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