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