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