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