InputMethodService.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
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.util.Log;
30import android.view.KeyEvent;
31import android.view.LayoutInflater;
32import android.view.MotionEvent;
33import android.view.View;
34import android.view.ViewGroup;
35import android.view.ViewTreeObserver;
36import android.view.inputmethod.CompletionInfo;
37import android.view.inputmethod.ExtractedText;
38import android.view.inputmethod.ExtractedTextRequest;
39import android.view.inputmethod.InputBinding;
40import android.view.inputmethod.InputConnection;
41import android.view.inputmethod.InputMethod;
42import android.view.inputmethod.InputMethodManager;
43import android.view.inputmethod.EditorInfo;
44import android.widget.FrameLayout;
45
46/**
47 * InputMethodService provides a standard implementation of an InputMethod,
48 * which final implementations can derive from and customize.  See the
49 * base class {@link AbstractInputMethodService} and the {@link InputMethod}
50 * interface for more information on the basics of writing input methods.
51 */
52public class InputMethodService extends AbstractInputMethodService {
53    static final String TAG = "InputMethodService";
54    static final boolean DEBUG = false;
55
56    LayoutInflater mInflater;
57    View mRootView;
58    SoftInputWindow mWindow;
59    boolean mWindowCreated;
60    boolean mWindowAdded;
61    boolean mWindowVisible;
62    FrameLayout mExtractFrame;
63    FrameLayout mCandidatesFrame;
64    FrameLayout mInputFrame;
65
66    IBinder mToken;
67
68    InputBinding mInputBinding;
69    InputConnection mInputConnection;
70    boolean mInputStarted;
71    EditorInfo mInputInfo;
72
73    boolean mShowInputRequested;
74    boolean mShowCandidatesRequested;
75
76    boolean mFullscreenApplied;
77    boolean mIsFullscreen;
78    View mExtractView;
79    ExtractEditText mExtractEditText;
80    ExtractedText mExtractedText;
81    int mExtractedToken;
82
83    View mInputView;
84    boolean mIsInputViewShown;
85
86    int mStatusIcon;
87
88    final Insets mTmpInsets = new Insets();
89    final int[] mTmpLocation = new int[2];
90
91    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
92            new ViewTreeObserver.OnComputeInternalInsetsListener() {
93        public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
94            if (isFullscreenMode()) {
95                // In fullscreen mode, we just say the window isn't covering
96                // any content so we don't impact whatever is behind.
97                View decor = getWindow().getWindow().getDecorView();
98                info.contentInsets.top = info.visibleInsets.top
99                        = decor.getHeight();
100                info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
101            } else {
102                onComputeInsets(mTmpInsets);
103                info.contentInsets.top = mTmpInsets.contentTopInsets;
104                info.visibleInsets.top = mTmpInsets.visibleTopInsets;
105                info.setTouchableInsets(mTmpInsets.touchableInsets);
106            }
107        }
108    };
109
110    /**
111     * Concrete implementation of
112     * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
113     * all of the standard behavior for an input method.
114     */
115    public class InputMethodImpl extends AbstractInputMethodImpl {
116        /**
117         * Take care of attaching the given window token provided by the system.
118         */
119        public void attachToken(IBinder token) {
120            if (mToken == null) {
121                mToken = token;
122                mWindow.setToken(token);
123            }
124        }
125
126        /**
127         * Handle a new input binding, calling
128         * {@link InputMethodService#onBindInput InputMethodService.onBindInput()}
129         * when done.
130         */
131        public void bindInput(InputBinding binding) {
132            mInputBinding = binding;
133            mInputConnection = binding.getConnection();
134            onBindInput();
135        }
136
137        /**
138         * Clear the current input binding.
139         */
140        public void unbindInput() {
141            mInputStarted = false;
142            mInputBinding = null;
143            mInputConnection = null;
144        }
145
146        public void startInput(EditorInfo attribute) {
147            doStartInput(attribute, false);
148        }
149
150        public void restartInput(EditorInfo attribute) {
151            doStartInput(attribute, false);
152        }
153
154        /**
155         * Handle a request by the system to hide the soft input area.
156         */
157        public void hideSoftInput() {
158            if (DEBUG) Log.v(TAG, "hideSoftInput()");
159            mShowInputRequested = false;
160            hideWindow();
161        }
162
163        /**
164         * Handle a request by the system to show the soft input area.
165         */
166        public void showSoftInput() {
167            if (DEBUG) Log.v(TAG, "showSoftInput()");
168            showWindow(true);
169        }
170    }
171
172    /**
173     * Concrete implementation of
174     * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
175     * all of the standard behavior for an input method session.
176     */
177    public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
178        public void finishInput() {
179            if (!isEnabled()) {
180                return;
181            }
182            onFinishInput();
183            mInputStarted = false;
184        }
185
186        /**
187         * Call {@link InputMethodService#onDisplayCompletions
188         * InputMethodService.onDisplayCompletions()}.
189         */
190        public void displayCompletions(CompletionInfo[] completions) {
191            if (!isEnabled()) {
192                return;
193            }
194            onDisplayCompletions(completions);
195        }
196
197        /**
198         * Call {@link InputMethodService#onUpdateExtractedText
199         * InputMethodService.onUpdateExtractedText()}.
200         */
201        public void updateExtractedText(int token, ExtractedText text) {
202            if (!isEnabled()) {
203                return;
204            }
205            onUpdateExtractedText(token, text);
206        }
207
208        /**
209         * Call {@link InputMethodService#onUpdateSelection
210         * InputMethodService.onUpdateSelection()}.
211         */
212        public void updateSelection(int oldSelStart, int oldSelEnd,
213                int newSelStart, int newSelEnd) {
214            if (!isEnabled()) {
215                return;
216            }
217            InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
218                    newSelStart, newSelEnd);
219        }
220
221        /**
222         * Call {@link InputMethodService#onUpdateCursor
223         * InputMethodService.onUpdateCursor()}.
224         */
225        public void updateCursor(Rect newCursor) {
226            if (!isEnabled()) {
227                return;
228            }
229            InputMethodService.this.onUpdateCursor(newCursor);
230        }
231
232        /**
233         * Call {@link InputMethodService#onAppPrivateCommand
234         * InputMethodService.onAppPrivateCommand()}.
235         */
236        public void appPrivateCommand(String action, Bundle data) {
237            if (!isEnabled()) {
238                return;
239            }
240            InputMethodService.this.onAppPrivateCommand(action, data);
241        }
242    }
243
244    /**
245     * Information about where interesting parts of the input method UI appear.
246     */
247    public static final class Insets {
248        /**
249         * This is the top part of the UI that is the main content.  It is
250         * used to determine the basic space needed, to resize/pan the
251         * application behind.  It is assumed that this inset does not
252         * change very much, since any change will cause a full resize/pan
253         * of the application behind.  This value is relative to the top edge
254         * of the input method window.
255         */
256        int contentTopInsets;
257
258        /**
259         * This is the top part of the UI that is visibly covering the
260         * application behind it.  This provides finer-grained control over
261         * visibility, allowing you to change it relatively frequently (such
262         * as hiding or showing candidates) without disrupting the underlying
263         * UI too much.  For example, this will never resize the application
264         * UI, will only pan if needed to make the current focus visible, and
265         * will not aggressively move the pan position when this changes unless
266         * needed to make the focus visible.  This value is relative to the top edge
267         * of the input method window.
268         */
269        int visibleTopInsets;
270
271        /**
272         * Option for {@link #touchableInsets}: the entire window frame
273         * can be touched.
274         */
275        public static final int TOUCHABLE_INSETS_FRAME
276                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
277
278        /**
279         * Option for {@link #touchableInsets}: the area inside of
280         * the content insets can be touched.
281         */
282        public static final int TOUCHABLE_INSETS_CONTENT
283                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
284
285        /**
286         * Option for {@link #touchableInsets}: the area inside of
287         * the visible insets can be touched.
288         */
289        public static final int TOUCHABLE_INSETS_VISIBLE
290                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
291
292        /**
293         * Determine which area of the window is touchable by the user.  May
294         * be one of: {@link #TOUCHABLE_INSETS_FRAME},
295         * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_VISIBLE}.
296         */
297        public int touchableInsets;
298    }
299
300    @Override public void onCreate() {
301        super.onCreate();
302        mInflater = (LayoutInflater)getSystemService(
303                Context.LAYOUT_INFLATER_SERVICE);
304        mWindow = new SoftInputWindow(this);
305        initViews();
306    }
307
308    void initViews() {
309        mWindowVisible = false;
310        mWindowCreated = false;
311        mShowInputRequested = false;
312        mShowCandidatesRequested = false;
313
314        mRootView = mInflater.inflate(
315                com.android.internal.R.layout.input_method, null);
316        mWindow.setContentView(mRootView);
317        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
318
319        mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
320        mExtractView = null;
321        mExtractEditText = null;
322        mFullscreenApplied = false;
323
324        mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
325        mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
326        mInputView = null;
327        mIsInputViewShown = false;
328
329        mExtractFrame.setVisibility(View.GONE);
330        mCandidatesFrame.setVisibility(View.GONE);
331        mInputFrame.setVisibility(View.GONE);
332    }
333
334    @Override public void onDestroy() {
335        super.onDestroy();
336        mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
337                mInsetsComputer);
338        if (mWindowAdded) {
339            mWindow.dismiss();
340        }
341    }
342
343    /**
344     * Implement to return our standard {@link InputMethodImpl}.  Subclasses
345     * can override to provide their own customized version.
346     */
347    public AbstractInputMethodImpl onCreateInputMethodInterface() {
348        return new InputMethodImpl();
349    }
350
351    /**
352     * Implement to return our standard {@link InputMethodSessionImpl}.  Subclasses
353     * can override to provide their own customized version.
354     */
355    public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
356        return new InputMethodSessionImpl();
357    }
358
359    public LayoutInflater getLayoutInflater() {
360        return mInflater;
361    }
362
363    public Dialog getWindow() {
364        return mWindow;
365    }
366
367    /**
368     * Return the currently active InputBinding for the input method, or
369     * null if there is none.
370     */
371    public InputBinding getCurrentInputBinding() {
372        return mInputBinding;
373    }
374
375    /**
376     * Retrieve the currently active InputConnection that is bound to
377     * the input method, or null if there is none.
378     */
379    public InputConnection getCurrentInputConnection() {
380        return mInputConnection;
381    }
382
383    public boolean getCurrentInputStarted() {
384        return mInputStarted;
385    }
386
387    public EditorInfo getCurrentInputInfo() {
388        return mInputInfo;
389    }
390
391    /**
392     * Re-evaluate whether the input method should be running in fullscreen
393     * mode, and update its UI if this has changed since the last time it
394     * was evaluated.  This will call {@link #onEvaluateFullscreenMode()} to
395     * determine whether it should currently run in fullscreen mode.  You
396     * can use {@link #isFullscreenMode()} to determine if the input method
397     * is currently running in fullscreen mode.
398     */
399    public void updateFullscreenMode() {
400        boolean isFullscreen = onEvaluateFullscreenMode();
401        if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
402            mIsFullscreen = isFullscreen;
403            mFullscreenApplied = true;
404            mWindow.getWindow().setBackgroundDrawable(
405                    onCreateBackgroundDrawable());
406            mExtractFrame.setVisibility(isFullscreen ? View.VISIBLE : View.GONE);
407            if (isFullscreen) {
408                if (mExtractView == null) {
409                    View v = onCreateExtractTextView();
410                    if (v != null) {
411                        setExtractView(v);
412                    }
413                }
414                startExtractingText();
415                mWindow.getWindow().setLayout(FILL_PARENT, FILL_PARENT);
416            } else {
417                mWindow.getWindow().setLayout(WRAP_CONTENT, WRAP_CONTENT);
418            }
419        }
420    }
421
422    /**
423     * Return whether the input method is <em>currently</em> running in
424     * fullscreen mode.  This is the mode that was last determined and
425     * applied by {@link #updateFullscreenMode()}.
426     */
427    public boolean isFullscreenMode() {
428        return mIsFullscreen;
429    }
430
431    /**
432     * Override this to control when the input method should run in
433     * fullscreen mode.  The default implementation runs in fullsceen only
434     * when the screen is in landscape mode and the input view is being
435     * shown ({@link #onEvaluateInputViewShown} returns true).  If you change what
436     * this returns, you will need to call {@link #updateFullscreenMode()}
437     * yourself whenever the returned value may have changed to have it
438     * re-evaluated and applied.
439     */
440    public boolean onEvaluateFullscreenMode() {
441        Configuration config = getResources().getConfiguration();
442        return config.orientation == Configuration.ORIENTATION_LANDSCAPE
443                && onEvaluateInputViewShown();
444    }
445
446    /**
447     * Compute the interesting insets into your UI.  The default implementation
448     * uses the top of the candidates frame for the visible insets, and the
449     * top of the input frame for the content insets.  The default touchable
450     * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
451     *
452     * <p>Note that this method is not called when in fullscreen mode, since
453     * in that case the application is left as-is behind the input method and
454     * not impacted by anything in its UI.
455     *
456     * @param outInsets Fill in with the current UI insets.
457     */
458    public void onComputeInsets(Insets outInsets) {
459        int[] loc = mTmpLocation;
460        if (mInputFrame.getVisibility() == View.VISIBLE) {
461            mInputFrame.getLocationInWindow(loc);
462            outInsets.contentTopInsets = loc[1];
463        }
464        if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
465            mCandidatesFrame.getLocationInWindow(loc);
466            outInsets.visibleTopInsets = loc[1];
467        } else {
468            outInsets.visibleTopInsets = loc[1];
469        }
470        outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
471    }
472
473    /**
474     * Re-evaluate whether the soft input area should currently be shown, and
475     * update its UI if this has changed since the last time it
476     * was evaluated.  This will call {@link #onEvaluateInputViewShown()} to
477     * determine whether the input view should currently be shown.  You
478     * can use {@link #isInputViewShown()} to determine if the input view
479     * is currently shown.
480     */
481    public void updateInputViewShown() {
482        boolean isShown = onEvaluateInputViewShown();
483        if (mIsInputViewShown != isShown && mWindowVisible) {
484            mIsInputViewShown = isShown;
485            mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
486            if (mInputView == null) {
487                View v = onCreateInputView();
488                if (v != null) {
489                    setInputView(v);
490                }
491            }
492        }
493    }
494
495    /**
496     * Return whether the soft input view is <em>currently</em> shown to the
497     * user.  This is the state that was last determined and
498     * applied by {@link #updateInputViewShown()}.
499     */
500    public boolean isInputViewShown() {
501        return mIsInputViewShown;
502    }
503
504    /**
505     * Override this to control when the soft input area should be shown to
506     * the user.  The default implementation only shows the input view when
507     * there is no hard keyboard or the keyboard is hidden.  If you change what
508     * this returns, you will need to call {@link #updateInputViewShown()}
509     * yourself whenever the returned value may have changed to have it
510     * re-evalauted and applied.
511     */
512    public boolean onEvaluateInputViewShown() {
513        Configuration config = getResources().getConfiguration();
514        return config.keyboard == Configuration.KEYBOARD_NOKEYS
515                || config.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES;
516    }
517
518    /**
519     * Controls the visibility of the candidates display area.  By default
520     * it is hidden.
521     */
522    public void setCandidatesViewShown(boolean shown) {
523        if (mShowCandidatesRequested != shown) {
524            mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
525            if (!mShowInputRequested) {
526                // If we are being asked to show the candidates view while the app
527                // has not asked for the input view to be shown, then we need
528                // to update whether the window is shown.
529                if (shown) {
530                    showWindow(false);
531                } else {
532                    hideWindow();
533                }
534            }
535            mShowCandidatesRequested = shown;
536        }
537    }
538
539    public void setStatusIcon(int iconResId) {
540        mStatusIcon = iconResId;
541        if (mInputConnection != null && mWindowVisible) {
542            mInputConnection.showStatusIcon(getPackageName(), iconResId);
543        }
544    }
545
546    /**
547     * Force switch to a new input method, as identified by <var>id</var>.  This
548     * input method will be destroyed, and the requested one started on the
549     * current input field.
550     *
551     * @param id Unique identifier of the new input method ot start.
552     */
553    public void switchInputMethod(String id) {
554        ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE))
555                .setInputMethod(mToken, id);
556    }
557
558    public void setExtractView(View view) {
559        mExtractFrame.removeAllViews();
560        mExtractFrame.addView(view, new FrameLayout.LayoutParams(
561                ViewGroup.LayoutParams.FILL_PARENT,
562                ViewGroup.LayoutParams.WRAP_CONTENT));
563        mExtractView = view;
564        if (view != null) {
565            mExtractEditText = (ExtractEditText)view.findViewById(
566                    com.android.internal.R.id.inputExtractEditText);
567            startExtractingText();
568        } else {
569            mExtractEditText = null;
570        }
571    }
572
573    /**
574     * Replaces the current candidates view with a new one.  You only need to
575     * call this when dynamically changing the view; normally, you should
576     * implement {@link #onCreateCandidatesView()} and create your view when
577     * first needed by the input method.
578     */
579    public void setCandidatesView(View view) {
580        mCandidatesFrame.removeAllViews();
581        mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
582                ViewGroup.LayoutParams.FILL_PARENT,
583                ViewGroup.LayoutParams.WRAP_CONTENT));
584    }
585
586    /**
587     * Replaces the current input view with a new one.  You only need to
588     * call this when dynamically changing the view; normally, you should
589     * implement {@link #onCreateInputView()} and create your view when
590     * first needed by the input method.
591     */
592    public void setInputView(View view) {
593        mInputFrame.removeAllViews();
594        mInputFrame.addView(view, new FrameLayout.LayoutParams(
595                ViewGroup.LayoutParams.FILL_PARENT,
596                ViewGroup.LayoutParams.WRAP_CONTENT));
597        mInputView = view;
598    }
599
600    /**
601     * Called by the framework to create a Drawable for the background of
602     * the input method window.  May return null for no background.  The default
603     * implementation returns a non-null standard background only when in
604     * fullscreen mode.
605     */
606    public Drawable onCreateBackgroundDrawable() {
607        if (isFullscreenMode()) {
608            return getResources().getDrawable(
609                    com.android.internal.R.drawable.input_method_fullscreen_background);
610        }
611        return null;
612    }
613
614    /**
615     * Called by the framework to create the layout for showing extacted text.
616     * Only called when in fullscreen mode.  The returned view hierarchy must
617     * have an {@link ExtractEditText} whose ID is
618     * {@link android.R.id#inputExtractEditText}.
619     */
620    public View onCreateExtractTextView() {
621        return mInflater.inflate(
622                com.android.internal.R.layout.input_method_extract_view, null);
623    }
624
625    /**
626     * Create and return the view hierarchy used to show candidates.  This will
627     * be called once, when the candidates are first displayed.  You can return
628     * null to have no candidates view; the default implementation returns null.
629     *
630     * <p>To control when the candidates view is displayed, use
631     * {@link #setCandidatesViewShown(boolean)}.
632     * To change the candidates view after the first one is created by this
633     * function, use {@link #setCandidatesView(View)}.
634     */
635    public View onCreateCandidatesView() {
636        return null;
637    }
638
639    /**
640     * Create and return the view hierarchy used for the input area (such as
641     * a soft keyboard).  This will be called once, when the input area is
642     * first displayed.  You can return null to have no input area; the default
643     * implementation returns null.
644     *
645     * <p>To control when the input view is displayed, implement
646     * {@link #onEvaluateInputViewShown()}.
647     * To change the input view after the first one is created by this
648     * function, use {@link #setInputView(View)}.
649     */
650    public View onCreateInputView() {
651        return null;
652    }
653
654    /**
655     * Called when an input session is starting or restarting.
656     *
657     * @param info Description of the type of text being edited.
658     * @param restarting Set to true if we are restarting input on the
659     * same text field as before.
660     */
661    public void onStartInputView(EditorInfo info, boolean restarting) {
662    }
663
664    @Override
665    public void onConfigurationChanged(Configuration newConfig) {
666        super.onConfigurationChanged(newConfig);
667
668        boolean visible = mWindowVisible;
669        boolean showingInput = mShowInputRequested;
670        boolean showingCandidates = mShowCandidatesRequested;
671        initViews();
672        if (visible) {
673            if (showingCandidates) {
674                setCandidatesViewShown(true);
675            }
676            showWindow(showingInput);
677        }
678    }
679
680    public void showWindow(boolean showInput) {
681        if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
682                + " mShowInputRequested=" + mShowInputRequested
683                + " mWindowAdded=" + mWindowAdded
684                + " mWindowCreated=" + mWindowCreated
685                + " mWindowVisible=" + mWindowVisible
686                + " mInputStarted=" + mInputStarted);
687        boolean doShowInput = false;
688        boolean wasVisible = mWindowVisible;
689        mWindowVisible = true;
690        if (!mShowInputRequested) {
691            doShowInput = true;
692            mShowInputRequested = true;
693        } else {
694            showInput = true;
695        }
696
697        if (doShowInput) {
698            if (DEBUG) Log.v(TAG, "showWindow: updating UI");
699            updateFullscreenMode();
700            updateInputViewShown();
701        }
702
703        if (!mWindowAdded || !mWindowCreated) {
704            mWindowAdded = true;
705            mWindowCreated = true;
706            View v = onCreateCandidatesView();
707            if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
708            if (v != null) {
709                setCandidatesView(v);
710            }
711        }
712        if (doShowInput) {
713            if (mInputStarted) {
714                if (DEBUG) Log.v(TAG, "showWindow: starting input view");
715                onStartInputView(mInputInfo, false);
716            }
717            startExtractingText();
718        }
719
720        if (!wasVisible) {
721            if (DEBUG) Log.v(TAG, "showWindow: showing!");
722            mWindow.show();
723            if (mInputConnection != null) {
724                mInputConnection.showStatusIcon(getPackageName(), mStatusIcon);
725            }
726        }
727    }
728
729    public void hideWindow() {
730        if (mWindowVisible) {
731            mWindow.hide();
732            mWindowVisible = false;
733            if (mInputConnection != null) {
734                mInputConnection.hideStatusIcon();
735            }
736        }
737    }
738
739    public void onBindInput() {
740    }
741
742    public void onStartInput(EditorInfo attribute, boolean restarting) {
743    }
744
745    void doStartInput(EditorInfo attribute, boolean restarting) {
746        mInputStarted = true;
747        mInputInfo = attribute;
748        onStartInput(attribute, restarting);
749        if (mWindowVisible) {
750            if (mWindowCreated) {
751                onStartInputView(mInputInfo, restarting);
752            }
753            startExtractingText();
754        }
755    }
756
757    public void onFinishInput() {
758    }
759
760    /**
761     * Called when the application has reported auto-completion candidates that
762     * it would like to have the input method displayed.  Typically these are
763     * only used when an input method is running in full-screen mode, since
764     * otherwise the user can see and interact with the pop-up window of
765     * completions shown by the application.
766     *
767     * <p>The default implementation here does nothing.
768     */
769    public void onDisplayCompletions(CompletionInfo[] completions) {
770    }
771
772    /**
773     * Called when the application has reported new extracted text to be shown
774     * due to changes in its current text state.  The default implementation
775     * here places the new text in the extract edit text, when the input
776     * method is running in fullscreen mode.
777     */
778    public void onUpdateExtractedText(int token, ExtractedText text) {
779        if (mExtractedToken != token) {
780            return;
781        }
782        if (mExtractEditText != null && text != null) {
783            mExtractedText = text;
784            mExtractEditText.setExtractedText(text);
785        }
786    }
787
788    /**
789     * Called when the application has reported a new selection region of
790     * the text.  This is called whether or not the input method has requested
791     * extracted text updates, although if so it will not receive this call
792     * if the extracted text has changed as well.
793     *
794     * <p>The default implementation takes care of updating the cursor in
795     * the extract text, if it is being shown.
796     */
797    public void onUpdateSelection(int oldSelStart, int oldSelEnd,
798            int newSelStart, int newSelEnd) {
799        if (mExtractEditText != null && mExtractedText != null) {
800            final int off = mExtractedText.startOffset;
801            mExtractEditText.setSelection(newSelStart-off, newSelEnd-off);
802        }
803    }
804
805    /**
806     * Called when the application has reported a new location of its text
807     * cursor.  This is only called if explicitly requested by the input method.
808     * The default implementation does nothing.
809     */
810    public void onUpdateCursor(Rect newCursor) {
811    }
812
813    /**
814     * Close this input method's soft input area, removing it from the display.
815     * The input method will continue running, but the user can no longer use
816     * it to generate input by touching the screen.
817     */
818    public void dismissSoftInput() {
819        ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE))
820                .hideSoftInputFromInputMethod(mToken);
821    }
822
823    public boolean onKeyDown(int keyCode, KeyEvent event) {
824        if (mWindowVisible && event.getKeyCode() == KeyEvent.KEYCODE_BACK
825                && event.getRepeatCount() == 0) {
826            dismissSoftInput();
827            return true;
828        }
829        return false;
830    }
831
832    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
833        return false;
834    }
835
836    public boolean onKeyUp(int keyCode, KeyEvent event) {
837        return false;
838    }
839
840    public boolean onTrackballEvent(MotionEvent event) {
841        return false;
842    }
843
844    public void onAppPrivateCommand(String action, Bundle data) {
845    }
846
847    void startExtractingText() {
848        if (mExtractEditText != null && getCurrentInputStarted()
849                && isFullscreenMode()) {
850            mExtractedToken++;
851            ExtractedTextRequest req = new ExtractedTextRequest();
852            req.token = mExtractedToken;
853            req.hintMaxLines = 10;
854            req.hintMaxChars = 10000;
855            mExtractedText = mInputConnection.getExtractedText(req,
856                    InputConnection.EXTRACTED_TEXT_MONITOR);
857            if (mExtractedText != null) {
858                mExtractEditText.setExtractedText(mExtractedText);
859            }
860            mExtractEditText.setInputType(getCurrentInputInfo().inputType);
861            mExtractEditText.setHint(mInputInfo.hintText);
862        }
863    }
864}
865