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