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