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