• 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.inputmethodservice;
18 
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21 
22 import android.app.ActivityManager;
23 import android.app.Dialog;
24 import android.content.Context;
25 import android.content.res.Configuration;
26 import android.content.res.Resources;
27 import android.content.res.TypedArray;
28 import android.graphics.Rect;
29 import android.graphics.Region;
30 import android.os.Bundle;
31 import android.os.IBinder;
32 import android.os.ResultReceiver;
33 import android.os.SystemClock;
34 import android.provider.Settings;
35 import android.text.InputType;
36 import android.text.Layout;
37 import android.text.Spannable;
38 import android.text.method.MovementMethod;
39 import android.util.Log;
40 import android.util.PrintWriterPrinter;
41 import android.util.Printer;
42 import android.view.Gravity;
43 import android.view.KeyCharacterMap;
44 import android.view.KeyEvent;
45 import android.view.LayoutInflater;
46 import android.view.MotionEvent;
47 import android.view.View;
48 import android.view.ViewGroup;
49 import android.view.ViewTreeObserver;
50 import android.view.Window;
51 import android.view.WindowManager;
52 import android.view.WindowManager.BadTokenException;
53 import android.view.animation.AnimationUtils;
54 import android.view.inputmethod.CompletionInfo;
55 import android.view.inputmethod.CursorAnchorInfo;
56 import android.view.inputmethod.EditorInfo;
57 import android.view.inputmethod.ExtractedText;
58 import android.view.inputmethod.ExtractedTextRequest;
59 import android.view.inputmethod.InputBinding;
60 import android.view.inputmethod.InputConnection;
61 import android.view.inputmethod.InputMethod;
62 import android.view.inputmethod.InputMethodManager;
63 import android.view.inputmethod.InputMethodSubtype;
64 import android.widget.Button;
65 import android.widget.FrameLayout;
66 import android.widget.LinearLayout;
67 
68 import java.io.FileDescriptor;
69 import java.io.PrintWriter;
70 
71 /**
72  * InputMethodService provides a standard implementation of an InputMethod,
73  * which final implementations can derive from and customize.  See the
74  * base class {@link AbstractInputMethodService} and the {@link InputMethod}
75  * interface for more information on the basics of writing input methods.
76  *
77  * <p>In addition to the normal Service lifecycle methods, this class
78  * introduces some new specific callbacks that most subclasses will want
79  * to make use of:</p>
80  * <ul>
81  * <li> {@link #onInitializeInterface()} for user-interface initialization,
82  * in particular to deal with configuration changes while the service is
83  * running.
84  * <li> {@link #onBindInput} to find out about switching to a new client.
85  * <li> {@link #onStartInput} to deal with an input session starting with
86  * the client.
87  * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
88  * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
89  * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
90  * starting within the input area of the IME.
91  * </ul>
92  *
93  * <p>An input method has significant discretion in how it goes about its
94  * work: the {@link android.inputmethodservice.InputMethodService} provides
95  * a basic framework for standard UI elements (input view, candidates view,
96  * and running in fullscreen mode), but it is up to a particular implementor
97  * to decide how to use them.  For example, one input method could implement
98  * an input area with a keyboard, another could allow the user to draw text,
99  * while a third could have no input area (and thus not be visible to the
100  * user) but instead listen to audio and perform text to speech conversion.</p>
101  *
102  * <p>In the implementation provided here, all of these elements are placed
103  * together in a single window managed by the InputMethodService.  It will
104  * execute callbacks as it needs information about them, and provides APIs for
105  * programmatic control over them.  They layout of these elements is explicitly
106  * defined:</p>
107  *
108  * <ul>
109  * <li>The soft input view, if available, is placed at the bottom of the
110  * screen.
111  * <li>The candidates view, if currently shown, is placed above the soft
112  * input view.
113  * <li>If not running fullscreen, the application is moved or resized to be
114  * above these views; if running fullscreen, the window will completely cover
115  * the application and its top part will contain the extract text of what is
116  * currently being edited by the application.
117  * </ul>
118  *
119  *
120  * <a name="SoftInputView"></a>
121  * <h3>Soft Input View</h3>
122  *
123  * <p>Central to most input methods is the soft input view.  This is where most
124  * user interaction occurs: pressing on soft keys, drawing characters, or
125  * however else your input method wants to generate text.  Most implementations
126  * will simply have their own view doing all of this work, and return a new
127  * instance of it when {@link #onCreateInputView()} is called.  At that point,
128  * as long as the input view is visible, you will see user interaction in
129  * that view and can call back on the InputMethodService to interact with the
130  * application as appropriate.</p>
131  *
132  * <p>There are some situations where you want to decide whether or not your
133  * soft input view should be shown to the user.  This is done by implementing
134  * the {@link #onEvaluateInputViewShown()} to return true or false based on
135  * whether it should be shown in the current environment.  If any of your
136  * state has changed that may impact this, call
137  * {@link #updateInputViewShown()} to have it re-evaluated.  The default
138  * implementation always shows the input view unless there is a hard
139  * keyboard available, which is the appropriate behavior for most input
140  * methods.</p>
141  *
142  *
143  * <a name="CandidatesView"></a>
144  * <h3>Candidates View</h3>
145  *
146  * <p>Often while the user is generating raw text, an input method wants to
147  * provide them with a list of possible interpretations of that text that can
148  * be selected for use.  This is accomplished with the candidates view, and
149  * like the soft input view you implement {@link #onCreateCandidatesView()}
150  * to instantiate your own view implementing your candidates UI.</p>
151  *
152  * <p>Management of the candidates view is a little different than the input
153  * view, because the candidates view tends to be more transient, being shown
154  * only when there are possible candidates for the current text being entered
155  * by the user.  To control whether the candidates view is shown, you use
156  * {@link #setCandidatesViewShown(boolean)}.  Note that because the candidate
157  * view tends to be shown and hidden a lot, it does not impact the application
158  * UI in the same way as the soft input view: it will never cause application
159  * windows to resize, only cause them to be panned if needed for the user to
160  * see the current focus.</p>
161  *
162  *
163  * <a name="FullscreenMode"></a>
164  * <h3>Fullscreen Mode</h3>
165  *
166  * <p>Sometimes your input method UI is too large to integrate with the
167  * application UI, so you just want to take over the screen.  This is
168  * accomplished by switching to full-screen mode, causing the input method
169  * window to fill the entire screen and add its own "extracted text" editor
170  * showing the user the text that is being typed.  Unlike the other UI elements,
171  * there is a standard implementation for the extract editor that you should
172  * not need to change.  The editor is placed at the top of the IME, above the
173  * input and candidates views.</p>
174  *
175  * <p>Similar to the input view, you control whether the IME is running in
176  * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
177  * to return true or false based on
178  * whether it should be fullscreen in the current environment.  If any of your
179  * state has changed that may impact this, call
180  * {@link #updateFullscreenMode()} to have it re-evaluated.  The default
181  * implementation selects fullscreen mode when the screen is in a landscape
182  * orientation, which is appropriate behavior for most input methods that have
183  * a significant input area.</p>
184  *
185  * <p>When in fullscreen mode, you have some special requirements because the
186  * user can not see the application UI.  In particular, you should implement
187  * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
188  * generated by your application, typically in your candidates view like you
189  * would normally show candidates.
190  *
191  *
192  * <a name="GeneratingText"></a>
193  * <h3>Generating Text</h3>
194  *
195  * <p>The key part of an IME is of course generating text for the application.
196  * This is done through calls to the
197  * {@link android.view.inputmethod.InputConnection} interface to the
198  * application, which can be retrieved from {@link #getCurrentInputConnection()}.
199  * This interface allows you to generate raw key events or, if the target
200  * supports it, directly edit in strings of candidates and committed text.</p>
201  *
202  * <p>Information about what the target is expected and supports can be found
203  * through the {@link android.view.inputmethod.EditorInfo} class, which is
204  * retrieved with {@link #getCurrentInputEditorInfo()} method.  The most
205  * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
206  * EditorInfo.inputType}; in particular, if this is
207  * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
208  * then the target does not support complex edits and you need to only deliver
209  * raw key events to it.  An input method will also want to look at other
210  * values here, to for example detect password mode, auto complete text views,
211  * phone number entry, etc.</p>
212  *
213  * <p>When the user switches between input targets, you will receive calls to
214  * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
215  * You can use these to reset and initialize your input state for the current
216  * target.  For example, you will often want to clear any input state, and
217  * update a soft keyboard to be appropriate for the new inputType.</p>
218  *
219  * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
220  * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
221  * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
222  */
223 public class InputMethodService extends AbstractInputMethodService {
224     static final String TAG = "InputMethodService";
225     static final boolean DEBUG = false;
226 
227     /**
228      * The back button will close the input window.
229      */
230     public static final int BACK_DISPOSITION_DEFAULT = 0;  // based on window
231 
232     /**
233      * This input method will not consume the back key.
234      */
235     public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // back
236 
237     /**
238      * This input method will consume the back key.
239      */
240     public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // down
241 
242     /**
243      * @hide
244      * The IME is active.  It may or may not be visible.
245      */
246     public static final int IME_ACTIVE = 0x1;
247 
248     /**
249      * @hide
250      * The IME is visible.
251      */
252     public static final int IME_VISIBLE = 0x2;
253 
254     InputMethodManager mImm;
255 
256     int mTheme = 0;
257     boolean mHardwareAccelerated = false;
258 
259     LayoutInflater mInflater;
260     TypedArray mThemeAttrs;
261     View mRootView;
262     SoftInputWindow mWindow;
263     boolean mInitialized;
264     boolean mWindowCreated;
265     boolean mWindowAdded;
266     boolean mWindowVisible;
267     boolean mWindowWasVisible;
268     boolean mInShowWindow;
269     ViewGroup mFullscreenArea;
270     FrameLayout mExtractFrame;
271     FrameLayout mCandidatesFrame;
272     FrameLayout mInputFrame;
273 
274     IBinder mToken;
275 
276     InputBinding mInputBinding;
277     InputConnection mInputConnection;
278     boolean mInputStarted;
279     boolean mInputViewStarted;
280     boolean mCandidatesViewStarted;
281     InputConnection mStartedInputConnection;
282     EditorInfo mInputEditorInfo;
283 
284     int mShowInputFlags;
285     boolean mShowInputRequested;
286     boolean mLastShowInputRequested;
287     int mCandidatesVisibility;
288     CompletionInfo[] mCurCompletions;
289 
290     boolean mShowInputForced;
291 
292     boolean mFullscreenApplied;
293     boolean mIsFullscreen;
294     View mExtractView;
295     boolean mExtractViewHidden;
296     ExtractEditText mExtractEditText;
297     ViewGroup mExtractAccessories;
298     Button mExtractAction;
299     ExtractedText mExtractedText;
300     int mExtractedToken;
301 
302     View mInputView;
303     boolean mIsInputViewShown;
304 
305     int mStatusIcon;
306     int mBackDisposition;
307 
308     final Insets mTmpInsets = new Insets();
309     final int[] mTmpLocation = new int[2];
310 
311     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
312             new ViewTreeObserver.OnComputeInternalInsetsListener() {
313         public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
314             if (isExtractViewShown()) {
315                 // In true fullscreen mode, we just say the window isn't covering
316                 // any content so we don't impact whatever is behind.
317                 View decor = getWindow().getWindow().getDecorView();
318                 info.contentInsets.top = info.visibleInsets.top
319                         = decor.getHeight();
320                 info.touchableRegion.setEmpty();
321                 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
322             } else {
323                 onComputeInsets(mTmpInsets);
324                 info.contentInsets.top = mTmpInsets.contentTopInsets;
325                 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
326                 info.touchableRegion.set(mTmpInsets.touchableRegion);
327                 info.setTouchableInsets(mTmpInsets.touchableInsets);
328             }
329         }
330     };
331 
332     final View.OnClickListener mActionClickListener = new View.OnClickListener() {
333         public void onClick(View v) {
334             final EditorInfo ei = getCurrentInputEditorInfo();
335             final InputConnection ic = getCurrentInputConnection();
336             if (ei != null && ic != null) {
337                 if (ei.actionId != 0) {
338                     ic.performEditorAction(ei.actionId);
339                 } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION)
340                         != EditorInfo.IME_ACTION_NONE) {
341                     ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
342                 }
343             }
344         }
345     };
346 
347     /**
348      * Concrete implementation of
349      * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
350      * all of the standard behavior for an input method.
351      */
352     public class InputMethodImpl extends AbstractInputMethodImpl {
353         /**
354          * Take care of attaching the given window token provided by the system.
355          */
attachToken(IBinder token)356         public void attachToken(IBinder token) {
357             if (mToken == null) {
358                 mToken = token;
359                 mWindow.setToken(token);
360             }
361         }
362 
363         /**
364          * Handle a new input binding, calling
365          * {@link InputMethodService#onBindInput InputMethodService.onBindInput()}
366          * when done.
367          */
bindInput(InputBinding binding)368         public void bindInput(InputBinding binding) {
369             mInputBinding = binding;
370             mInputConnection = binding.getConnection();
371             if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
372                     + " ic=" + mInputConnection);
373             InputConnection ic = getCurrentInputConnection();
374             if (ic != null) ic.reportFullscreenMode(mIsFullscreen);
375             initialize();
376             onBindInput();
377         }
378 
379         /**
380          * Clear the current input binding.
381          */
unbindInput()382         public void unbindInput() {
383             if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
384                     + " ic=" + mInputConnection);
385             onUnbindInput();
386             mInputBinding = null;
387             mInputConnection = null;
388         }
389 
startInput(InputConnection ic, EditorInfo attribute)390         public void startInput(InputConnection ic, EditorInfo attribute) {
391             if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
392             doStartInput(ic, attribute, false);
393         }
394 
restartInput(InputConnection ic, EditorInfo attribute)395         public void restartInput(InputConnection ic, EditorInfo attribute) {
396             if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
397             doStartInput(ic, attribute, true);
398         }
399 
400         /**
401          * Handle a request by the system to hide the soft input area.
402          */
hideSoftInput(int flags, ResultReceiver resultReceiver)403         public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
404             if (DEBUG) Log.v(TAG, "hideSoftInput()");
405             boolean wasVis = isInputViewShown();
406             mShowInputFlags = 0;
407             mShowInputRequested = false;
408             mShowInputForced = false;
409             doHideWindow();
410             if (resultReceiver != null) {
411                 resultReceiver.send(wasVis != isInputViewShown()
412                         ? InputMethodManager.RESULT_HIDDEN
413                         : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
414                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
415             }
416         }
417 
418         /**
419          * Handle a request by the system to show the soft input area.
420          */
showSoftInput(int flags, ResultReceiver resultReceiver)421         public void showSoftInput(int flags, ResultReceiver resultReceiver) {
422             if (DEBUG) Log.v(TAG, "showSoftInput()");
423             boolean wasVis = isInputViewShown();
424             mShowInputFlags = 0;
425             if (onShowInputRequested(flags, false)) {
426                 try {
427                     showWindow(true);
428                 } catch (BadTokenException e) {
429                     if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
430                     mWindowVisible = false;
431                     mWindowAdded = false;
432                 }
433             }
434             // If user uses hard keyboard, IME button should always be shown.
435             boolean showing = isInputViewShown();
436             mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
437                     mBackDisposition);
438             if (resultReceiver != null) {
439                 resultReceiver.send(wasVis != isInputViewShown()
440                         ? InputMethodManager.RESULT_SHOWN
441                         : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
442                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
443             }
444         }
445 
changeInputMethodSubtype(InputMethodSubtype subtype)446         public void changeInputMethodSubtype(InputMethodSubtype subtype) {
447             onCurrentInputMethodSubtypeChanged(subtype);
448         }
449     }
450 
451     /**
452      * Concrete implementation of
453      * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
454      * all of the standard behavior for an input method session.
455      */
456     public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
finishInput()457         public void finishInput() {
458             if (!isEnabled()) {
459                 return;
460             }
461             if (DEBUG) Log.v(TAG, "finishInput() in " + this);
462             doFinishInput();
463         }
464 
465         /**
466          * Call {@link InputMethodService#onDisplayCompletions
467          * InputMethodService.onDisplayCompletions()}.
468          */
displayCompletions(CompletionInfo[] completions)469         public void displayCompletions(CompletionInfo[] completions) {
470             if (!isEnabled()) {
471                 return;
472             }
473             mCurCompletions = completions;
474             onDisplayCompletions(completions);
475         }
476 
477         /**
478          * Call {@link InputMethodService#onUpdateExtractedText
479          * InputMethodService.onUpdateExtractedText()}.
480          */
updateExtractedText(int token, ExtractedText text)481         public void updateExtractedText(int token, ExtractedText text) {
482             if (!isEnabled()) {
483                 return;
484             }
485             onUpdateExtractedText(token, text);
486         }
487 
488         /**
489          * Call {@link InputMethodService#onUpdateSelection
490          * InputMethodService.onUpdateSelection()}.
491          */
updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)492         public void updateSelection(int oldSelStart, int oldSelEnd,
493                 int newSelStart, int newSelEnd,
494                 int candidatesStart, int candidatesEnd) {
495             if (!isEnabled()) {
496                 return;
497             }
498             InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
499                     newSelStart, newSelEnd, candidatesStart, candidatesEnd);
500         }
501 
502         @Override
viewClicked(boolean focusChanged)503         public void viewClicked(boolean focusChanged) {
504             if (!isEnabled()) {
505                 return;
506             }
507             InputMethodService.this.onViewClicked(focusChanged);
508         }
509 
510         /**
511          * Call {@link InputMethodService#onUpdateCursor
512          * InputMethodService.onUpdateCursor()}.
513          */
updateCursor(Rect newCursor)514         public void updateCursor(Rect newCursor) {
515             if (!isEnabled()) {
516                 return;
517             }
518             InputMethodService.this.onUpdateCursor(newCursor);
519         }
520 
521         /**
522          * Call {@link InputMethodService#onAppPrivateCommand
523          * InputMethodService.onAppPrivateCommand()}.
524          */
appPrivateCommand(String action, Bundle data)525         public void appPrivateCommand(String action, Bundle data) {
526             if (!isEnabled()) {
527                 return;
528             }
529             InputMethodService.this.onAppPrivateCommand(action, data);
530         }
531 
532         /**
533          *
534          */
toggleSoftInput(int showFlags, int hideFlags)535         public void toggleSoftInput(int showFlags, int hideFlags) {
536             InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
537         }
538 
539         /**
540          * Call {@link InputMethodService#onUpdateCursorAnchorInfo
541          * InputMethodService.onUpdateCursorAnchorInfo()}.
542          */
updateCursorAnchorInfo(CursorAnchorInfo info)543         public void updateCursorAnchorInfo(CursorAnchorInfo info) {
544             if (!isEnabled()) {
545                 return;
546             }
547             InputMethodService.this.onUpdateCursorAnchorInfo(info);
548         }
549     }
550 
551     /**
552      * Information about where interesting parts of the input method UI appear.
553      */
554     public static final class Insets {
555         /**
556          * This is the top part of the UI that is the main content.  It is
557          * used to determine the basic space needed, to resize/pan the
558          * application behind.  It is assumed that this inset does not
559          * change very much, since any change will cause a full resize/pan
560          * of the application behind.  This value is relative to the top edge
561          * of the input method window.
562          */
563         public int contentTopInsets;
564 
565         /**
566          * This is the top part of the UI that is visibly covering the
567          * application behind it.  This provides finer-grained control over
568          * visibility, allowing you to change it relatively frequently (such
569          * as hiding or showing candidates) without disrupting the underlying
570          * UI too much.  For example, this will never resize the application
571          * UI, will only pan if needed to make the current focus visible, and
572          * will not aggressively move the pan position when this changes unless
573          * needed to make the focus visible.  This value is relative to the top edge
574          * of the input method window.
575          */
576         public int visibleTopInsets;
577 
578         /**
579          * This is the region of the UI that is touchable.  It is used when
580          * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
581          * The region should be specified relative to the origin of the window frame.
582          */
583         public final Region touchableRegion = new Region();
584 
585         /**
586          * Option for {@link #touchableInsets}: the entire window frame
587          * can be touched.
588          */
589         public static final int TOUCHABLE_INSETS_FRAME
590                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
591 
592         /**
593          * Option for {@link #touchableInsets}: the area inside of
594          * the content insets can be touched.
595          */
596         public static final int TOUCHABLE_INSETS_CONTENT
597                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
598 
599         /**
600          * Option for {@link #touchableInsets}: the area inside of
601          * the visible insets can be touched.
602          */
603         public static final int TOUCHABLE_INSETS_VISIBLE
604                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
605 
606         /**
607          * Option for {@link #touchableInsets}: the region specified by
608          * {@link #touchableRegion} can be touched.
609          */
610         public static final int TOUCHABLE_INSETS_REGION
611                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
612 
613         /**
614          * Determine which area of the window is touchable by the user.  May
615          * be one of: {@link #TOUCHABLE_INSETS_FRAME},
616          * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
617          * or {@link #TOUCHABLE_INSETS_REGION}.
618          */
619         public int touchableInsets;
620     }
621 
622     /**
623      * You can call this to customize the theme used by your IME's window.
624      * This theme should typically be one that derives from
625      * {@link android.R.style#Theme_InputMethod}, which is the default theme
626      * you will get.  This must be set before {@link #onCreate}, so you
627      * will typically call it in your constructor with the resource ID
628      * of your custom theme.
629      */
630     @Override
setTheme(int theme)631     public void setTheme(int theme) {
632         if (mWindow != null) {
633             throw new IllegalStateException("Must be called before onCreate()");
634         }
635         mTheme = theme;
636     }
637 
638     /**
639      * You can call this to try to enable hardware accelerated drawing for
640      * your IME. This must be set before {@link #onCreate}, so you
641      * will typically call it in your constructor.  It is not always possible
642      * to use hardware accelerated drawing in an IME (for example on low-end
643      * devices that do not have the resources to support this), so the call
644      * returns true if it succeeds otherwise false if you will need to draw
645      * in software.  You must be able to handle either case.
646      *
647      * @deprecated Starting in API 21, hardware acceleration is always enabled
648      *             on capable devices.
649      */
enableHardwareAcceleration()650     public boolean enableHardwareAcceleration() {
651         if (mWindow != null) {
652             throw new IllegalStateException("Must be called before onCreate()");
653         }
654         if (ActivityManager.isHighEndGfx()) {
655             mHardwareAccelerated = true;
656             return true;
657         }
658         return false;
659     }
660 
onCreate()661     @Override public void onCreate() {
662         mTheme = Resources.selectSystemTheme(mTheme,
663                 getApplicationInfo().targetSdkVersion,
664                 android.R.style.Theme_InputMethod,
665                 android.R.style.Theme_Holo_InputMethod,
666                 android.R.style.Theme_DeviceDefault_InputMethod,
667                 android.R.style.Theme_DeviceDefault_InputMethod);
668         super.setTheme(mTheme);
669         super.onCreate();
670         mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
671         mInflater = (LayoutInflater)getSystemService(
672                 Context.LAYOUT_INFLATER_SERVICE);
673         mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
674                 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
675         if (mHardwareAccelerated) {
676             mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
677         }
678         initViews();
679         mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
680     }
681 
682     /**
683      * This is a hook that subclasses can use to perform initialization of
684      * their interface.  It is called for you prior to any of your UI objects
685      * being created, both after the service is first created and after a
686      * configuration change happens.
687      */
onInitializeInterface()688     public void onInitializeInterface() {
689         // Intentionally empty
690     }
691 
initialize()692     void initialize() {
693         if (!mInitialized) {
694             mInitialized = true;
695             onInitializeInterface();
696         }
697     }
698 
initViews()699     void initViews() {
700         mInitialized = false;
701         mWindowCreated = false;
702         mShowInputRequested = false;
703         mShowInputForced = false;
704 
705         mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
706         mRootView = mInflater.inflate(
707                 com.android.internal.R.layout.input_method, null);
708         mRootView.setSystemUiVisibility(
709                 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
710         mWindow.setContentView(mRootView);
711         mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
712         if (Settings.Global.getInt(getContentResolver(),
713                 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
714             mWindow.getWindow().setWindowAnimations(
715                     com.android.internal.R.style.Animation_InputMethodFancy);
716         }
717         mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
718         mExtractViewHidden = false;
719         mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
720         mExtractView = null;
721         mExtractEditText = null;
722         mExtractAccessories = null;
723         mExtractAction = null;
724         mFullscreenApplied = false;
725 
726         mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
727         mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
728         mInputView = null;
729         mIsInputViewShown = false;
730 
731         mExtractFrame.setVisibility(View.GONE);
732         mCandidatesVisibility = getCandidatesHiddenVisibility();
733         mCandidatesFrame.setVisibility(mCandidatesVisibility);
734         mInputFrame.setVisibility(View.GONE);
735     }
736 
onDestroy()737     @Override public void onDestroy() {
738         super.onDestroy();
739         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
740                 mInsetsComputer);
741         doFinishInput();
742         if (mWindowAdded) {
743             // Disable exit animation for the current IME window
744             // to avoid the race condition between the exit and enter animations
745             // when the current IME is being switched to another one.
746             mWindow.getWindow().setWindowAnimations(0);
747             mWindow.dismiss();
748         }
749     }
750 
751     /**
752      * Take care of handling configuration changes.  Subclasses of
753      * InputMethodService generally don't need to deal directly with
754      * this on their own; the standard implementation here takes care of
755      * regenerating the input method UI as a result of the configuration
756      * change, so you can rely on your {@link #onCreateInputView} and
757      * other methods being called as appropriate due to a configuration change.
758      *
759      * <p>When a configuration change does happen,
760      * {@link #onInitializeInterface()} is guaranteed to be called the next
761      * time prior to any of the other input or UI creation callbacks.  The
762      * following will be called immediately depending if appropriate for current
763      * state: {@link #onStartInput} if input is active, and
764      * {@link #onCreateInputView} and {@link #onStartInputView} and related
765      * appropriate functions if the UI is displayed.
766      */
onConfigurationChanged(Configuration newConfig)767     @Override public void onConfigurationChanged(Configuration newConfig) {
768         super.onConfigurationChanged(newConfig);
769 
770         boolean visible = mWindowVisible;
771         int showFlags = mShowInputFlags;
772         boolean showingInput = mShowInputRequested;
773         CompletionInfo[] completions = mCurCompletions;
774         initViews();
775         mInputViewStarted = false;
776         mCandidatesViewStarted = false;
777         if (mInputStarted) {
778             doStartInput(getCurrentInputConnection(),
779                     getCurrentInputEditorInfo(), true);
780         }
781         if (visible) {
782             if (showingInput) {
783                 // If we were last showing the soft keyboard, try to do so again.
784                 if (onShowInputRequested(showFlags, true)) {
785                     showWindow(true);
786                     if (completions != null) {
787                         mCurCompletions = completions;
788                         onDisplayCompletions(completions);
789                     }
790                 } else {
791                     doHideWindow();
792                 }
793             } else if (mCandidatesVisibility == View.VISIBLE) {
794                 // If the candidates are currently visible, make sure the
795                 // window is shown for them.
796                 showWindow(false);
797             } else {
798                 // Otherwise hide the window.
799                 doHideWindow();
800             }
801             // If user uses hard keyboard, IME button should always be shown.
802             boolean showing = onEvaluateInputViewShown();
803             mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
804                     mBackDisposition);
805         }
806     }
807 
808     /**
809      * Implement to return our standard {@link InputMethodImpl}.  Subclasses
810      * can override to provide their own customized version.
811      */
812     @Override
onCreateInputMethodInterface()813     public AbstractInputMethodImpl onCreateInputMethodInterface() {
814         return new InputMethodImpl();
815     }
816 
817     /**
818      * Implement to return our standard {@link InputMethodSessionImpl}.  Subclasses
819      * can override to provide their own customized version.
820      */
821     @Override
onCreateInputMethodSessionInterface()822     public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
823         return new InputMethodSessionImpl();
824     }
825 
getLayoutInflater()826     public LayoutInflater getLayoutInflater() {
827         return mInflater;
828     }
829 
getWindow()830     public Dialog getWindow() {
831         return mWindow;
832     }
833 
setBackDisposition(int disposition)834     public void setBackDisposition(int disposition) {
835         mBackDisposition = disposition;
836     }
837 
getBackDisposition()838     public int getBackDisposition() {
839         return mBackDisposition;
840     }
841 
842     /**
843      * Return the maximum width, in pixels, available the input method.
844      * Input methods are positioned at the bottom of the screen and, unless
845      * running in fullscreen, will generally want to be as short as possible
846      * so should compute their height based on their contents.  However, they
847      * can stretch as much as needed horizontally.  The function returns to
848      * you the maximum amount of space available horizontally, which you can
849      * use if needed for UI placement.
850      *
851      * <p>In many cases this is not needed, you can just rely on the normal
852      * view layout mechanisms to position your views within the full horizontal
853      * space given to the input method.
854      *
855      * <p>Note that this value can change dynamically, in particular when the
856      * screen orientation changes.
857      */
getMaxWidth()858     public int getMaxWidth() {
859         WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
860         return wm.getDefaultDisplay().getWidth();
861     }
862 
863     /**
864      * Return the currently active InputBinding for the input method, or
865      * null if there is none.
866      */
getCurrentInputBinding()867     public InputBinding getCurrentInputBinding() {
868         return mInputBinding;
869     }
870 
871     /**
872      * Retrieve the currently active InputConnection that is bound to
873      * the input method, or null if there is none.
874      */
getCurrentInputConnection()875     public InputConnection getCurrentInputConnection() {
876         InputConnection ic = mStartedInputConnection;
877         if (ic != null) {
878             return ic;
879         }
880         return mInputConnection;
881     }
882 
getCurrentInputStarted()883     public boolean getCurrentInputStarted() {
884         return mInputStarted;
885     }
886 
getCurrentInputEditorInfo()887     public EditorInfo getCurrentInputEditorInfo() {
888         return mInputEditorInfo;
889     }
890 
891     /**
892      * Re-evaluate whether the input method should be running in fullscreen
893      * mode, and update its UI if this has changed since the last time it
894      * was evaluated.  This will call {@link #onEvaluateFullscreenMode()} to
895      * determine whether it should currently run in fullscreen mode.  You
896      * can use {@link #isFullscreenMode()} to determine if the input method
897      * is currently running in fullscreen mode.
898      */
updateFullscreenMode()899     public void updateFullscreenMode() {
900         boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
901         boolean changed = mLastShowInputRequested != mShowInputRequested;
902         if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
903             changed = true;
904             mIsFullscreen = isFullscreen;
905             InputConnection ic = getCurrentInputConnection();
906             if (ic != null) ic.reportFullscreenMode(isFullscreen);
907             mFullscreenApplied = true;
908             initialize();
909             LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
910                     mFullscreenArea.getLayoutParams();
911             if (isFullscreen) {
912                 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
913                         com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
914                 lp.height = 0;
915                 lp.weight = 1;
916             } else {
917                 mFullscreenArea.setBackgroundDrawable(null);
918                 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
919                 lp.weight = 0;
920             }
921             ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
922                     mFullscreenArea, lp);
923             if (isFullscreen) {
924                 if (mExtractView == null) {
925                     View v = onCreateExtractTextView();
926                     if (v != null) {
927                         setExtractView(v);
928                     }
929                 }
930                 startExtractingText(false);
931             }
932             updateExtractFrameVisibility();
933         }
934 
935         if (changed) {
936             onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
937             mLastShowInputRequested = mShowInputRequested;
938         }
939     }
940 
941     /**
942      * Update the given window's parameters for the given mode.  This is called
943      * when the window is first displayed and each time the fullscreen or
944      * candidates only mode changes.
945      *
946      * <p>The default implementation makes the layout for the window
947      * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
948      * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
949      *
950      * @param win The input method's window.
951      * @param isFullscreen If true, the window is running in fullscreen mode
952      * and intended to cover the entire application display.
953      * @param isCandidatesOnly If true, the window is only showing the
954      * candidates view and none of the rest of its UI.  This is mutually
955      * exclusive with fullscreen mode.
956      */
onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly)957     public void onConfigureWindow(Window win, boolean isFullscreen,
958             boolean isCandidatesOnly) {
959         final int currentHeight = mWindow.getWindow().getAttributes().height;
960         final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
961         if (mIsInputViewShown && currentHeight != newHeight) {
962             Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: "
963                     + currentHeight + " -> " + newHeight);
964         }
965         mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
966     }
967 
968     /**
969      * Return whether the input method is <em>currently</em> running in
970      * fullscreen mode.  This is the mode that was last determined and
971      * applied by {@link #updateFullscreenMode()}.
972      */
isFullscreenMode()973     public boolean isFullscreenMode() {
974         return mIsFullscreen;
975     }
976 
977     /**
978      * Override this to control when the input method should run in
979      * fullscreen mode.  The default implementation runs in fullsceen only
980      * when the screen is in landscape mode.  If you change what
981      * this returns, you will need to call {@link #updateFullscreenMode()}
982      * yourself whenever the returned value may have changed to have it
983      * re-evaluated and applied.
984      */
onEvaluateFullscreenMode()985     public boolean onEvaluateFullscreenMode() {
986         Configuration config = getResources().getConfiguration();
987         if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
988             return false;
989         }
990         if (mInputEditorInfo != null
991                 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
992             return false;
993         }
994         return true;
995     }
996 
997     /**
998      * Controls the visibility of the extracted text area.  This only applies
999      * when the input method is in fullscreen mode, and thus showing extracted
1000      * text.  When false, the extracted text will not be shown, allowing some
1001      * of the application to be seen behind.  This is normally set for you
1002      * by {@link #onUpdateExtractingVisibility}.  This controls the visibility
1003      * of both the extracted text and candidate view; the latter since it is
1004      * not useful if there is no text to see.
1005      */
setExtractViewShown(boolean shown)1006     public void setExtractViewShown(boolean shown) {
1007         if (mExtractViewHidden == shown) {
1008             mExtractViewHidden = !shown;
1009             updateExtractFrameVisibility();
1010         }
1011     }
1012 
1013     /**
1014      * Return whether the fullscreen extract view is shown.  This will only
1015      * return true if {@link #isFullscreenMode()} returns true, and in that
1016      * case its value depends on the last call to
1017      * {@link #setExtractViewShown(boolean)}.  This effectively lets you
1018      * determine if the application window is entirely covered (when this
1019      * returns true) or if some part of it may be shown (if this returns
1020      * false, though if {@link #isFullscreenMode()} returns true in that case
1021      * then it is probably only a sliver of the application).
1022      */
isExtractViewShown()1023     public boolean isExtractViewShown() {
1024         return mIsFullscreen && !mExtractViewHidden;
1025     }
1026 
updateExtractFrameVisibility()1027     void updateExtractFrameVisibility() {
1028         final int vis;
1029         if (isFullscreenMode()) {
1030             vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
1031             // "vis" should be applied for the extract frame as well in the fullscreen mode.
1032             mExtractFrame.setVisibility(vis);
1033         } else {
1034             vis = View.VISIBLE;
1035             mExtractFrame.setVisibility(View.GONE);
1036         }
1037         updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
1038         if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
1039             int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1040                     ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1041                     : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1042                     0);
1043             if (animRes != 0) {
1044                 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1045                         this, animRes));
1046             }
1047         }
1048         mFullscreenArea.setVisibility(vis);
1049     }
1050 
1051     /**
1052      * Compute the interesting insets into your UI.  The default implementation
1053      * uses the top of the candidates frame for the visible insets, and the
1054      * top of the input frame for the content insets.  The default touchable
1055      * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1056      *
1057      * <p>Note that this method is not called when
1058      * {@link #isExtractViewShown} returns true, since
1059      * in that case the application is left as-is behind the input method and
1060      * not impacted by anything in its UI.
1061      *
1062      * @param outInsets Fill in with the current UI insets.
1063      */
onComputeInsets(Insets outInsets)1064     public void onComputeInsets(Insets outInsets) {
1065         int[] loc = mTmpLocation;
1066         if (mInputFrame.getVisibility() == View.VISIBLE) {
1067             mInputFrame.getLocationInWindow(loc);
1068         } else {
1069             View decor = getWindow().getWindow().getDecorView();
1070             loc[1] = decor.getHeight();
1071         }
1072         if (isFullscreenMode()) {
1073             // In fullscreen mode, we never resize the underlying window.
1074             View decor = getWindow().getWindow().getDecorView();
1075             outInsets.contentTopInsets = decor.getHeight();
1076         } else {
1077             outInsets.contentTopInsets = loc[1];
1078         }
1079         if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1080             mCandidatesFrame.getLocationInWindow(loc);
1081         }
1082         outInsets.visibleTopInsets = loc[1];
1083         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
1084         outInsets.touchableRegion.setEmpty();
1085     }
1086 
1087     /**
1088      * Re-evaluate whether the soft input area should currently be shown, and
1089      * update its UI if this has changed since the last time it
1090      * was evaluated.  This will call {@link #onEvaluateInputViewShown()} to
1091      * determine whether the input view should currently be shown.  You
1092      * can use {@link #isInputViewShown()} to determine if the input view
1093      * is currently shown.
1094      */
updateInputViewShown()1095     public void updateInputViewShown() {
1096         boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
1097         if (mIsInputViewShown != isShown && mWindowVisible) {
1098             mIsInputViewShown = isShown;
1099             mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1100             if (mInputView == null) {
1101                 initialize();
1102                 View v = onCreateInputView();
1103                 if (v != null) {
1104                     setInputView(v);
1105                 }
1106             }
1107         }
1108     }
1109 
1110     /**
1111      * Returns true if we have been asked to show our input view.
1112      */
isShowInputRequested()1113     public boolean isShowInputRequested() {
1114         return mShowInputRequested;
1115     }
1116 
1117     /**
1118      * Return whether the soft input view is <em>currently</em> shown to the
1119      * user.  This is the state that was last determined and
1120      * applied by {@link #updateInputViewShown()}.
1121      */
isInputViewShown()1122     public boolean isInputViewShown() {
1123         return mIsInputViewShown && mWindowVisible;
1124     }
1125 
1126     /**
1127      * Override this to control when the soft input area should be shown to
1128      * the user.  The default implementation only shows the input view when
1129      * there is no hard keyboard or the keyboard is hidden.  If you change what
1130      * this returns, you will need to call {@link #updateInputViewShown()}
1131      * yourself whenever the returned value may have changed to have it
1132      * re-evaluated and applied.
1133      */
onEvaluateInputViewShown()1134     public boolean onEvaluateInputViewShown() {
1135         Configuration config = getResources().getConfiguration();
1136         return config.keyboard == Configuration.KEYBOARD_NOKEYS
1137                 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
1138     }
1139 
1140     /**
1141      * Controls the visibility of the candidates display area.  By default
1142      * it is hidden.
1143      */
setCandidatesViewShown(boolean shown)1144     public void setCandidatesViewShown(boolean shown) {
1145         updateCandidatesVisibility(shown);
1146         if (!mShowInputRequested && mWindowVisible != shown) {
1147             // If we are being asked to show the candidates view while the app
1148             // has not asked for the input view to be shown, then we need
1149             // to update whether the window is shown.
1150             if (shown) {
1151                 showWindow(false);
1152             } else {
1153                 doHideWindow();
1154             }
1155         }
1156     }
1157 
updateCandidatesVisibility(boolean shown)1158     void updateCandidatesVisibility(boolean shown) {
1159         int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1160         if (mCandidatesVisibility != vis) {
1161             mCandidatesFrame.setVisibility(vis);
1162             mCandidatesVisibility = vis;
1163         }
1164     }
1165 
1166     /**
1167      * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1168      * or {@link View#GONE View.GONE}) of the candidates view when it is not
1169      * shown.  The default implementation returns GONE when
1170      * {@link #isExtractViewShown} returns true,
1171      * otherwise VISIBLE.  Be careful if you change this to return GONE in
1172      * other situations -- if showing or hiding the candidates view causes
1173      * your window to resize, this can cause temporary drawing artifacts as
1174      * the resize takes place.
1175      */
getCandidatesHiddenVisibility()1176     public int getCandidatesHiddenVisibility() {
1177         return isExtractViewShown() ? View.GONE : View.INVISIBLE;
1178     }
1179 
showStatusIcon(int iconResId)1180     public void showStatusIcon(int iconResId) {
1181         mStatusIcon = iconResId;
1182         mImm.showStatusIcon(mToken, getPackageName(), iconResId);
1183     }
1184 
hideStatusIcon()1185     public void hideStatusIcon() {
1186         mStatusIcon = 0;
1187         mImm.hideStatusIcon(mToken);
1188     }
1189 
1190     /**
1191      * Force switch to a new input method, as identified by <var>id</var>.  This
1192      * input method will be destroyed, and the requested one started on the
1193      * current input field.
1194      *
1195      * @param id Unique identifier of the new input method ot start.
1196      */
switchInputMethod(String id)1197     public void switchInputMethod(String id) {
1198         mImm.setInputMethod(mToken, id);
1199     }
1200 
setExtractView(View view)1201     public void setExtractView(View view) {
1202         mExtractFrame.removeAllViews();
1203         mExtractFrame.addView(view, new FrameLayout.LayoutParams(
1204                 ViewGroup.LayoutParams.MATCH_PARENT,
1205                 ViewGroup.LayoutParams.MATCH_PARENT));
1206         mExtractView = view;
1207         if (view != null) {
1208             mExtractEditText = (ExtractEditText)view.findViewById(
1209                     com.android.internal.R.id.inputExtractEditText);
1210             mExtractEditText.setIME(this);
1211             mExtractAction = (Button)view.findViewById(
1212                     com.android.internal.R.id.inputExtractAction);
1213             if (mExtractAction != null) {
1214                 mExtractAccessories = (ViewGroup)view.findViewById(
1215                         com.android.internal.R.id.inputExtractAccessories);
1216             }
1217             startExtractingText(false);
1218         } else {
1219             mExtractEditText = null;
1220             mExtractAccessories = null;
1221             mExtractAction = null;
1222         }
1223     }
1224 
1225     /**
1226      * Replaces the current candidates view with a new one.  You only need to
1227      * call this when dynamically changing the view; normally, you should
1228      * implement {@link #onCreateCandidatesView()} and create your view when
1229      * first needed by the input method.
1230      */
setCandidatesView(View view)1231     public void setCandidatesView(View view) {
1232         mCandidatesFrame.removeAllViews();
1233         mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
1234                 ViewGroup.LayoutParams.MATCH_PARENT,
1235                 ViewGroup.LayoutParams.WRAP_CONTENT));
1236     }
1237 
1238     /**
1239      * Replaces the current input view with a new one.  You only need to
1240      * call this when dynamically changing the view; normally, you should
1241      * implement {@link #onCreateInputView()} and create your view when
1242      * first needed by the input method.
1243      */
setInputView(View view)1244     public void setInputView(View view) {
1245         mInputFrame.removeAllViews();
1246         mInputFrame.addView(view, new FrameLayout.LayoutParams(
1247                 ViewGroup.LayoutParams.MATCH_PARENT,
1248                 ViewGroup.LayoutParams.WRAP_CONTENT));
1249         mInputView = view;
1250     }
1251 
1252     /**
1253      * Called by the framework to create the layout for showing extacted text.
1254      * Only called when in fullscreen mode.  The returned view hierarchy must
1255      * have an {@link ExtractEditText} whose ID is
1256      * {@link android.R.id#inputExtractEditText}.
1257      */
onCreateExtractTextView()1258     public View onCreateExtractTextView() {
1259         return mInflater.inflate(
1260                 com.android.internal.R.layout.input_method_extract_view, null);
1261     }
1262 
1263     /**
1264      * Create and return the view hierarchy used to show candidates.  This will
1265      * be called once, when the candidates are first displayed.  You can return
1266      * null to have no candidates view; the default implementation returns null.
1267      *
1268      * <p>To control when the candidates view is displayed, use
1269      * {@link #setCandidatesViewShown(boolean)}.
1270      * To change the candidates view after the first one is created by this
1271      * function, use {@link #setCandidatesView(View)}.
1272      */
onCreateCandidatesView()1273     public View onCreateCandidatesView() {
1274         return null;
1275     }
1276 
1277     /**
1278      * Create and return the view hierarchy used for the input area (such as
1279      * a soft keyboard).  This will be called once, when the input area is
1280      * first displayed.  You can return null to have no input area; the default
1281      * implementation returns null.
1282      *
1283      * <p>To control when the input view is displayed, implement
1284      * {@link #onEvaluateInputViewShown()}.
1285      * To change the input view after the first one is created by this
1286      * function, use {@link #setInputView(View)}.
1287      */
onCreateInputView()1288     public View onCreateInputView() {
1289         return null;
1290     }
1291 
1292     /**
1293      * Called when the input view is being shown and input has started on
1294      * a new editor.  This will always be called after {@link #onStartInput},
1295      * allowing you to do your general setup there and just view-specific
1296      * setup here.  You are guaranteed that {@link #onCreateInputView()} will
1297      * have been called some time before this function is called.
1298      *
1299      * @param info Description of the type of text being edited.
1300      * @param restarting Set to true if we are restarting input on the
1301      * same text field as before.
1302      */
onStartInputView(EditorInfo info, boolean restarting)1303     public void onStartInputView(EditorInfo info, boolean restarting) {
1304         // Intentionally empty
1305     }
1306 
1307     /**
1308      * Called when the input view is being hidden from the user.  This will
1309      * be called either prior to hiding the window, or prior to switching to
1310      * another target for editing.
1311      *
1312      * <p>The default
1313      * implementation uses the InputConnection to clear any active composing
1314      * text; you can override this (not calling the base class implementation)
1315      * to perform whatever behavior you would like.
1316      *
1317      * @param finishingInput If true, {@link #onFinishInput} will be
1318      * called immediately after.
1319      */
onFinishInputView(boolean finishingInput)1320     public void onFinishInputView(boolean finishingInput) {
1321         if (!finishingInput) {
1322             InputConnection ic = getCurrentInputConnection();
1323             if (ic != null) {
1324                 ic.finishComposingText();
1325             }
1326         }
1327     }
1328 
1329     /**
1330      * Called when only the candidates view has been shown for showing
1331      * processing as the user enters text through a hard keyboard.
1332      * This will always be called after {@link #onStartInput},
1333      * allowing you to do your general setup there and just view-specific
1334      * setup here.  You are guaranteed that {@link #onCreateCandidatesView()}
1335      * will have been called some time before this function is called.
1336      *
1337      * <p>Note that this will <em>not</em> be called when the input method
1338      * is running in full editing mode, and thus receiving
1339      * {@link #onStartInputView} to initiate that operation.  This is only
1340      * for the case when candidates are being shown while the input method
1341      * editor is hidden but wants to show its candidates UI as text is
1342      * entered through some other mechanism.
1343      *
1344      * @param info Description of the type of text being edited.
1345      * @param restarting Set to true if we are restarting input on the
1346      * same text field as before.
1347      */
onStartCandidatesView(EditorInfo info, boolean restarting)1348     public void onStartCandidatesView(EditorInfo info, boolean restarting) {
1349         // Intentionally empty
1350     }
1351 
1352     /**
1353      * Called when the candidates view is being hidden from the user.  This will
1354      * be called either prior to hiding the window, or prior to switching to
1355      * another target for editing.
1356      *
1357      * <p>The default
1358      * implementation uses the InputConnection to clear any active composing
1359      * text; you can override this (not calling the base class implementation)
1360      * to perform whatever behavior you would like.
1361      *
1362      * @param finishingInput If true, {@link #onFinishInput} will be
1363      * called immediately after.
1364      */
onFinishCandidatesView(boolean finishingInput)1365     public void onFinishCandidatesView(boolean finishingInput) {
1366         if (!finishingInput) {
1367             InputConnection ic = getCurrentInputConnection();
1368             if (ic != null) {
1369                 ic.finishComposingText();
1370             }
1371         }
1372     }
1373 
1374     /**
1375      * The system has decided that it may be time to show your input method.
1376      * This is called due to a corresponding call to your
1377      * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
1378      * method.  The default implementation uses
1379      * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
1380      * and the current configuration to decide whether the input view should
1381      * be shown at this point.
1382      *
1383      * @param flags Provides additional information about the show request,
1384      * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
1385      * @param configChange This is true if we are re-showing due to a
1386      * configuration change.
1387      * @return Returns true to indicate that the window should be shown.
1388      */
onShowInputRequested(int flags, boolean configChange)1389     public boolean onShowInputRequested(int flags, boolean configChange) {
1390         if (!onEvaluateInputViewShown()) {
1391             return false;
1392         }
1393         if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
1394             if (!configChange && onEvaluateFullscreenMode()) {
1395                 // Don't show if this is not explicitly requested by the user and
1396                 // the input method is fullscreen.  That would be too disruptive.
1397                 // However, we skip this change for a config change, since if
1398                 // the IME is already shown we do want to go into fullscreen
1399                 // mode at this point.
1400                 return false;
1401             }
1402             Configuration config = getResources().getConfiguration();
1403             if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
1404                 // And if the device has a hard keyboard, even if it is
1405                 // currently hidden, don't show the input method implicitly.
1406                 // These kinds of devices don't need it that much.
1407                 return false;
1408             }
1409         }
1410         if ((flags&InputMethod.SHOW_FORCED) != 0) {
1411             mShowInputForced = true;
1412         }
1413         return true;
1414     }
1415 
showWindow(boolean showInput)1416     public void showWindow(boolean showInput) {
1417         if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
1418                 + " mShowInputRequested=" + mShowInputRequested
1419                 + " mWindowAdded=" + mWindowAdded
1420                 + " mWindowCreated=" + mWindowCreated
1421                 + " mWindowVisible=" + mWindowVisible
1422                 + " mInputStarted=" + mInputStarted);
1423 
1424         if (mInShowWindow) {
1425             Log.w(TAG, "Re-entrance in to showWindow");
1426             return;
1427         }
1428 
1429         try {
1430             mWindowWasVisible = mWindowVisible;
1431             mInShowWindow = true;
1432             showWindowInner(showInput);
1433         } finally {
1434             mWindowWasVisible = true;
1435             mInShowWindow = false;
1436         }
1437     }
1438 
showWindowInner(boolean showInput)1439     void showWindowInner(boolean showInput) {
1440         boolean doShowInput = false;
1441         boolean wasVisible = mWindowVisible;
1442         mWindowVisible = true;
1443         if (!mShowInputRequested) {
1444             if (mInputStarted) {
1445                 if (showInput) {
1446                     doShowInput = true;
1447                     mShowInputRequested = true;
1448                 }
1449             }
1450         } else {
1451             showInput = true;
1452         }
1453 
1454         if (DEBUG) Log.v(TAG, "showWindow: updating UI");
1455         initialize();
1456         updateFullscreenMode();
1457         updateInputViewShown();
1458 
1459         if (!mWindowAdded || !mWindowCreated) {
1460             mWindowAdded = true;
1461             mWindowCreated = true;
1462             initialize();
1463             if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
1464             View v = onCreateCandidatesView();
1465             if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
1466             if (v != null) {
1467                 setCandidatesView(v);
1468             }
1469         }
1470         if (mShowInputRequested) {
1471             if (!mInputViewStarted) {
1472                 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1473                 mInputViewStarted = true;
1474                 onStartInputView(mInputEditorInfo, false);
1475             }
1476         } else if (!mCandidatesViewStarted) {
1477             if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1478             mCandidatesViewStarted = true;
1479             onStartCandidatesView(mInputEditorInfo, false);
1480         }
1481 
1482         if (doShowInput) {
1483             startExtractingText(false);
1484         }
1485 
1486         if (!wasVisible) {
1487             if (DEBUG) Log.v(TAG, "showWindow: showing!");
1488             mImm.setImeWindowStatus(mToken, IME_ACTIVE, mBackDisposition);
1489             onWindowShown();
1490             mWindow.show();
1491         }
1492     }
1493 
finishViews()1494     private void finishViews() {
1495         if (mInputViewStarted) {
1496             if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1497             onFinishInputView(false);
1498         } else if (mCandidatesViewStarted) {
1499             if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1500             onFinishCandidatesView(false);
1501         }
1502         mInputViewStarted = false;
1503         mCandidatesViewStarted = false;
1504     }
1505 
doHideWindow()1506     private void doHideWindow() {
1507         mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
1508         hideWindow();
1509     }
1510 
hideWindow()1511     public void hideWindow() {
1512         finishViews();
1513         if (mWindowVisible) {
1514             mWindow.hide();
1515             mWindowVisible = false;
1516             onWindowHidden();
1517             mWindowWasVisible = false;
1518         }
1519     }
1520 
1521     /**
1522      * Called when the input method window has been shown to the user, after
1523      * previously not being visible.  This is done after all of the UI setup
1524      * for the window has occurred (creating its views etc).
1525      */
onWindowShown()1526     public void onWindowShown() {
1527         // Intentionally empty
1528     }
1529 
1530     /**
1531      * Called when the input method window has been hidden from the user,
1532      * after previously being visible.
1533      */
onWindowHidden()1534     public void onWindowHidden() {
1535         // Intentionally empty
1536     }
1537 
1538     /**
1539      * Called when a new client has bound to the input method.  This
1540      * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
1541      * and {@link #onFinishInput()} calls as the user navigates through its
1542      * UI.  Upon this call you know that {@link #getCurrentInputBinding}
1543      * and {@link #getCurrentInputConnection} return valid objects.
1544      */
onBindInput()1545     public void onBindInput() {
1546         // Intentionally empty
1547     }
1548 
1549     /**
1550      * Called when the previous bound client is no longer associated
1551      * with the input method.  After returning {@link #getCurrentInputBinding}
1552      * and {@link #getCurrentInputConnection} will no longer return
1553      * valid objects.
1554      */
onUnbindInput()1555     public void onUnbindInput() {
1556         // Intentionally empty
1557     }
1558 
1559     /**
1560      * Called to inform the input method that text input has started in an
1561      * editor.  You should use this callback to initialize the state of your
1562      * input to match the state of the editor given to it.
1563      *
1564      * @param attribute The attributes of the editor that input is starting
1565      * in.
1566      * @param restarting Set to true if input is restarting in the same
1567      * editor such as because the application has changed the text in
1568      * the editor.  Otherwise will be false, indicating this is a new
1569      * session with the editor.
1570      */
onStartInput(EditorInfo attribute, boolean restarting)1571     public void onStartInput(EditorInfo attribute, boolean restarting) {
1572         // Intentionally empty
1573     }
1574 
doFinishInput()1575     void doFinishInput() {
1576         if (mInputViewStarted) {
1577             if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1578             onFinishInputView(true);
1579         } else if (mCandidatesViewStarted) {
1580             if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1581             onFinishCandidatesView(true);
1582         }
1583         mInputViewStarted = false;
1584         mCandidatesViewStarted = false;
1585         if (mInputStarted) {
1586             if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
1587             onFinishInput();
1588         }
1589         mInputStarted = false;
1590         mStartedInputConnection = null;
1591         mCurCompletions = null;
1592     }
1593 
doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting)1594     void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
1595         if (!restarting) {
1596             doFinishInput();
1597         }
1598         mInputStarted = true;
1599         mStartedInputConnection = ic;
1600         mInputEditorInfo = attribute;
1601         initialize();
1602         if (DEBUG) Log.v(TAG, "CALL: onStartInput");
1603         onStartInput(attribute, restarting);
1604         if (mWindowVisible) {
1605             if (mShowInputRequested) {
1606                 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1607                 mInputViewStarted = true;
1608                 onStartInputView(mInputEditorInfo, restarting);
1609                 startExtractingText(true);
1610             } else if (mCandidatesVisibility == View.VISIBLE) {
1611                 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1612                 mCandidatesViewStarted = true;
1613                 onStartCandidatesView(mInputEditorInfo, restarting);
1614             }
1615         }
1616     }
1617 
1618     /**
1619      * Called to inform the input method that text input has finished in
1620      * the last editor.  At this point there may be a call to
1621      * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
1622      * new editor, or the input method may be left idle.  This method is
1623      * <em>not</em> called when input restarts in the same editor.
1624      *
1625      * <p>The default
1626      * implementation uses the InputConnection to clear any active composing
1627      * text; you can override this (not calling the base class implementation)
1628      * to perform whatever behavior you would like.
1629      */
onFinishInput()1630     public void onFinishInput() {
1631         InputConnection ic = getCurrentInputConnection();
1632         if (ic != null) {
1633             ic.finishComposingText();
1634         }
1635     }
1636 
1637     /**
1638      * Called when the application has reported auto-completion candidates that
1639      * it would like to have the input method displayed.  Typically these are
1640      * only used when an input method is running in full-screen mode, since
1641      * otherwise the user can see and interact with the pop-up window of
1642      * completions shown by the application.
1643      *
1644      * <p>The default implementation here does nothing.
1645      */
onDisplayCompletions(CompletionInfo[] completions)1646     public void onDisplayCompletions(CompletionInfo[] completions) {
1647         // Intentionally empty
1648     }
1649 
1650     /**
1651      * Called when the application has reported new extracted text to be shown
1652      * due to changes in its current text state.  The default implementation
1653      * here places the new text in the extract edit text, when the input
1654      * method is running in fullscreen mode.
1655      */
onUpdateExtractedText(int token, ExtractedText text)1656     public void onUpdateExtractedText(int token, ExtractedText text) {
1657         if (mExtractedToken != token) {
1658             return;
1659         }
1660         if (text != null) {
1661             if (mExtractEditText != null) {
1662                 mExtractedText = text;
1663                 mExtractEditText.setExtractedText(text);
1664             }
1665         }
1666     }
1667 
1668     /**
1669      * Called when the application has reported a new selection region of
1670      * the text.  This is called whether or not the input method has requested
1671      * extracted text updates, although if so it will not receive this call
1672      * if the extracted text has changed as well.
1673      *
1674      * <p>Be careful about changing the text in reaction to this call with
1675      * methods such as setComposingText, commitText or
1676      * deleteSurroundingText. If the cursor moves as a result, this method
1677      * will be called again, which may result in an infinite loop.
1678      *
1679      * <p>The default implementation takes care of updating the cursor in
1680      * the extract text, if it is being shown.
1681      */
onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)1682     public void onUpdateSelection(int oldSelStart, int oldSelEnd,
1683             int newSelStart, int newSelEnd,
1684             int candidatesStart, int candidatesEnd) {
1685         final ExtractEditText eet = mExtractEditText;
1686         if (eet != null && isFullscreenMode() && mExtractedText != null) {
1687             final int off = mExtractedText.startOffset;
1688             eet.startInternalChanges();
1689             newSelStart -= off;
1690             newSelEnd -= off;
1691             final int len = eet.getText().length();
1692             if (newSelStart < 0) newSelStart = 0;
1693             else if (newSelStart > len) newSelStart = len;
1694             if (newSelEnd < 0) newSelEnd = 0;
1695             else if (newSelEnd > len) newSelEnd = len;
1696             eet.setSelection(newSelStart, newSelEnd);
1697             eet.finishInternalChanges();
1698         }
1699     }
1700 
1701     /**
1702      * Called when the user tapped or clicked a text view.
1703      * IMEs can't rely on this method being called because this was not part of the original IME
1704      * protocol, so applications with custom text editing written before this method appeared will
1705      * not call to inform the IME of this interaction.
1706      * @param focusChanged true if the user changed the focused view by this click.
1707      */
onViewClicked(boolean focusChanged)1708     public void onViewClicked(boolean focusChanged) {
1709         // Intentionally empty
1710     }
1711 
1712     /**
1713      * Called when the application has reported a new location of its text
1714      * cursor.  This is only called if explicitly requested by the input method.
1715      * The default implementation does nothing.
1716      * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
1717      */
1718     @Deprecated
onUpdateCursor(Rect newCursor)1719     public void onUpdateCursor(Rect newCursor) {
1720         // Intentionally empty
1721     }
1722 
1723     /**
1724      * Called when the application has reported a new location of its text insertion point and
1725      * characters in the composition string.  This is only called if explicitly requested by the
1726      * input method. The default implementation does nothing.
1727      * @param cursorAnchorInfo The positional information of the text insertion point and the
1728      * composition string.
1729      */
onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo)1730     public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
1731         // Intentionally empty
1732     }
1733 
1734     /**
1735      * Close this input method's soft input area, removing it from the display.
1736      * The input method will continue running, but the user can no longer use
1737      * it to generate input by touching the screen.
1738      * @param flags Provides additional operating flags.  Currently may be
1739      * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
1740      * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
1741      */
requestHideSelf(int flags)1742     public void requestHideSelf(int flags) {
1743         mImm.hideSoftInputFromInputMethod(mToken, flags);
1744     }
1745 
1746     /**
1747      * Show the input method. This is a call back to the
1748      * IMF to handle showing the input method.
1749      * @param flags Provides additional operating flags.  Currently may be
1750      * 0 or have the {@link InputMethodManager#SHOW_FORCED
1751      * InputMethodManager.} bit set.
1752      */
requestShowSelf(int flags)1753     private void requestShowSelf(int flags) {
1754         mImm.showSoftInputFromInputMethod(mToken, flags);
1755     }
1756 
handleBack(boolean doIt)1757     private boolean handleBack(boolean doIt) {
1758         if (mShowInputRequested) {
1759             if (isExtractViewShown() && mExtractView instanceof ExtractEditLayout) {
1760                 ExtractEditLayout extractEditLayout = (ExtractEditLayout) mExtractView;
1761                 if (extractEditLayout.isActionModeStarted()) {
1762                     if (doIt) extractEditLayout.finishActionMode();
1763                     return true;
1764                 }
1765             }
1766             // If the soft input area is shown, back closes it and we
1767             // consume the back key.
1768             if (doIt) requestHideSelf(0);
1769             return true;
1770         } else if (mWindowVisible) {
1771             if (mCandidatesVisibility == View.VISIBLE) {
1772                 // If we are showing candidates even if no input area, then
1773                 // hide them.
1774                 if (doIt) setCandidatesViewShown(false);
1775             } else {
1776                 // If we have the window visible for some other reason --
1777                 // most likely to show candidates -- then just get rid
1778                 // of it.  This really shouldn't happen, but just in case...
1779                 if (doIt) doHideWindow();
1780             }
1781             return true;
1782         }
1783         return false;
1784     }
1785 
1786     /**
1787      * Override this to intercept key down events before they are processed by the
1788      * application.  If you return true, the application will not
1789      * process the event itself.  If you return false, the normal application processing
1790      * will occur as if the IME had not seen the event at all.
1791      *
1792      * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
1793      * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
1794      * possibly hide it when the key goes up (if not canceled or long pressed).  In
1795      * addition, in fullscreen mode only, it will consume DPAD movement
1796      * events to move the cursor in the extracted text view, not allowing
1797      * them to perform navigation in the underlying application.
1798      */
onKeyDown(int keyCode, KeyEvent event)1799     public boolean onKeyDown(int keyCode, KeyEvent event) {
1800         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
1801             if (handleBack(false)) {
1802                 event.startTracking();
1803                 return true;
1804             }
1805             return false;
1806         }
1807         return doMovementKey(keyCode, event, MOVEMENT_DOWN);
1808     }
1809 
1810     /**
1811      * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
1812      * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
1813      * the event).
1814      */
onKeyLongPress(int keyCode, KeyEvent event)1815     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1816         return false;
1817     }
1818 
1819     /**
1820      * Override this to intercept special key multiple events before they are
1821      * processed by the
1822      * application.  If you return true, the application will not itself
1823      * process the event.  If you return false, the normal application processing
1824      * will occur as if the IME had not seen the event at all.
1825      *
1826      * <p>The default implementation always returns false, except when
1827      * in fullscreen mode, where it will consume DPAD movement
1828      * events to move the cursor in the extracted text view, not allowing
1829      * them to perform navigation in the underlying application.
1830      */
onKeyMultiple(int keyCode, int count, KeyEvent event)1831     public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1832         return doMovementKey(keyCode, event, count);
1833     }
1834 
1835     /**
1836      * Override this to intercept key up events before they are processed by the
1837      * application.  If you return true, the application will not itself
1838      * process the event.  If you return false, the normal application processing
1839      * will occur as if the IME had not seen the event at all.
1840      *
1841      * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
1842      * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown.  In
1843      * addition, in fullscreen mode only, it will consume DPAD movement
1844      * events to move the cursor in the extracted text view, not allowing
1845      * them to perform navigation in the underlying application.
1846      */
onKeyUp(int keyCode, KeyEvent event)1847     public boolean onKeyUp(int keyCode, KeyEvent event) {
1848         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isTracking()
1849                 && !event.isCanceled()) {
1850             return handleBack(true);
1851         }
1852 
1853         return doMovementKey(keyCode, event, MOVEMENT_UP);
1854     }
1855 
1856     /**
1857      * Override this to intercept trackball motion events before they are
1858      * processed by the application.
1859      * If you return true, the application will not itself process the event.
1860      * If you return false, the normal application processing will occur as if
1861      * the IME had not seen the event at all.
1862      */
1863     @Override
onTrackballEvent(MotionEvent event)1864     public boolean onTrackballEvent(MotionEvent event) {
1865         if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
1866         return false;
1867     }
1868 
1869     /**
1870      * Override this to intercept generic motion events before they are
1871      * processed by the application.
1872      * If you return true, the application will not itself process the event.
1873      * If you return false, the normal application processing will occur as if
1874      * the IME had not seen the event at all.
1875      */
1876     @Override
onGenericMotionEvent(MotionEvent event)1877     public boolean onGenericMotionEvent(MotionEvent event) {
1878         if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
1879         return false;
1880     }
1881 
onAppPrivateCommand(String action, Bundle data)1882     public void onAppPrivateCommand(String action, Bundle data) {
1883     }
1884 
1885     /**
1886      * Handle a request by the system to toggle the soft input area.
1887      */
onToggleSoftInput(int showFlags, int hideFlags)1888     private void onToggleSoftInput(int showFlags, int hideFlags) {
1889         if (DEBUG) Log.v(TAG, "toggleSoftInput()");
1890         if (isInputViewShown()) {
1891             requestHideSelf(hideFlags);
1892         } else {
1893             requestShowSelf(showFlags);
1894         }
1895     }
1896 
1897     static final int MOVEMENT_DOWN = -1;
1898     static final int MOVEMENT_UP = -2;
1899 
reportExtractedMovement(int keyCode, int count)1900     void reportExtractedMovement(int keyCode, int count) {
1901         int dx = 0, dy = 0;
1902         switch (keyCode) {
1903             case KeyEvent.KEYCODE_DPAD_LEFT:
1904                 dx = -count;
1905                 break;
1906             case KeyEvent.KEYCODE_DPAD_RIGHT:
1907                 dx = count;
1908                 break;
1909             case KeyEvent.KEYCODE_DPAD_UP:
1910                 dy = -count;
1911                 break;
1912             case KeyEvent.KEYCODE_DPAD_DOWN:
1913                 dy = count;
1914                 break;
1915         }
1916         onExtractedCursorMovement(dx, dy);
1917     }
1918 
doMovementKey(int keyCode, KeyEvent event, int count)1919     boolean doMovementKey(int keyCode, KeyEvent event, int count) {
1920         final ExtractEditText eet = mExtractEditText;
1921         if (isExtractViewShown() && isInputViewShown() && eet != null) {
1922             // If we are in fullscreen mode, the cursor will move around
1923             // the extract edit text, but should NOT cause focus to move
1924             // to other fields.
1925             MovementMethod movement = eet.getMovementMethod();
1926             Layout layout = eet.getLayout();
1927             if (movement != null && layout != null) {
1928                 // We want our own movement method to handle the key, so the
1929                 // cursor will properly move in our own word wrapping.
1930                 if (count == MOVEMENT_DOWN) {
1931                     if (movement.onKeyDown(eet,
1932                             (Spannable)eet.getText(), keyCode, event)) {
1933                         reportExtractedMovement(keyCode, 1);
1934                         return true;
1935                     }
1936                 } else if (count == MOVEMENT_UP) {
1937                     if (movement.onKeyUp(eet,
1938                             (Spannable)eet.getText(), keyCode, event)) {
1939                         return true;
1940                     }
1941                 } else {
1942                     if (movement.onKeyOther(eet, (Spannable)eet.getText(), event)) {
1943                         reportExtractedMovement(keyCode, count);
1944                     } else {
1945                         KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
1946                         if (movement.onKeyDown(eet,
1947                                 (Spannable)eet.getText(), keyCode, down)) {
1948                             KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
1949                             movement.onKeyUp(eet,
1950                                     (Spannable)eet.getText(), keyCode, up);
1951                             while (--count > 0) {
1952                                 movement.onKeyDown(eet,
1953                                         (Spannable)eet.getText(), keyCode, down);
1954                                 movement.onKeyUp(eet,
1955                                         (Spannable)eet.getText(), keyCode, up);
1956                             }
1957                             reportExtractedMovement(keyCode, count);
1958                         }
1959                     }
1960                 }
1961             }
1962             // Regardless of whether the movement method handled the key,
1963             // we never allow DPAD navigation to the application.
1964             switch (keyCode) {
1965                 case KeyEvent.KEYCODE_DPAD_LEFT:
1966                 case KeyEvent.KEYCODE_DPAD_RIGHT:
1967                 case KeyEvent.KEYCODE_DPAD_UP:
1968                 case KeyEvent.KEYCODE_DPAD_DOWN:
1969                     return true;
1970             }
1971         }
1972 
1973         return false;
1974     }
1975 
1976     /**
1977      * Send the given key event code (as defined by {@link KeyEvent}) to the
1978      * current input connection is a key down + key up event pair.  The sent
1979      * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
1980      * set, so that the recipient can identify them as coming from a software
1981      * input method, and
1982      * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
1983      * that they don't impact the current touch mode of the UI.
1984      *
1985      * <p>Note that it's discouraged to send such key events in normal operation;
1986      * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
1987      * text fields, or for non-rich input methods. A reasonably capable software
1988      * input method should use the
1989      * {@link android.view.inputmethod.InputConnection#commitText} family of methods
1990      * to send text to an application, rather than sending key events.</p>
1991      *
1992      * @param keyEventCode The raw key code to send, as defined by
1993      * {@link KeyEvent}.
1994      */
sendDownUpKeyEvents(int keyEventCode)1995     public void sendDownUpKeyEvents(int keyEventCode) {
1996         InputConnection ic = getCurrentInputConnection();
1997         if (ic == null) return;
1998         long eventTime = SystemClock.uptimeMillis();
1999         ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
2000                 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
2001                 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2002         ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
2003                 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
2004                 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2005     }
2006 
2007     /**
2008      * Ask the input target to execute its default action via
2009      * {@link InputConnection#performEditorAction
2010      * InputConnection.performEditorAction()}.
2011      *
2012      * @param fromEnterKey If true, this will be executed as if the user had
2013      * pressed an enter key on the keyboard, that is it will <em>not</em>
2014      * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
2015      * EditorInfo.IME_FLAG_NO_ENTER_ACTION}.  If false, the action will be
2016      * sent regardless of how the editor has set that flag.
2017      *
2018      * @return Returns a boolean indicating whether an action has been sent.
2019      * If false, either the editor did not specify a default action or it
2020      * does not want an action from the enter key.  If true, the action was
2021      * sent (or there was no input connection at all).
2022      */
sendDefaultEditorAction(boolean fromEnterKey)2023     public boolean sendDefaultEditorAction(boolean fromEnterKey) {
2024         EditorInfo ei = getCurrentInputEditorInfo();
2025         if (ei != null &&
2026                 (!fromEnterKey || (ei.imeOptions &
2027                         EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
2028                 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
2029                     EditorInfo.IME_ACTION_NONE) {
2030             // If the enter key was pressed, and the editor has a default
2031             // action associated with pressing enter, then send it that
2032             // explicit action instead of the key event.
2033             InputConnection ic = getCurrentInputConnection();
2034             if (ic != null) {
2035                 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
2036             }
2037             return true;
2038         }
2039 
2040         return false;
2041     }
2042 
2043     /**
2044      * Send the given UTF-16 character to the current input connection.  Most
2045      * characters will be delivered simply by calling
2046      * {@link InputConnection#commitText InputConnection.commitText()} with
2047      * the character; some, however, may be handled different.  In particular,
2048      * the enter character ('\n') will either be delivered as an action code
2049      * or a raw key event, as appropriate.  Consider this as a convenience
2050      * method for IMEs that do not have a full implementation of actions; a
2051      * fully complying IME will decide of the right action for each event and
2052      * will likely never call this method except maybe to handle events coming
2053      * from an actual hardware keyboard.
2054      *
2055      * @param charCode The UTF-16 character code to send.
2056      */
sendKeyChar(char charCode)2057     public void sendKeyChar(char charCode) {
2058         switch (charCode) {
2059             case '\n': // Apps may be listening to an enter key to perform an action
2060                 if (!sendDefaultEditorAction(true)) {
2061                     sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2062                 }
2063                 break;
2064             default:
2065                 // Make sure that digits go through any text watcher on the client side.
2066                 if (charCode >= '0' && charCode <= '9') {
2067                     sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2068                 } else {
2069                     InputConnection ic = getCurrentInputConnection();
2070                     if (ic != null) {
2071                         ic.commitText(String.valueOf((char) charCode), 1);
2072                     }
2073                 }
2074                 break;
2075         }
2076     }
2077 
2078     /**
2079      * This is called when the user has moved the cursor in the extracted
2080      * text view, when running in fullsreen mode.  The default implementation
2081      * performs the corresponding selection change on the underlying text
2082      * editor.
2083      */
onExtractedSelectionChanged(int start, int end)2084     public void onExtractedSelectionChanged(int start, int end) {
2085         InputConnection conn = getCurrentInputConnection();
2086         if (conn != null) {
2087             conn.setSelection(start, end);
2088         }
2089     }
2090 
2091     /**
2092      * @hide
2093      */
onExtractedDeleteText(int start, int end)2094     public void onExtractedDeleteText(int start, int end) {
2095         InputConnection conn = getCurrentInputConnection();
2096         if (conn != null) {
2097             conn.setSelection(start, start);
2098             conn.deleteSurroundingText(0, end-start);
2099         }
2100     }
2101 
2102     /**
2103      * @hide
2104      */
onExtractedReplaceText(int start, int end, CharSequence text)2105     public void onExtractedReplaceText(int start, int end, CharSequence text) {
2106         InputConnection conn = getCurrentInputConnection();
2107         if (conn != null) {
2108             conn.setComposingRegion(start, end);
2109             conn.commitText(text, 1);
2110         }
2111     }
2112 
2113     /**
2114      * @hide
2115      */
onExtractedSetSpan(Object span, int start, int end, int flags)2116     public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2117         InputConnection conn = getCurrentInputConnection();
2118         if (conn != null) {
2119             if (!conn.setSelection(start, end)) return;
2120             CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2121             if (text instanceof Spannable) {
2122                 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2123                 conn.setComposingRegion(start, end);
2124                 conn.commitText(text, 1);
2125             }
2126         }
2127     }
2128 
2129     /**
2130      * This is called when the user has clicked on the extracted text view,
2131      * when running in fullscreen mode.  The default implementation hides
2132      * the candidates view when this happens, but only if the extracted text
2133      * editor has a vertical scroll bar because its text doesn't fit.
2134      * Re-implement this to provide whatever behavior you want.
2135      */
onExtractedTextClicked()2136     public void onExtractedTextClicked() {
2137         if (mExtractEditText == null) {
2138             return;
2139         }
2140         if (mExtractEditText.hasVerticalScrollBar()) {
2141             setCandidatesViewShown(false);
2142         }
2143     }
2144 
2145     /**
2146      * This is called when the user has performed a cursor movement in the
2147      * extracted text view, when it is running in fullscreen mode.  The default
2148      * implementation hides the candidates view when a vertical movement
2149      * happens, but only if the extracted text editor has a vertical scroll bar
2150      * because its text doesn't fit.
2151      * Re-implement this to provide whatever behavior you want.
2152      * @param dx The amount of cursor movement in the x dimension.
2153      * @param dy The amount of cursor movement in the y dimension.
2154      */
onExtractedCursorMovement(int dx, int dy)2155     public void onExtractedCursorMovement(int dx, int dy) {
2156         if (mExtractEditText == null || dy == 0) {
2157             return;
2158         }
2159         if (mExtractEditText.hasVerticalScrollBar()) {
2160             setCandidatesViewShown(false);
2161         }
2162     }
2163 
2164     /**
2165      * This is called when the user has selected a context menu item from the
2166      * extracted text view, when running in fullscreen mode.  The default
2167      * implementation sends this action to the current InputConnection's
2168      * {@link InputConnection#performContextMenuAction(int)}, for it
2169      * to be processed in underlying "real" editor.  Re-implement this to
2170      * provide whatever behavior you want.
2171      */
onExtractTextContextMenuItem(int id)2172     public boolean onExtractTextContextMenuItem(int id) {
2173         InputConnection ic = getCurrentInputConnection();
2174         if (ic != null) {
2175             ic.performContextMenuAction(id);
2176         }
2177         return true;
2178     }
2179 
2180     /**
2181      * Return text that can be used as a button label for the given
2182      * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.  Returns null
2183      * if there is no action requested.  Note that there is no guarantee that
2184      * the returned text will be relatively short, so you probably do not
2185      * want to use it as text on a soft keyboard key label.
2186      *
2187      * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
2188      *
2189      * @return Returns a label to use, or null if there is no action.
2190      */
getTextForImeAction(int imeOptions)2191     public CharSequence getTextForImeAction(int imeOptions) {
2192         switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2193             case EditorInfo.IME_ACTION_NONE:
2194                 return null;
2195             case EditorInfo.IME_ACTION_GO:
2196                 return getText(com.android.internal.R.string.ime_action_go);
2197             case EditorInfo.IME_ACTION_SEARCH:
2198                 return getText(com.android.internal.R.string.ime_action_search);
2199             case EditorInfo.IME_ACTION_SEND:
2200                 return getText(com.android.internal.R.string.ime_action_send);
2201             case EditorInfo.IME_ACTION_NEXT:
2202                 return getText(com.android.internal.R.string.ime_action_next);
2203             case EditorInfo.IME_ACTION_DONE:
2204                 return getText(com.android.internal.R.string.ime_action_done);
2205             case EditorInfo.IME_ACTION_PREVIOUS:
2206                 return getText(com.android.internal.R.string.ime_action_previous);
2207             default:
2208                 return getText(com.android.internal.R.string.ime_action_default);
2209         }
2210     }
2211 
2212     /**
2213      * Called when the fullscreen-mode extracting editor info has changed,
2214      * to determine whether the extracting (extract text and candidates) portion
2215      * of the UI should be shown.  The standard implementation hides or shows
2216      * the extract area depending on whether it makes sense for the
2217      * current editor.  In particular, a {@link InputType#TYPE_NULL}
2218      * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
2219      * turn off the extract area since there is no text to be shown.
2220      */
onUpdateExtractingVisibility(EditorInfo ei)2221     public void onUpdateExtractingVisibility(EditorInfo ei) {
2222         if (ei.inputType == InputType.TYPE_NULL ||
2223                 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
2224             // No reason to show extract UI!
2225             setExtractViewShown(false);
2226             return;
2227         }
2228 
2229         setExtractViewShown(true);
2230     }
2231 
2232     /**
2233      * Called when the fullscreen-mode extracting editor info has changed,
2234      * to update the state of its UI such as the action buttons shown.
2235      * You do not need to deal with this if you are using the standard
2236      * full screen extract UI.  If replacing it, you will need to re-implement
2237      * this to put the appropriate action button in your own UI and handle it,
2238      * and perform any other changes.
2239      *
2240      * <p>The standard implementation turns on or off its accessory area
2241      * depending on whether there is an action button, and hides or shows
2242      * the entire extract area depending on whether it makes sense for the
2243      * current editor.  In particular, a {@link InputType#TYPE_NULL} or
2244      * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
2245      * extract area since there is no text to be shown.
2246      */
onUpdateExtractingViews(EditorInfo ei)2247     public void onUpdateExtractingViews(EditorInfo ei) {
2248         if (!isExtractViewShown()) {
2249             return;
2250         }
2251 
2252         if (mExtractAccessories == null) {
2253             return;
2254         }
2255         final boolean hasAction = ei.actionLabel != null || (
2256                 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
2257                 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
2258                 ei.inputType != InputType.TYPE_NULL);
2259         if (hasAction) {
2260             mExtractAccessories.setVisibility(View.VISIBLE);
2261             if (mExtractAction != null) {
2262                 if (ei.actionLabel != null) {
2263                     mExtractAction.setText(ei.actionLabel);
2264                 } else {
2265                     mExtractAction.setText(getTextForImeAction(ei.imeOptions));
2266                 }
2267                 mExtractAction.setOnClickListener(mActionClickListener);
2268             }
2269         } else {
2270             mExtractAccessories.setVisibility(View.GONE);
2271             if (mExtractAction != null) {
2272                 mExtractAction.setOnClickListener(null);
2273             }
2274         }
2275     }
2276 
2277     /**
2278      * This is called when, while currently displayed in extract mode, the
2279      * current input target changes.  The default implementation will
2280      * auto-hide the IME if the new target is not a full editor, since this
2281      * can be a confusing experience for the user.
2282      */
onExtractingInputChanged(EditorInfo ei)2283     public void onExtractingInputChanged(EditorInfo ei) {
2284         if (ei.inputType == InputType.TYPE_NULL) {
2285             requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
2286         }
2287     }
2288 
startExtractingText(boolean inputChanged)2289     void startExtractingText(boolean inputChanged) {
2290         final ExtractEditText eet = mExtractEditText;
2291         if (eet != null && getCurrentInputStarted()
2292                 && isFullscreenMode()) {
2293             mExtractedToken++;
2294             ExtractedTextRequest req = new ExtractedTextRequest();
2295             req.token = mExtractedToken;
2296             req.flags = InputConnection.GET_TEXT_WITH_STYLES;
2297             req.hintMaxLines = 10;
2298             req.hintMaxChars = 10000;
2299             InputConnection ic = getCurrentInputConnection();
2300             mExtractedText = ic == null? null
2301                     : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
2302             if (mExtractedText == null || ic == null) {
2303                 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
2304                         + mExtractedText + ", input connection = " + ic);
2305             }
2306             final EditorInfo ei = getCurrentInputEditorInfo();
2307 
2308             try {
2309                 eet.startInternalChanges();
2310                 onUpdateExtractingVisibility(ei);
2311                 onUpdateExtractingViews(ei);
2312                 int inputType = ei.inputType;
2313                 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
2314                         == EditorInfo.TYPE_CLASS_TEXT) {
2315                     if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
2316                         inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2317                     }
2318                 }
2319                 eet.setInputType(inputType);
2320                 eet.setHint(ei.hintText);
2321                 if (mExtractedText != null) {
2322                     eet.setEnabled(true);
2323                     eet.setExtractedText(mExtractedText);
2324                 } else {
2325                     eet.setEnabled(false);
2326                     eet.setText("");
2327                 }
2328             } finally {
2329                 eet.finishInternalChanges();
2330             }
2331 
2332             if (inputChanged) {
2333                 onExtractingInputChanged(ei);
2334             }
2335         }
2336     }
2337 
2338     // TODO: Handle the subtype change event
2339     /**
2340      * Called when the subtype was changed.
2341      * @param newSubtype the subtype which is being changed to.
2342      */
onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype)2343     protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
2344         if (DEBUG) {
2345             int nameResId = newSubtype.getNameResId();
2346             String mode = newSubtype.getMode();
2347             String output = "changeInputMethodSubtype:"
2348                 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
2349                 + mode + ","
2350                 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
2351             Log.v(TAG, "--- " + output);
2352         }
2353     }
2354 
2355     /**
2356      * @return The recommended height of the input method window.
2357      * An IME author can get the last input method's height as the recommended height
2358      * by calling this in
2359      * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}.
2360      * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME
2361      * switching by using this value as a visible inset height. It's efficient for the smooth
2362      * transition between different IMEs. However, note that this may return 0 (or possibly
2363      * unexpectedly low height). You should thus avoid relying on the return value of this method
2364      * all the time. Please make sure to use a reasonable height for the IME.
2365      */
getInputMethodWindowRecommendedHeight()2366     public int getInputMethodWindowRecommendedHeight() {
2367         return mImm.getInputMethodWindowVisibleHeight();
2368     }
2369 
2370     /**
2371      * Performs a dump of the InputMethodService's internal state.  Override
2372      * to add your own information to the dump.
2373      */
dump(FileDescriptor fd, PrintWriter fout, String[] args)2374     @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2375         final Printer p = new PrintWriterPrinter(fout);
2376         p.println("Input method service state for " + this + ":");
2377         p.println("  mWindowCreated=" + mWindowCreated
2378                 + " mWindowAdded=" + mWindowAdded);
2379         p.println("  mWindowVisible=" + mWindowVisible
2380                 + " mWindowWasVisible=" + mWindowWasVisible
2381                 + " mInShowWindow=" + mInShowWindow);
2382         p.println("  Configuration=" + getResources().getConfiguration());
2383         p.println("  mToken=" + mToken);
2384         p.println("  mInputBinding=" + mInputBinding);
2385         p.println("  mInputConnection=" + mInputConnection);
2386         p.println("  mStartedInputConnection=" + mStartedInputConnection);
2387         p.println("  mInputStarted=" + mInputStarted
2388                 + " mInputViewStarted=" + mInputViewStarted
2389                 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
2390 
2391         if (mInputEditorInfo != null) {
2392             p.println("  mInputEditorInfo:");
2393             mInputEditorInfo.dump(p, "    ");
2394         } else {
2395             p.println("  mInputEditorInfo: null");
2396         }
2397 
2398         p.println("  mShowInputRequested=" + mShowInputRequested
2399                 + " mLastShowInputRequested=" + mLastShowInputRequested
2400                 + " mShowInputForced=" + mShowInputForced
2401                 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
2402         p.println("  mCandidatesVisibility=" + mCandidatesVisibility
2403                 + " mFullscreenApplied=" + mFullscreenApplied
2404                 + " mIsFullscreen=" + mIsFullscreen
2405                 + " mExtractViewHidden=" + mExtractViewHidden);
2406 
2407         if (mExtractedText != null) {
2408             p.println("  mExtractedText:");
2409             p.println("    text=" + mExtractedText.text.length() + " chars"
2410                     + " startOffset=" + mExtractedText.startOffset);
2411             p.println("    selectionStart=" + mExtractedText.selectionStart
2412                     + " selectionEnd=" + mExtractedText.selectionEnd
2413                     + " flags=0x" + Integer.toHexString(mExtractedText.flags));
2414         } else {
2415             p.println("  mExtractedText: null");
2416         }
2417         p.println("  mExtractedToken=" + mExtractedToken);
2418         p.println("  mIsInputViewShown=" + mIsInputViewShown
2419                 + " mStatusIcon=" + mStatusIcon);
2420         p.println("Last computed insets:");
2421         p.println("  contentTopInsets=" + mTmpInsets.contentTopInsets
2422                 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
2423                 + " touchableInsets=" + mTmpInsets.touchableInsets
2424                 + " touchableRegion=" + mTmpInsets.touchableRegion);
2425     }
2426 }
2427