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