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