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