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