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