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