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