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