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