InputMethodManager.java revision d24b8183b93e781080b2c16c487e60d51c12da31
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 android.content.Context;
20import android.graphics.Rect;
21import android.os.Bundle;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.Looper;
25import android.os.Message;
26import android.os.RemoteException;
27import android.os.ServiceManager;
28import android.util.Log;
29import android.util.PrintWriterPrinter;
30import android.util.Printer;
31import android.view.KeyEvent;
32import android.view.MotionEvent;
33import android.view.View;
34import android.view.ViewRoot;
35
36import com.android.internal.os.HandlerCaller;
37import com.android.internal.view.IInputConnectionWrapper;
38import com.android.internal.view.IInputContext;
39import com.android.internal.view.IInputMethodCallback;
40import com.android.internal.view.IInputMethodClient;
41import com.android.internal.view.IInputMethodManager;
42import com.android.internal.view.IInputMethodSession;
43import com.android.internal.view.InputBindResult;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47import java.util.List;
48import java.util.concurrent.CountDownLatch;
49import java.util.concurrent.TimeUnit;
50
51/**
52 * Central system API to the overall input method framework (IMF) architecture,
53 * which arbitrates interaction between applications and the current input method.
54 * You can retrieve an instance of this interface with
55 * {@link Context#getSystemService(String) Context.getSystemService()}.
56 *
57 * <p>Topics covered here:
58 * <ol>
59 * <li><a href="#ArchitectureOverview">Architecture Overview</a>
60 * </ol>
61 *
62 * <a name="ArchitectureOverview"></a>
63 * <h3>Architecture Overview</h3>
64 *
65 * <p>There are three primary parties involved in the input method
66 * framework (IMF) architecture:</p>
67 *
68 * <ul>
69 * <li> The <strong>input method manager</strong> as expressed by this class
70 * is the central point of the system that manages interaction between all
71 * other parts.  It is expressed as the client-side API here which exists
72 * in each application context and communicates with a global system service
73 * that manages the interaction across all processes.
74 * <li> An <strong>input method (IME)</strong> implements a particular
75 * interaction model allowing the user to generate text.  The system binds
76 * to the current input method that is use, causing it to be created and run,
77 * and tells it when to hide and show its UI.  Only one IME is running at a time.
78 * <li> Multiple <strong>client applications</strong> arbitrate with the input
79 * method manager for input focus and control over the state of the IME.  Only
80 * one such client is ever active (working with the IME) at a time.
81 * </ul>
82 *
83 *
84 * <a name="Applications"></a>
85 * <h3>Applications</h3>
86 *
87 * <p>In most cases, applications that are using the standard
88 * {@link android.widget.TextView} or its subclasses will have little they need
89 * to do to work well with soft input methods.  The main things you need to
90 * be aware of are:</p>
91 *
92 * <ul>
93 * <li> Properly set the {@link android.R.attr#inputType} if your editable
94 * text views, so that the input method will have enough context to help the
95 * user in entering text into them.
96 * <li> Deal well with losing screen space when the input method is
97 * displayed.  Ideally an application should handle its window being resized
98 * smaller, but it can rely on the system performing panning of the window
99 * if needed.  You should set the {@link android.R.attr#windowSoftInputMode}
100 * attribute on your activity or the corresponding values on windows you
101 * create to help the system determine whether to pan or resize (it will
102 * try to determine this automatically but may get it wrong).
103 * <li> You can also control the preferred soft input state (open, closed, etc)
104 * for your window using the same {@link android.R.attr#windowSoftInputMode}
105 * attribute.
106 * </ul>
107 *
108 * <p>More finer-grained control is available through the APIs here to directly
109 * interact with the IMF and its IME -- either showing or hiding the input
110 * area, letting the user pick an input method, etc.</p>
111 *
112 * <p>For the rare people amongst us writing their own text editors, you
113 * will need to implement {@link android.view.View#onCreateInputConnection}
114 * to return a new instance of your own {@link InputConnection} interface
115 * allowing the IME to interact with your editor.</p>
116 *
117 *
118 * <a name="InputMethods"></a>
119 * <h3>Input Methods</h3>
120 *
121 * <p>An input method (IME) is implemented
122 * as a {@link android.app.Service}, typically deriving from
123 * {@link android.inputmethodservice.InputMethodService}.  It must provide
124 * the core {@link InputMethod} interface, though this is normally handled by
125 * {@link android.inputmethodservice.InputMethodService} and implementors will
126 * only need to deal with the higher-level API there.</p>
127 *
128 * See the {@link android.inputmethodservice.InputMethodService} class for
129 * more information on implementing IMEs.
130 *
131 *
132 * <a name="Security"></a>
133 * <h3>Security</h3>
134 *
135 * <p>There are a lot of security issues associated with input methods,
136 * since they essentially have freedom to completely drive the UI and monitor
137 * everything the user enters.  The Android input method framework also allows
138 * arbitrary third party IMEs, so care must be taken to restrict their
139 * selection and interactions.</p>
140 *
141 * <p>Here are some key points about the security architecture behind the
142 * IMF:</p>
143 *
144 * <ul>
145 * <li> <p>Only the system is allowed to directly access an IME's
146 * {@link InputMethod} interface, via the
147 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission.  This is
148 * enforced in the system by not binding to an input method service that does
149 * not require this permission, so the system can guarantee no other untrusted
150 * clients are accessing the current input method outside of its control.</p>
151 *
152 * <li> <p>There may be many client processes of the IMF, but only one may
153 * be active at a time.  The inactive clients can not interact with key
154 * parts of the IMF through the mechanisms described below.</p>
155 *
156 * <li> <p>Clients of an input method are only given access to its
157 * {@link InputMethodSession} interface.  One instance of this interface is
158 * created for each client, and only calls from the session associated with
159 * the active client will be processed by the current IME.  This is enforced
160 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
161 * IMEs, but must be explicitly handled by an IME that is customizing the
162 * raw {@link InputMethodSession} implementation.</p>
163 *
164 * <li> <p>Only the active client's {@link InputConnection} will accept
165 * operations.  The IMF tells each client process whether it is active, and
166 * the framework enforces that in inactive processes calls on to the current
167 * InputConnection will be ignored.  This ensures that the current IME can
168 * only deliver events and text edits to the UI that the user sees as
169 * being in focus.</p>
170 *
171 * <li> <p>An IME can never interact with an {@link InputConnection} while
172 * the screen is off.  This is enforced by making all clients inactive while
173 * the screen is off, and prevents bad IMEs from driving the UI when the user
174 * can not be aware of its behavior.</p>
175 *
176 * <li> <p>A client application can ask that the system let the user pick a
177 * new IME, but can not programmatically switch to one itself.  This avoids
178 * malicious applications from switching the user to their own IME, which
179 * remains running when the user navigates away to another application.  An
180 * IME, on the other hand, <em>is</em> allowed to programmatically switch
181 * the system to another IME, since it already has full control of user
182 * input.</p>
183 *
184 * <li> <p>The user must explicitly enable a new IME in settings before
185 * they can switch to it, to confirm with the system that they know about it
186 * and want to make it available for use.</p>
187 * </ul>
188 */
189public final class InputMethodManager {
190    static final boolean DEBUG = false;
191    static final String TAG = "InputMethodManager";
192
193    /**
194     * The package name of the build-in input method.
195     * {@hide}
196     */
197    public static final String BUILDIN_INPUTMETHOD_PACKAGE = "android.text.inputmethod";
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
219    /**
220     * This is the view that should currently be served by an input method,
221     * regardless of the state of setting that up.
222     */
223    View mServedView;
224    /**
225     * For evaluating the state after a focus change, this is the view that
226     * had focus.
227     */
228    View mLastServedView;
229    /**
230     * This is set when we are in the process of connecting, to determine
231     * when we have actually finished.
232     */
233    boolean mServedConnecting;
234    /**
235     * This is non-null when we have connected the served view; it holds
236     * the attributes that were last retrieved from the served view and given
237     * to the input connection.
238     */
239    EditorInfo mCurrentTextBoxAttribute;
240    /**
241     * The InputConnection that was last retrieved from the served view.
242     */
243    InputConnection mServedInputConnection;
244    /**
245     * The completions that were last provided by the served view.
246     */
247    CompletionInfo[] mCompletions;
248
249    // Cursor position on the screen.
250    Rect mTmpCursorRect = new Rect();
251    Rect mCursorRect = new Rect();
252    int mCursorSelStart;
253    int mCursorSelEnd;
254    int mCursorCandStart;
255    int mCursorCandEnd;
256
257    // -----------------------------------------------------------
258
259    /**
260     * Sequence number of this binding, as returned by the server.
261     */
262    int mBindSequence = -1;
263    /**
264     * ID of the method we are bound to.
265     */
266    String mCurId;
267    /**
268     * The actual instance of the method to make calls on it.
269     */
270    IInputMethodSession mCurMethod;
271
272    // -----------------------------------------------------------
273
274    static final int MSG_DUMP = 1;
275
276    class H extends Handler {
277        H(Looper looper) {
278            super(looper);
279        }
280
281        @Override
282        public void handleMessage(Message msg) {
283            switch (msg.what) {
284                case MSG_DUMP: {
285                    HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
286                    try {
287                        doDump((FileDescriptor)args.arg1,
288                                (PrintWriter)args.arg2, (String[])args.arg3);
289                    } catch (RuntimeException e) {
290                        ((PrintWriter)args.arg2).println("Exception: " + e);
291                    }
292                    synchronized (args.arg4) {
293                        ((CountDownLatch)args.arg4).countDown();
294                    }
295                    return;
296                }
297            }
298        }
299    }
300
301    class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
302        public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
303            super(mainLooper, conn);
304        }
305
306        public boolean isActive() {
307            return mActive;
308        }
309    }
310
311    final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
312        @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
313            // No need to check for dump permission, since we only give this
314            // interface to the system.
315
316            CountDownLatch latch = new CountDownLatch(1);
317            HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs();
318            sargs.arg1 = fd;
319            sargs.arg2 = fout;
320            sargs.arg3 = args;
321            sargs.arg4 = latch;
322            mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
323            try {
324                if (!latch.await(5, TimeUnit.SECONDS)) {
325                    fout.println("Timeout waiting for dump");
326                }
327            } catch (InterruptedException e) {
328                fout.println("Interrupted waiting for dump");
329            }
330        }
331
332        public void setUsingInputMethod(boolean state) {
333        }
334
335        public void onBindMethod(InputBindResult res) {
336            synchronized (mH) {
337                if (mBindSequence < 0 || mBindSequence != res.sequence) {
338                    Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
339                            + ", given seq=" + res.sequence);
340                    return;
341                }
342
343                mCurMethod = res.method;
344                mCurId = res.id;
345                mBindSequence = res.sequence;
346            }
347            startInputInner();
348        }
349
350        public void onUnbindMethod(int sequence) {
351            synchronized (mH) {
352                if (mBindSequence == sequence) {
353                    if (false) {
354                        // XXX the server has already unbound!
355                        if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
356                            try {
357                                mCurMethod.finishInput();
358                            } catch (RemoteException e) {
359                                Log.w(TAG, "IME died: " + mCurId, e);
360                            }
361                        }
362                    }
363                    clearBindingLocked();
364
365                    // If we were actively using the last input method, then
366                    // we would like to re-connect to the next input method.
367                    if (mServedView != null && mServedView.isFocused()) {
368                        mServedConnecting = true;
369                    }
370                }
371                startInputInner();
372            }
373        }
374
375        public void setActive(boolean active) {
376            mActive = active;
377        }
378    };
379
380    final InputConnection mDummyInputConnection = new BaseInputConnection(this, true);
381
382    InputMethodManager(IInputMethodManager service, Looper looper) {
383        mService = service;
384        mMainLooper = looper;
385        mH = new H(looper);
386        mIInputContext = new ControlledInputConnectionWrapper(looper,
387                mDummyInputConnection);
388
389        if (mInstance == null) {
390            mInstance = this;
391        }
392    }
393
394    /**
395     * Retrieve the global InputMethodManager instance, creating it if it
396     * doesn't already exist.
397     * @hide
398     */
399    static public InputMethodManager getInstance(Context context) {
400        synchronized (mInstanceSync) {
401            if (mInstance != null) {
402                return mInstance;
403            }
404            IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
405            IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
406            mInstance = new InputMethodManager(service, context.getMainLooper());
407        }
408        return mInstance;
409    }
410
411    /**
412     * Private optimization: retrieve the global InputMethodManager instance,
413     * if it exists.
414     * @hide
415     */
416    static public InputMethodManager peekInstance() {
417        return mInstance;
418    }
419
420    /** @hide */
421    public IInputMethodClient getClient() {
422        return mClient;
423    }
424
425    /** @hide */
426    public IInputContext getInputContext() {
427        return mIInputContext;
428    }
429
430    public List<InputMethodInfo> getInputMethodList() {
431        try {
432            return mService.getInputMethodList();
433        } catch (RemoteException e) {
434            throw new RuntimeException(e);
435        }
436    }
437
438    public List<InputMethodInfo> getEnabledInputMethodList() {
439        try {
440            return mService.getEnabledInputMethodList();
441        } catch (RemoteException e) {
442            throw new RuntimeException(e);
443        }
444    }
445
446    public void updateStatusIcon(int iconId, String iconPackage) {
447        try {
448            mService.updateStatusIcon(iconId, iconPackage);
449        } catch (RemoteException e) {
450            throw new RuntimeException(e);
451        }
452    }
453
454    /**
455     * Return true if the given view is the currently active view for the
456     * input method.
457     */
458    public boolean isActive(View view) {
459        synchronized (mH) {
460            return mServedView == view && mCurrentTextBoxAttribute != null;
461        }
462    }
463
464    /**
465     * Return true if any view is currently active in the input method.
466     */
467    public boolean isActive() {
468        synchronized (mH) {
469            return mServedView != null && mCurrentTextBoxAttribute != null;
470        }
471    }
472
473    /**
474     * Return true if the currently served view is accepting full text edits.
475     * If false, it has no input connection, so can only handle raw key events.
476     */
477    public boolean isAcceptingText() {
478        return mServedInputConnection != null;
479    }
480
481    /**
482     * Reset all of the state associated with being bound to an input method.
483     */
484    void clearBindingLocked() {
485        clearConnectionLocked();
486        mBindSequence = -1;
487        mCurId = null;
488        mCurMethod = null;
489    }
490
491    /**
492     * Reset all of the state associated with a served view being connected
493     * to an input method
494     */
495    void clearConnectionLocked() {
496        mCurrentTextBoxAttribute = null;
497        mServedInputConnection = null;
498    }
499
500    /**
501     * Disconnect any existing input connection, clearing the served view.
502     */
503    void finishInputLocked() {
504        if (mServedView != null) {
505            if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
506            updateStatusIcon(0, null);
507
508            if (mCurrentTextBoxAttribute != null) {
509                try {
510                    mService.finishInput(mClient);
511                } catch (RemoteException e) {
512                }
513            }
514
515            if (mServedInputConnection != null) {
516                // We need to tell the previously served view that it is no
517                // longer the input target, so it can reset its state.  Schedule
518                // this call on its window's Handler so it will be on the correct
519                // thread and outside of our lock.
520                Handler vh = mServedView.getHandler();
521                if (vh != null) {
522                    // This will result in a call to reportFinishInputConnection()
523                    // below.
524                    vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION,
525                            mServedInputConnection));
526                }
527            }
528
529            mServedView = null;
530            mCompletions = null;
531            mServedConnecting = false;
532            clearConnectionLocked();
533        }
534    }
535
536    /**
537     * Called from the FINISH_INPUT_CONNECTION message above.
538     * @hide
539     */
540    public void reportFinishInputConnection(InputConnection ic) {
541        if (mServedInputConnection != ic) {
542            ic.finishComposingText();
543        }
544    }
545
546    public void displayCompletions(View view, CompletionInfo[] completions) {
547        synchronized (mH) {
548            if (mServedView != view) {
549                return;
550            }
551
552            mCompletions = completions;
553            if (mCurMethod != null) {
554                try {
555                    mCurMethod.displayCompletions(mCompletions);
556                } catch (RemoteException e) {
557                }
558            }
559        }
560    }
561
562    public void updateExtractedText(View view, int token, ExtractedText text) {
563        synchronized (mH) {
564            if (mServedView != view) {
565                return;
566            }
567
568            if (mCurMethod != null) {
569                try {
570                    mCurMethod.updateExtractedText(token, text);
571                } catch (RemoteException e) {
572                }
573            }
574        }
575    }
576
577    /**
578     * Flag for {@link #showSoftInput} to indicate that this is an implicit
579     * request to show the input window, not as the result of a direct request
580     * by the user.  The window may not be shown in this case.
581     */
582    public static final int SHOW_IMPLICIT = 0x0001;
583
584    /**
585     * Flag for {@link #showSoftInput} to indicate that the user has forced
586     * the input method open (such as by long-pressing menu) so it should
587     * not be closed until they explicitly do so.
588     */
589    public static final int SHOW_FORCED = 0x0002;
590
591    /**
592     * Explicitly request that the current input method's soft input area be
593     * shown to the user, if needed.  Call this if the user interacts with
594     * your view in such a way that they have expressed they would like to
595     * start performing input into it.
596     *
597     * @param view The currently focused view, which would like to receive
598     * soft keyboard input.
599     * @param flags Provides additional operating flags.  Currently may be
600     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
601     */
602    public void showSoftInput(View view, int flags) {
603        synchronized (mH) {
604            if (mServedView != view) {
605                return;
606            }
607
608            try {
609                mService.showSoftInput(mClient, flags);
610            } catch (RemoteException e) {
611            }
612        }
613    }
614
615    /** @hide */
616    public void showSoftInputUnchecked(int flags) {
617        try {
618            mService.showSoftInput(mClient, flags);
619        } catch (RemoteException e) {
620        }
621    }
622
623    /**
624     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
625     * input window should only be hidden if it was not explicitly shown
626     * by the user.
627     */
628    public static final int HIDE_IMPLICIT_ONLY = 0x0001;
629
630    /**
631     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
632     * input window should normally be hidden, unless it was originally
633     * shown with {@link #SHOW_FORCED}.
634     */
635    public static final int HIDE_NOT_ALWAYS = 0x0002;
636
637    /**
638     * Request to hide the soft input window from the context of the window
639     * that is currently accepting input.  This should be called as a result
640     * of the user doing some actually than fairly explicitly requests to
641     * have the input window hidden.
642     *
643     * @param windowToken The token of the window that is making the request,
644     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
645     * @param flags Provides additional operating flags.  Currently may be
646     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
647     */
648    public void hideSoftInputFromWindow(IBinder windowToken, int flags) {
649        synchronized (mH) {
650            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
651                return;
652            }
653
654            try {
655                mService.hideSoftInput(mClient, flags);
656            } catch (RemoteException e) {
657            }
658        }
659    }
660
661    /**
662     * If the input method is currently connected to the given view,
663     * restart it with its new contents.  You should call this when the text
664     * within your view changes outside of the normal input method or key
665     * input flow, such as when an application calls TextView.setText().
666     *
667     * @param view The view whose text has changed.
668     */
669    public void restartInput(View view) {
670        synchronized (mH) {
671            if (mServedView != view) {
672                return;
673            }
674
675            mServedConnecting = true;
676        }
677
678        startInputInner();
679    }
680
681    void startInputInner() {
682        final View view;
683        synchronized (mH) {
684            view = mServedView;
685
686            // Make sure we have a window token for the served view.
687            if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
688            if (view == null) {
689                if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
690                return;
691            }
692        }
693
694        // Now we need to get an input connection from the served view.
695        // This is complicated in a couple ways: we can't be holding our lock
696        // when calling out to the view, and we need to make sure we call into
697        // the view on the same thread that is driving its view hierarchy.
698        Handler vh = view.getHandler();
699        if (vh == null) {
700            // If the view doesn't have a handler, something has changed out
701            // from under us, so just bail.
702            if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
703            return;
704        }
705        if (vh.getLooper() != Looper.myLooper()) {
706            // The view is running on a different thread than our own, so
707            // we need to reschedule our work for over there.
708            if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
709            vh.post(new Runnable() {
710                public void run() {
711                    startInputInner();
712                }
713            });
714        }
715
716        // Okay we are now ready to call into the served view and have it
717        // do its stuff.
718        // Life is good: let's hook everything up!
719        EditorInfo tba = new EditorInfo();
720        tba.packageName = view.getContext().getPackageName();
721        tba.fieldId = view.getId();
722        InputConnection ic = view.onCreateInputConnection(tba);
723        if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
724
725        synchronized (mH) {
726            // Now that we are locked again, validate that our state hasn't
727            // changed.
728            if (mServedView != view || !mServedConnecting) {
729                // Something else happened, so abort.
730                if (DEBUG) Log.v(TAG,
731                        "Starting input: finished by someone else (view="
732                        + mServedView + " conn=" + mServedConnecting + ")");
733                return;
734            }
735
736            // If we already have a text box, then this view is already
737            // connected so we want to restart it.
738            final boolean initial = mCurrentTextBoxAttribute == null;
739
740            // Hook 'em up and let 'er rip.
741            mCurrentTextBoxAttribute = tba;
742            mServedConnecting = false;
743            mServedInputConnection = ic;
744            IInputContext servedContext;
745            if (ic != null) {
746                mCursorSelStart = tba.initialSelStart;
747                mCursorSelEnd = tba.initialSelEnd;
748                mCursorCandStart = -1;
749                mCursorCandEnd = -1;
750                mCursorRect.setEmpty();
751                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
752            } else {
753                servedContext = null;
754            }
755
756            try {
757                if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
758                        + ic + " tba=" + tba + " initial=" + initial);
759                InputBindResult res = mService.startInput(mClient,
760                        servedContext, tba, initial, mCurMethod == null);
761                if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
762                if (res != null) {
763                    if (res.id != null) {
764                        mBindSequence = res.sequence;
765                        mCurMethod = res.method;
766                    } else {
767                        // This means there is no input method available.
768                        if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
769                        return;
770                    }
771                }
772                if (mCurMethod != null && mCompletions != null) {
773                    try {
774                        mCurMethod.displayCompletions(mCompletions);
775                    } catch (RemoteException e) {
776                    }
777                }
778            } catch (RemoteException e) {
779                Log.w(TAG, "IME died: " + mCurId, e);
780            }
781        }
782    }
783
784    /**
785     * When the focused window is dismissed, this method is called to finish the
786     * input method started before.
787     * @hide
788     */
789    public void windowDismissed(IBinder appWindowToken) {
790        synchronized (mH) {
791            if (mServedView != null &&
792                    mServedView.getWindowToken() == appWindowToken) {
793                finishInputLocked();
794            }
795        }
796    }
797
798    /**
799     * Call this when a view receives focus.
800     * @hide
801     */
802    public void focusIn(View view) {
803        synchronized (mH) {
804            if (DEBUG) Log.v(TAG, "focusIn: " + view);
805            // Okay we have a new view that is being served.
806            if (mServedView != view) {
807                mCurrentTextBoxAttribute = null;
808            }
809            mServedView = view;
810            mCompletions = null;
811            mServedConnecting = true;
812        }
813
814        startInputInner();
815    }
816
817    /**
818     * Call this when a view loses focus.
819     * @hide
820     */
821    public void focusOut(View view) {
822        InputConnection ic = null;
823        synchronized (mH) {
824            if (DEBUG) Log.v(TAG, "focusOut: " + view
825                    + " mServedView=" + mServedView
826                    + " winFocus=" + view.hasWindowFocus());
827            if (mServedView == view) {
828                ic = mServedInputConnection;
829                // The following code would auto-hide the IME if we end up
830                // with no more views with focus.  This can happen, however,
831                // whenever we go into touch mode, so it ends up hiding
832                // at times when we don't really want it to.  For now it
833                // seems better to just turn it all off.
834                if (false && view.hasWindowFocus()) {
835                    mLastServedView = view;
836                    Handler vh = view.getHandler();
837                    if (vh != null) {
838                        // This will result in a call to checkFocus() below.
839                        vh.removeMessages(ViewRoot.CHECK_FOCUS);
840                        vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS,
841                                view));
842                    }
843                }
844            }
845        }
846
847        if (ic != null) {
848            ic.finishComposingText();
849        }
850    }
851
852    /**
853     * @hide
854     */
855    public void checkFocus(View view) {
856        synchronized (mH) {
857            if (view != mLastServedView) {
858                return;
859            }
860            if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
861                    + " last=" + mLastServedView);
862            if (mServedView == mLastServedView) {
863                finishInputLocked();
864                // In this case, we used to have a focused view on the window,
865                // but no longer do.  We should make sure the input method is
866                // no longer shown, since it serves no purpose.
867                closeCurrentInput();
868            }
869            mLastServedView = null;
870        }
871    }
872
873    void closeCurrentInput() {
874        try {
875            mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS);
876        } catch (RemoteException e) {
877        }
878    }
879
880    /**
881     * Called by ViewRoot the first time it gets window focus.
882     * @hide
883     */
884    public void onWindowFocus(View focusedView, int softInputMode,
885            boolean first, int windowFlags) {
886        synchronized (mH) {
887            if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
888                    + " softInputMode=" + softInputMode
889                    + " first=" + first + " flags=#"
890                    + Integer.toHexString(windowFlags));
891            try {
892                final boolean isTextEditor = focusedView != null &&
893                focusedView.onCheckIsTextEditor();
894                mService.windowGainedFocus(mClient, focusedView != null,
895                        isTextEditor, softInputMode, first, windowFlags);
896            } catch (RemoteException e) {
897            }
898        }
899    }
900
901    /**
902     * Report the current selection range.
903     */
904    public void updateSelection(View view, int selStart, int selEnd,
905            int candidatesStart, int candidatesEnd) {
906        synchronized (mH) {
907            if (mServedView != view || mCurrentTextBoxAttribute == null
908                    || mCurMethod == null) {
909                return;
910            }
911
912            if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
913                    || mCursorCandStart != candidatesStart
914                    || mCursorCandEnd != candidatesEnd) {
915                if (DEBUG) Log.d(TAG, "updateSelection");
916
917                try {
918                    if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
919                    mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
920                            selStart, selEnd, candidatesStart, candidatesEnd);
921                    mCursorSelStart = selStart;
922                    mCursorSelEnd = selEnd;
923                    mCursorCandStart = candidatesStart;
924                    mCursorCandEnd = candidatesEnd;
925                } catch (RemoteException e) {
926                    Log.w(TAG, "IME died: " + mCurId, e);
927                }
928            }
929        }
930    }
931
932    /**
933     * Returns true if the current input method wants to watch the location
934     * of the input editor's cursor in its window.
935     */
936    public boolean isWatchingCursor(View view) {
937        return false;
938    }
939
940    /**
941     * Report the current cursor location in its window.
942     */
943    public void updateCursor(View view, int left, int top, int right, int bottom) {
944        synchronized (mH) {
945            if (mServedView != view || mCurrentTextBoxAttribute == null
946                    || mCurMethod == null) {
947                return;
948            }
949
950            mTmpCursorRect.set(left, top, right, bottom);
951            if (!mCursorRect.equals(mTmpCursorRect)) {
952                if (DEBUG) Log.d(TAG, "updateCursor");
953
954                try {
955                    if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
956                    mCurMethod.updateCursor(mTmpCursorRect);
957                    mCursorRect.set(mTmpCursorRect);
958                } catch (RemoteException e) {
959                    Log.w(TAG, "IME died: " + mCurId, e);
960                }
961            }
962        }
963    }
964
965    /**
966     * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
967     * InputMethodSession.appPrivateCommand()} on the current Input Method.
968     * @param view Optional View that is sending the command, or null if
969     * you want to send the command regardless of the view that is attached
970     * to the input method.
971     * @param action Name of the command to be performed.  This <em>must</em>
972     * be a scoped name, i.e. prefixed with a package name you own, so that
973     * different developers will not create conflicting commands.
974     * @param data Any data to include with the command.
975     */
976    public void sendAppPrivateCommand(View view, String action, Bundle data) {
977        synchronized (mH) {
978            if ((view != null && mServedView != view)
979                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
980                return;
981            }
982            try {
983                if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
984                mCurMethod.appPrivateCommand(action, data);
985            } catch (RemoteException e) {
986                Log.w(TAG, "IME died: " + mCurId, e);
987            }
988        }
989    }
990
991    /**
992     * Force switch to a new input method component.  This can only be called
993     * from the currently active input method, as validated by the given token.
994     * @param token Supplies the identifying token given to an input method
995     * when it was started, which allows it to perform this operation on
996     * itself.
997     * @param id The unique identifier for the new input method to be switched to.
998     */
999    public void setInputMethod(IBinder token, String id) {
1000        try {
1001            mService.setInputMethod(token, id);
1002        } catch (RemoteException e) {
1003            throw new RuntimeException(e);
1004        }
1005    }
1006
1007    /**
1008     * Close/hide the input method's soft input area, so the user no longer
1009     * sees it or can interact with it.  This can only be called
1010     * from the currently active input method, as validated by the given token.
1011     *
1012     * @param token Supplies the identifying token given to an input method
1013     * when it was started, which allows it to perform this operation on
1014     * itself.
1015     * @param flags Provides additional operating flags.  Currently may be
1016     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
1017     */
1018    public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1019        try {
1020            mService.hideMySoftInput(token, flags);
1021        } catch (RemoteException e) {
1022            throw new RuntimeException(e);
1023        }
1024    }
1025
1026    /**
1027     * @hide
1028     */
1029    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
1030            IInputMethodCallback callback) {
1031        synchronized (mH) {
1032            if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
1033
1034            if (mCurMethod == null) {
1035                try {
1036                    callback.finishedEvent(seq, false);
1037                } catch (RemoteException e) {
1038                }
1039                return;
1040            }
1041
1042            if (key.getAction() == KeyEvent.ACTION_DOWN
1043                    && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
1044                showInputMethodPicker();
1045                try {
1046                    callback.finishedEvent(seq, true);
1047                } catch (RemoteException e) {
1048                }
1049                return;
1050            }
1051            try {
1052                if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
1053                mCurMethod.dispatchKeyEvent(seq, key, callback);
1054            } catch (RemoteException e) {
1055                Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
1056                try {
1057                    callback.finishedEvent(seq, false);
1058                } catch (RemoteException ex) {
1059                }
1060            }
1061        }
1062    }
1063
1064    /**
1065     * @hide
1066     */
1067    void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
1068            IInputMethodCallback callback) {
1069        synchronized (mH) {
1070            if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
1071
1072            if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
1073                try {
1074                    callback.finishedEvent(seq, false);
1075                } catch (RemoteException e) {
1076                }
1077                return;
1078            }
1079
1080            try {
1081                if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
1082                mCurMethod.dispatchTrackballEvent(seq, motion, callback);
1083            } catch (RemoteException e) {
1084                Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
1085                try {
1086                    callback.finishedEvent(seq, false);
1087                } catch (RemoteException ex) {
1088                }
1089            }
1090        }
1091    }
1092
1093    public void showInputMethodPicker() {
1094        synchronized (mH) {
1095            try {
1096                mService.showInputMethodPickerFromClient(mClient);
1097            } catch (RemoteException e) {
1098                Log.w(TAG, "IME died: " + mCurId, e);
1099            }
1100        }
1101    }
1102
1103    void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1104        final Printer p = new PrintWriterPrinter(fout);
1105        p.println("Input method client state for " + this + ":");
1106
1107        p.println("  mService=" + mService);
1108        p.println("  mMainLooper=" + mMainLooper);
1109        p.println("  mIInputContext=" + mIInputContext);
1110        p.println("  mActive=" + mActive
1111                + " mBindSequence=" + mBindSequence
1112                + " mCurId=" + mCurId);
1113        p.println("  mCurMethod=" + mCurMethod);
1114        p.println("  mServedView=" + mServedView);
1115        p.println("  mLastServedView=" + mLastServedView);
1116        p.println("  mServedConnecting=" + mServedConnecting);
1117        if (mCurrentTextBoxAttribute != null) {
1118            p.println("  mCurrentTextBoxAttribute:");
1119            mCurrentTextBoxAttribute.dump(p, "    ");
1120        } else {
1121            p.println("  mCurrentTextBoxAttribute: null");
1122        }
1123        p.println("  mServedInputConnection=" + mServedInputConnection);
1124        p.println("  mCompletions=" + mCompletions);
1125        p.println("  mCursorRect=" + mCursorRect);
1126        p.println("  mCursorSelStart=" + mCursorSelStart
1127                + " mCursorSelEnd=" + mCursorSelEnd
1128                + " mCursorCandStart=" + mCursorCandStart
1129                + " mCursorCandEnd=" + mCursorCandEnd);
1130    }
1131}
1132