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