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