[go: nahoru, domu]

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