InputMethodManager.java revision da996f390e17e16f2dfa60e972e7ebc4f868f37e
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     * As reported by IME through InputConnection.
219     */
220    boolean mFullscreenMode;
221
222    // -----------------------------------------------------------
223
224    /**
225     * This is the view that should currently be served by an input method,
226     * regardless of the state of setting that up.
227     */
228    View mServedView;
229    /**
230     * For evaluating the state after a focus change, this is the view that
231     * had focus.
232     */
233    View mLastServedView;
234    /**
235     * This is set when we are in the process of connecting, to determine
236     * when we have actually finished.
237     */
238    boolean mServedConnecting;
239    /**
240     * This is non-null when we have connected the served view; it holds
241     * the attributes that were last retrieved from the served view and given
242     * to the input connection.
243     */
244    EditorInfo mCurrentTextBoxAttribute;
245    /**
246     * The InputConnection that was last retrieved from the served view.
247     */
248    InputConnection mServedInputConnection;
249    /**
250     * The completions that were last provided by the served view.
251     */
252    CompletionInfo[] mCompletions;
253
254    // Cursor position on the screen.
255    Rect mTmpCursorRect = new Rect();
256    Rect mCursorRect = new Rect();
257    int mCursorSelStart;
258    int mCursorSelEnd;
259    int mCursorCandStart;
260    int mCursorCandEnd;
261
262    // -----------------------------------------------------------
263
264    /**
265     * Sequence number of this binding, as returned by the server.
266     */
267    int mBindSequence = -1;
268    /**
269     * ID of the method we are bound to.
270     */
271    String mCurId;
272    /**
273     * The actual instance of the method to make calls on it.
274     */
275    IInputMethodSession mCurMethod;
276
277    // -----------------------------------------------------------
278
279    static final int MSG_DUMP = 1;
280
281    class H extends Handler {
282        H(Looper looper) {
283            super(looper);
284        }
285
286        @Override
287        public void handleMessage(Message msg) {
288            switch (msg.what) {
289                case MSG_DUMP: {
290                    HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
291                    try {
292                        doDump((FileDescriptor)args.arg1,
293                                (PrintWriter)args.arg2, (String[])args.arg3);
294                    } catch (RuntimeException e) {
295                        ((PrintWriter)args.arg2).println("Exception: " + e);
296                    }
297                    synchronized (args.arg4) {
298                        ((CountDownLatch)args.arg4).countDown();
299                    }
300                    return;
301                }
302            }
303        }
304    }
305
306    class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
307        public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
308            super(mainLooper, conn);
309        }
310
311        public boolean isActive() {
312            return mActive;
313        }
314    }
315
316    final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
317        @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
318            // No need to check for dump permission, since we only give this
319            // interface to the system.
320
321            CountDownLatch latch = new CountDownLatch(1);
322            HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs();
323            sargs.arg1 = fd;
324            sargs.arg2 = fout;
325            sargs.arg3 = args;
326            sargs.arg4 = latch;
327            mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
328            try {
329                if (!latch.await(5, TimeUnit.SECONDS)) {
330                    fout.println("Timeout waiting for dump");
331                }
332            } catch (InterruptedException e) {
333                fout.println("Interrupted waiting for dump");
334            }
335        }
336
337        public void setUsingInputMethod(boolean state) {
338        }
339
340        public void onBindMethod(InputBindResult res) {
341            synchronized (mH) {
342                if (mBindSequence < 0 || mBindSequence != res.sequence) {
343                    Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
344                            + ", given seq=" + res.sequence);
345                    return;
346                }
347
348                mCurMethod = res.method;
349                mCurId = res.id;
350                mBindSequence = res.sequence;
351            }
352            startInputInner();
353        }
354
355        public void onUnbindMethod(int sequence) {
356            synchronized (mH) {
357                if (mBindSequence == sequence) {
358                    if (false) {
359                        // XXX the server has already unbound!
360                        if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
361                            try {
362                                mCurMethod.finishInput();
363                            } catch (RemoteException e) {
364                                Log.w(TAG, "IME died: " + mCurId, e);
365                            }
366                        }
367                    }
368                    clearBindingLocked();
369
370                    // If we were actively using the last input method, then
371                    // we would like to re-connect to the next input method.
372                    if (mServedView != null && mServedView.isFocused()) {
373                        mServedConnecting = true;
374                    }
375                }
376                startInputInner();
377            }
378        }
379
380        public void setActive(boolean active) {
381            mActive = active;
382            mFullscreenMode = false;
383        }
384    };
385
386    final InputConnection mDummyInputConnection = new BaseInputConnection(this, true);
387
388    InputMethodManager(IInputMethodManager service, Looper looper) {
389        mService = service;
390        mMainLooper = looper;
391        mH = new H(looper);
392        mIInputContext = new ControlledInputConnectionWrapper(looper,
393                mDummyInputConnection);
394
395        if (mInstance == null) {
396            mInstance = this;
397        }
398    }
399
400    /**
401     * Retrieve the global InputMethodManager instance, creating it if it
402     * doesn't already exist.
403     * @hide
404     */
405    static public InputMethodManager getInstance(Context context) {
406        synchronized (mInstanceSync) {
407            if (mInstance != null) {
408                return mInstance;
409            }
410            IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
411            IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
412            mInstance = new InputMethodManager(service, context.getMainLooper());
413        }
414        return mInstance;
415    }
416
417    /**
418     * Private optimization: retrieve the global InputMethodManager instance,
419     * if it exists.
420     * @hide
421     */
422    static public InputMethodManager peekInstance() {
423        return mInstance;
424    }
425
426    /** @hide */
427    public IInputMethodClient getClient() {
428        return mClient;
429    }
430
431    /** @hide */
432    public IInputContext getInputContext() {
433        return mIInputContext;
434    }
435
436    public List<InputMethodInfo> getInputMethodList() {
437        try {
438            return mService.getInputMethodList();
439        } catch (RemoteException e) {
440            throw new RuntimeException(e);
441        }
442    }
443
444    public List<InputMethodInfo> getEnabledInputMethodList() {
445        try {
446            return mService.getEnabledInputMethodList();
447        } catch (RemoteException e) {
448            throw new RuntimeException(e);
449        }
450    }
451
452    public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
453        try {
454            mService.updateStatusIcon(imeToken, packageName, iconId);
455        } catch (RemoteException e) {
456            throw new RuntimeException(e);
457        }
458    }
459
460    public void hideStatusIcon(IBinder imeToken) {
461        try {
462            mService.updateStatusIcon(imeToken, null, 0);
463        } catch (RemoteException e) {
464            throw new RuntimeException(e);
465        }
466    }
467
468    /** @hide */
469    public void setFullscreenMode(boolean enabled) {
470        mFullscreenMode = true;
471    }
472
473    /**
474     * Allows you to discover whether the attached input method is running
475     * in fullscreen mode.  Return true if it is fullscreen, entirely covering
476     * your UI, else returns false.
477     */
478    public boolean isFullscreenMode() {
479        return mFullscreenMode;
480    }
481
482    /**
483     * Return true if the given view is the currently active view for the
484     * input method.
485     */
486    public boolean isActive(View view) {
487        synchronized (mH) {
488            return mServedView == view && mCurrentTextBoxAttribute != null;
489        }
490    }
491
492    /**
493     * Return true if any view is currently active in the input method.
494     */
495    public boolean isActive() {
496        synchronized (mH) {
497            return mServedView != null && mCurrentTextBoxAttribute != null;
498        }
499    }
500
501    /**
502     * Return true if the currently served view is accepting full text edits.
503     * If false, it has no input connection, so can only handle raw key events.
504     */
505    public boolean isAcceptingText() {
506        return mServedInputConnection != null;
507    }
508
509    /**
510     * Reset all of the state associated with being bound to an input method.
511     */
512    void clearBindingLocked() {
513        clearConnectionLocked();
514        mBindSequence = -1;
515        mCurId = null;
516        mCurMethod = null;
517    }
518
519    /**
520     * Reset all of the state associated with a served view being connected
521     * to an input method
522     */
523    void clearConnectionLocked() {
524        mCurrentTextBoxAttribute = null;
525        mServedInputConnection = null;
526    }
527
528    /**
529     * Disconnect any existing input connection, clearing the served view.
530     */
531    void finishInputLocked() {
532        if (mServedView != null) {
533            if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
534
535            if (mCurrentTextBoxAttribute != null) {
536                try {
537                    mService.finishInput(mClient);
538                } catch (RemoteException e) {
539                }
540            }
541
542            if (mServedInputConnection != null) {
543                // We need to tell the previously served view that it is no
544                // longer the input target, so it can reset its state.  Schedule
545                // this call on its window's Handler so it will be on the correct
546                // thread and outside of our lock.
547                Handler vh = mServedView.getHandler();
548                if (vh != null) {
549                    // This will result in a call to reportFinishInputConnection()
550                    // below.
551                    vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION,
552                            mServedInputConnection));
553                }
554            }
555
556            mServedView = null;
557            mCompletions = null;
558            mServedConnecting = false;
559            clearConnectionLocked();
560        }
561    }
562
563    /**
564     * Called from the FINISH_INPUT_CONNECTION message above.
565     * @hide
566     */
567    public void reportFinishInputConnection(InputConnection ic) {
568        if (mServedInputConnection != ic) {
569            ic.finishComposingText();
570        }
571    }
572
573    public void displayCompletions(View view, CompletionInfo[] completions) {
574        synchronized (mH) {
575            if (mServedView != view) {
576                return;
577            }
578
579            mCompletions = completions;
580            if (mCurMethod != null) {
581                try {
582                    mCurMethod.displayCompletions(mCompletions);
583                } catch (RemoteException e) {
584                }
585            }
586        }
587    }
588
589    public void updateExtractedText(View view, int token, ExtractedText text) {
590        synchronized (mH) {
591            if (mServedView != view) {
592                return;
593            }
594
595            if (mCurMethod != null) {
596                try {
597                    mCurMethod.updateExtractedText(token, text);
598                } catch (RemoteException e) {
599                }
600            }
601        }
602    }
603
604    /**
605     * Flag for {@link #showSoftInput} to indicate that this is an implicit
606     * request to show the input window, not as the result of a direct request
607     * by the user.  The window may not be shown in this case.
608     */
609    public static final int SHOW_IMPLICIT = 0x0001;
610
611    /**
612     * Flag for {@link #showSoftInput} to indicate that the user has forced
613     * the input method open (such as by long-pressing menu) so it should
614     * not be closed until they explicitly do so.
615     */
616    public static final int SHOW_FORCED = 0x0002;
617
618    /**
619     * Explicitly request that the current input method's soft input area be
620     * shown to the user, if needed.  Call this if the user interacts with
621     * your view in such a way that they have expressed they would like to
622     * start performing input into it.
623     *
624     * @param view The currently focused view, which would like to receive
625     * soft keyboard input.
626     * @param flags Provides additional operating flags.  Currently may be
627     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
628     */
629    public void showSoftInput(View view, int flags) {
630        synchronized (mH) {
631            if (mServedView != view) {
632                return;
633            }
634
635            try {
636                mService.showSoftInput(mClient, flags);
637            } catch (RemoteException e) {
638            }
639        }
640    }
641
642    /** @hide */
643    public void showSoftInputUnchecked(int flags) {
644        try {
645            mService.showSoftInput(mClient, flags);
646        } catch (RemoteException e) {
647        }
648    }
649
650    /**
651     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
652     * input window should only be hidden if it was not explicitly shown
653     * by the user.
654     */
655    public static final int HIDE_IMPLICIT_ONLY = 0x0001;
656
657    /**
658     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
659     * input window should normally be hidden, unless it was originally
660     * shown with {@link #SHOW_FORCED}.
661     */
662    public static final int HIDE_NOT_ALWAYS = 0x0002;
663
664    /**
665     * Request to hide the soft input window from the context of the window
666     * that is currently accepting input.  This should be called as a result
667     * of the user doing some actually than fairly explicitly requests to
668     * have the input window hidden.
669     *
670     * @param windowToken The token of the window that is making the request,
671     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
672     * @param flags Provides additional operating flags.  Currently may be
673     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
674     */
675    public void hideSoftInputFromWindow(IBinder windowToken, int flags) {
676        synchronized (mH) {
677            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
678                return;
679            }
680
681            try {
682                mService.hideSoftInput(mClient, flags);
683            } catch (RemoteException e) {
684            }
685        }
686    }
687
688    /**
689     * If the input method is currently connected to the given view,
690     * restart it with its new contents.  You should call this when the text
691     * within your view changes outside of the normal input method or key
692     * input flow, such as when an application calls TextView.setText().
693     *
694     * @param view The view whose text has changed.
695     */
696    public void restartInput(View view) {
697        synchronized (mH) {
698            if (mServedView != view) {
699                return;
700            }
701
702            mServedConnecting = true;
703        }
704
705        startInputInner();
706    }
707
708    void startInputInner() {
709        final View view;
710        synchronized (mH) {
711            view = mServedView;
712
713            // Make sure we have a window token for the served view.
714            if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
715            if (view == null) {
716                if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
717                return;
718            }
719        }
720
721        // Now we need to get an input connection from the served view.
722        // This is complicated in a couple ways: we can't be holding our lock
723        // when calling out to the view, and we need to make sure we call into
724        // the view on the same thread that is driving its view hierarchy.
725        Handler vh = view.getHandler();
726        if (vh == null) {
727            // If the view doesn't have a handler, something has changed out
728            // from under us, so just bail.
729            if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
730            return;
731        }
732        if (vh.getLooper() != Looper.myLooper()) {
733            // The view is running on a different thread than our own, so
734            // we need to reschedule our work for over there.
735            if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
736            vh.post(new Runnable() {
737                public void run() {
738                    startInputInner();
739                }
740            });
741        }
742
743        // Okay we are now ready to call into the served view and have it
744        // do its stuff.
745        // Life is good: let's hook everything up!
746        EditorInfo tba = new EditorInfo();
747        tba.packageName = view.getContext().getPackageName();
748        tba.fieldId = view.getId();
749        InputConnection ic = view.onCreateInputConnection(tba);
750        if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
751
752        synchronized (mH) {
753            // Now that we are locked again, validate that our state hasn't
754            // changed.
755            if (mServedView != view || !mServedConnecting) {
756                // Something else happened, so abort.
757                if (DEBUG) Log.v(TAG,
758                        "Starting input: finished by someone else (view="
759                        + mServedView + " conn=" + mServedConnecting + ")");
760                return;
761            }
762
763            // If we already have a text box, then this view is already
764            // connected so we want to restart it.
765            final boolean initial = mCurrentTextBoxAttribute == null;
766
767            // Hook 'em up and let 'er rip.
768            mCurrentTextBoxAttribute = tba;
769            mServedConnecting = false;
770            mServedInputConnection = ic;
771            IInputContext servedContext;
772            if (ic != null) {
773                mCursorSelStart = tba.initialSelStart;
774                mCursorSelEnd = tba.initialSelEnd;
775                mCursorCandStart = -1;
776                mCursorCandEnd = -1;
777                mCursorRect.setEmpty();
778                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
779            } else {
780                servedContext = null;
781            }
782
783            try {
784                if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
785                        + ic + " tba=" + tba + " initial=" + initial);
786                InputBindResult res = mService.startInput(mClient,
787                        servedContext, tba, initial, 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            if (mServedView != view) {
834                mCurrentTextBoxAttribute = null;
835            }
836            mServedView = view;
837            mCompletions = null;
838            mServedConnecting = true;
839        }
840
841        startInputInner();
842    }
843
844    /**
845     * Call this when a view loses focus.
846     * @hide
847     */
848    public void focusOut(View view) {
849        InputConnection ic = null;
850        synchronized (mH) {
851            if (DEBUG) Log.v(TAG, "focusOut: " + view
852                    + " mServedView=" + mServedView
853                    + " winFocus=" + view.hasWindowFocus());
854            if (mServedView == view) {
855                ic = mServedInputConnection;
856                // The following code would auto-hide the IME if we end up
857                // with no more views with focus.  This can happen, however,
858                // whenever we go into touch mode, so it ends up hiding
859                // at times when we don't really want it to.  For now it
860                // seems better to just turn it all off.
861                if (false && view.hasWindowFocus()) {
862                    mLastServedView = view;
863                    Handler vh = view.getHandler();
864                    if (vh != null) {
865                        // This will result in a call to checkFocus() below.
866                        vh.removeMessages(ViewRoot.CHECK_FOCUS);
867                        vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS,
868                                view));
869                    }
870                }
871            }
872        }
873
874        if (ic != null) {
875            ic.finishComposingText();
876        }
877    }
878
879    /**
880     * @hide
881     */
882    public void checkFocus(View view) {
883        synchronized (mH) {
884            if (view != mLastServedView) {
885                return;
886            }
887            if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
888                    + " last=" + mLastServedView);
889            if (mServedView == mLastServedView) {
890                finishInputLocked();
891                // In this case, we used to have a focused view on the window,
892                // but no longer do.  We should make sure the input method is
893                // no longer shown, since it serves no purpose.
894                closeCurrentInput();
895            }
896            mLastServedView = null;
897        }
898    }
899
900    void closeCurrentInput() {
901        try {
902            mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS);
903        } catch (RemoteException e) {
904        }
905    }
906
907    /**
908     * Called by ViewRoot the first time it gets window focus.
909     * @hide
910     */
911    public void onWindowFocus(View focusedView, int softInputMode,
912            boolean first, int windowFlags) {
913        synchronized (mH) {
914            if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
915                    + " softInputMode=" + softInputMode
916                    + " first=" + first + " flags=#"
917                    + Integer.toHexString(windowFlags));
918            try {
919                final boolean isTextEditor = focusedView != null &&
920                focusedView.onCheckIsTextEditor();
921                mService.windowGainedFocus(mClient, focusedView != null,
922                        isTextEditor, softInputMode, first, windowFlags);
923            } catch (RemoteException e) {
924            }
925        }
926    }
927
928    /**
929     * Report the current selection range.
930     */
931    public void updateSelection(View view, int selStart, int selEnd,
932            int candidatesStart, int candidatesEnd) {
933        synchronized (mH) {
934            if (mServedView != view || mCurrentTextBoxAttribute == null
935                    || mCurMethod == null) {
936                return;
937            }
938
939            if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
940                    || mCursorCandStart != candidatesStart
941                    || mCursorCandEnd != candidatesEnd) {
942                if (DEBUG) Log.d(TAG, "updateSelection");
943
944                try {
945                    if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
946                    mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
947                            selStart, selEnd, candidatesStart, candidatesEnd);
948                    mCursorSelStart = selStart;
949                    mCursorSelEnd = selEnd;
950                    mCursorCandStart = candidatesStart;
951                    mCursorCandEnd = candidatesEnd;
952                } catch (RemoteException e) {
953                    Log.w(TAG, "IME died: " + mCurId, e);
954                }
955            }
956        }
957    }
958
959    /**
960     * Returns true if the current input method wants to watch the location
961     * of the input editor's cursor in its window.
962     */
963    public boolean isWatchingCursor(View view) {
964        return false;
965    }
966
967    /**
968     * Report the current cursor location in its window.
969     */
970    public void updateCursor(View view, int left, int top, int right, int bottom) {
971        synchronized (mH) {
972            if (mServedView != view || mCurrentTextBoxAttribute == null
973                    || mCurMethod == null) {
974                return;
975            }
976
977            mTmpCursorRect.set(left, top, right, bottom);
978            if (!mCursorRect.equals(mTmpCursorRect)) {
979                if (DEBUG) Log.d(TAG, "updateCursor");
980
981                try {
982                    if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
983                    mCurMethod.updateCursor(mTmpCursorRect);
984                    mCursorRect.set(mTmpCursorRect);
985                } catch (RemoteException e) {
986                    Log.w(TAG, "IME died: " + mCurId, e);
987                }
988            }
989        }
990    }
991
992    /**
993     * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
994     * InputMethodSession.appPrivateCommand()} on the current Input Method.
995     * @param view Optional View that is sending the command, or null if
996     * you want to send the command regardless of the view that is attached
997     * to the input method.
998     * @param action Name of the command to be performed.  This <em>must</em>
999     * be a scoped name, i.e. prefixed with a package name you own, so that
1000     * different developers will not create conflicting commands.
1001     * @param data Any data to include with the command.
1002     */
1003    public void sendAppPrivateCommand(View view, String action, Bundle data) {
1004        synchronized (mH) {
1005            if ((view != null && mServedView != view)
1006                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1007                return;
1008            }
1009            try {
1010                if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1011                mCurMethod.appPrivateCommand(action, data);
1012            } catch (RemoteException e) {
1013                Log.w(TAG, "IME died: " + mCurId, e);
1014            }
1015        }
1016    }
1017
1018    /**
1019     * Force switch to a new input method component.  This can only be called
1020     * from the currently active input method, as validated by the given token.
1021     * @param token Supplies the identifying token given to an input method
1022     * when it was started, which allows it to perform this operation on
1023     * itself.
1024     * @param id The unique identifier for the new input method to be switched to.
1025     */
1026    public void setInputMethod(IBinder token, String id) {
1027        try {
1028            mService.setInputMethod(token, id);
1029        } catch (RemoteException e) {
1030            throw new RuntimeException(e);
1031        }
1032    }
1033
1034    /**
1035     * Close/hide the input method's soft input area, so the user no longer
1036     * sees it or can interact with it.  This can only be called
1037     * from the currently active input method, as validated by the given token.
1038     *
1039     * @param token Supplies the identifying token given to an input method
1040     * when it was started, which allows it to perform this operation on
1041     * itself.
1042     * @param flags Provides additional operating flags.  Currently may be
1043     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
1044     */
1045    public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1046        try {
1047            mService.hideMySoftInput(token, flags);
1048        } catch (RemoteException e) {
1049            throw new RuntimeException(e);
1050        }
1051    }
1052
1053    /**
1054     * @hide
1055     */
1056    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
1057            IInputMethodCallback callback) {
1058        synchronized (mH) {
1059            if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
1060
1061            if (mCurMethod == null) {
1062                try {
1063                    callback.finishedEvent(seq, false);
1064                } catch (RemoteException e) {
1065                }
1066                return;
1067            }
1068
1069            if (key.getAction() == KeyEvent.ACTION_DOWN
1070                    && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
1071                showInputMethodPicker();
1072                try {
1073                    callback.finishedEvent(seq, true);
1074                } catch (RemoteException e) {
1075                }
1076                return;
1077            }
1078            try {
1079                if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
1080                mCurMethod.dispatchKeyEvent(seq, key, callback);
1081            } catch (RemoteException e) {
1082                Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
1083                try {
1084                    callback.finishedEvent(seq, false);
1085                } catch (RemoteException ex) {
1086                }
1087            }
1088        }
1089    }
1090
1091    /**
1092     * @hide
1093     */
1094    void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
1095            IInputMethodCallback callback) {
1096        synchronized (mH) {
1097            if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
1098
1099            if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
1100                try {
1101                    callback.finishedEvent(seq, false);
1102                } catch (RemoteException e) {
1103                }
1104                return;
1105            }
1106
1107            try {
1108                if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
1109                mCurMethod.dispatchTrackballEvent(seq, motion, callback);
1110            } catch (RemoteException e) {
1111                Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
1112                try {
1113                    callback.finishedEvent(seq, false);
1114                } catch (RemoteException ex) {
1115                }
1116            }
1117        }
1118    }
1119
1120    public void showInputMethodPicker() {
1121        synchronized (mH) {
1122            try {
1123                mService.showInputMethodPickerFromClient(mClient);
1124            } catch (RemoteException e) {
1125                Log.w(TAG, "IME died: " + mCurId, e);
1126            }
1127        }
1128    }
1129
1130    void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1131        final Printer p = new PrintWriterPrinter(fout);
1132        p.println("Input method client state for " + this + ":");
1133
1134        p.println("  mService=" + mService);
1135        p.println("  mMainLooper=" + mMainLooper);
1136        p.println("  mIInputContext=" + mIInputContext);
1137        p.println("  mActive=" + mActive
1138                + " mBindSequence=" + mBindSequence
1139                + " mCurId=" + mCurId);
1140        p.println("  mCurMethod=" + mCurMethod);
1141        p.println("  mServedView=" + mServedView);
1142        p.println("  mLastServedView=" + mLastServedView);
1143        p.println("  mServedConnecting=" + mServedConnecting);
1144        if (mCurrentTextBoxAttribute != null) {
1145            p.println("  mCurrentTextBoxAttribute:");
1146            mCurrentTextBoxAttribute.dump(p, "    ");
1147        } else {
1148            p.println("  mCurrentTextBoxAttribute: null");
1149        }
1150        p.println("  mServedInputConnection=" + mServedInputConnection);
1151        p.println("  mCompletions=" + mCompletions);
1152        p.println("  mCursorRect=" + mCursorRect);
1153        p.println("  mCursorSelStart=" + mCursorSelStart
1154                + " mCursorSelEnd=" + mCursorSelEnd
1155                + " mCursorCandStart=" + mCursorCandStart
1156                + " mCursorCandEnd=" + mCursorCandEnd);
1157    }
1158}
1159