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