InputMethodService.java revision 9266c558bf1d21ff647525ff99f7dadbca417309
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 *
52 * <p>An input method has significant discretion in how it goes about its
53 * work: the {@link android.inputmethodservice.InputMethodService} provides
54 * a basic framework for standard UI elements (input view, candidates view,
55 * and running in fullscreen mode), but it is up to a particular implementor
56 * to decide how to use them.  For example, one input method could implement
57 * an input area with a keyboard, another could allow the user to draw text,
58 * while a third could have no input area (and thus not be visible to the
59 * user) but instead listen to audio and perform text to speech conversion.</p>
60 *
61 * <p>In the implementation provided here, all of these elements are placed
62 * together in a single window managed by the InputMethodService.  It will
63 * execute callbacks as it needs information about them, and provides APIs for
64 * programmatic control over them.  They layout of these elements is explicitly
65 * defined:</p>
66 *
67 * <ul>
68 * <li>The soft input view, if available, is placed at the bottom of the
69 * screen.
70 * <li>The candidates view, if currently shown, is placed above the soft
71 * input view.
72 * <li>If not running fullscreen, the application is moved or resized to be
73 * above these views; if running fullscreen, the window will completely cover
74 * the application and its top part will contain the extract text of what is
75 * currently being edited by the application.
76 * </ul>
77 *
78 *
79 * <a name="SoftInputView"></a>
80 * <h3>Soft Input View</h3>
81 *
82 * <p>Central to most input methods is the soft input view.  This is where most
83 * user interaction occurs: pressing on soft keys, drawing characters, or
84 * however else your input method wants to generate text.  Most implementations
85 * will simply have their own view doing all of this work, and return a new
86 * instance of it when {@link #onCreateInputView()} is called.  At that point,
87 * as long as the input view is visible, you will see user interaction in
88 * that view and can call back on the InputMethodService to interact with the
89 * application as appropriate.</p>
90 *
91 * <p>There are some situations where you want to decide whether or not your
92 * soft input view should be shown to the user.  This is done by implementing
93 * the {@link #onEvaluateInputViewShown()} to return true or false based on
94 * whether it should be shown in the current environment.  If any of your
95 * state has changed that may impact this, call
96 * {@link #updateInputViewShown()} to have it re-evaluated.  The default
97 * implementation always shows the input view unless there is a hard
98 * keyboard available, which is the appropriate behavior for most input
99 * methods.</p>
100 *
101 *
102 * <a name="CandidatesView"></a>
103 * <h3>Candidates View</h3>
104 *
105 * <p>Often while the user is generating raw text, an input method wants to
106 * provide them with a list of possible interpretations of that text that can
107 * be selected for use.  This is accomplished with the candidates view, and
108 * like the soft input view you implement {@link #onCreateCandidatesView()}
109 * to instantiate your own view implementing your candidates UI.</p>
110 *
111 * <p>Management of the candidates view is a little different than the input
112 * view, because the candidates view tends to be more transient, being shown
113 * only when there are possible candidates for the current text being entered
114 * by the user.  To control whether the candidates view is shown, you use
115 * {@link #setCandidatesViewShown(boolean)}.  Note that because the candidate
116 * view tends to be shown and hidden a lot, it does not impact the application
117 * UI in the same way as the soft input view: it will never cause application
118 * windows to resize, only cause them to be panned if needed for the user to
119 * see the current focus.</p>
120 *
121 *
122 * <a name="FullscreenMode"></a>
123 * <h3>Fullscreen Mode</h3>
124 *
125 * <p>Sometimes your input method UI is too large to integrate with the
126 * application UI, so you just want to take over the screen.  This is
127 * accomplished by switching to full-screen mode, causing the input method
128 * window to fill the entire screen and add its own "extracted text" editor
129 * showing the user the text that is being typed.  Unlike the other UI elements,
130 * there is a standard implementation for the extract editor that you should
131 * not need to change.  The editor is placed at the top of the IME, above the
132 * input and candidates views.</p>
133 *
134 * <p>Similar to the input view, you control whether the IME is running in
135 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
136 * to return true or false based on
137 * whether it should be fullscreen in the current environment.  If any of your
138 * state has changed that may impact this, call
139 * {@link #updateFullscreenMode()} to have it re-evaluated.  The default
140 * implementation selects fullscreen mode when the screen is in a landscape
141 * orientation, which is appropriate behavior for most input methods that have
142 * a significant input area.</p>
143 *
144 * <p>When in fullscreen mode, you have some special requirements because the
145 * user can not see the application UI.  In particular, you should implement
146 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
147 * generated by your application, typically in your candidates view like you
148 * would normally show candidates.
149 *
150 *
151 * <a name="GeneratingText"></a>
152 * <h3>Generating Text</h3>
153 *
154 * <p>The key part of an IME is of course generating text for the application.
155 * This is done through calls to the
156 * {@link android.view.inputmethod.InputConnection} interface to the
157 * application, which can be retrieved from {@link #getCurrentInputConnection()}.
158 * This interface allows you to generate raw key events or, if the target
159 * supports it, directly edit in strings of candidates and committed text.</p>
160 *
161 * <p>Information about what the target is expected and supports can be found
162 * through the {@link android.view.inputmethod.EditorInfo} class, which is
163 * retrieved with {@link #getCurrentInputEditorInfo()} method.  The most
164 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
165 * EditorInfo.inputType}; in particular, if this is
166 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
167 * then the target does not support complex edits and you need to only deliver
168 * raw key events to it.  An input method will also want to look at other
169 * values here, to for example detect password mode, auto complete text views,
170 * phone number entry, etc.</p>
171 *
172 * <p>When the user switches between input targets, you will receive calls to
173 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
174 * You can use these to reset and initialize your input state for the current
175 * target.  For example, you will often want to clear any input state, and
176 * update a soft keyboard to be appropriate for the new inputType.</p>
177 */
178public class InputMethodService extends AbstractInputMethodService {
179    static final String TAG = "InputMethodService";
180    static final boolean DEBUG = false;
181
182    LayoutInflater mInflater;
183    View mRootView;
184    SoftInputWindow mWindow;
185    boolean mWindowCreated;
186    boolean mWindowAdded;
187    boolean mWindowVisible;
188    FrameLayout mExtractFrame;
189    FrameLayout mCandidatesFrame;
190    FrameLayout mInputFrame;
191
192    IBinder mToken;
193
194    InputBinding mInputBinding;
195    InputConnection mInputConnection;
196    boolean mInputStarted;
197    EditorInfo mInputEditorInfo;
198
199    boolean mShowInputRequested;
200    boolean mShowCandidatesRequested;
201
202    boolean mFullscreenApplied;
203    boolean mIsFullscreen;
204    View mExtractView;
205    ExtractEditText mExtractEditText;
206    ExtractedText mExtractedText;
207    int mExtractedToken;
208
209    View mInputView;
210    boolean mIsInputViewShown;
211
212    int mStatusIcon;
213
214    final Insets mTmpInsets = new Insets();
215    final int[] mTmpLocation = new int[2];
216
217    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
218            new ViewTreeObserver.OnComputeInternalInsetsListener() {
219        public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
220            if (isFullscreenMode()) {
221                // In fullscreen mode, we just say the window isn't covering
222                // any content so we don't impact whatever is behind.
223                View decor = getWindow().getWindow().getDecorView();
224                info.contentInsets.top = info.visibleInsets.top
225                        = decor.getHeight();
226                info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
227            } else {
228                onComputeInsets(mTmpInsets);
229                info.contentInsets.top = mTmpInsets.contentTopInsets;
230                info.visibleInsets.top = mTmpInsets.visibleTopInsets;
231                info.setTouchableInsets(mTmpInsets.touchableInsets);
232            }
233        }
234    };
235
236    /**
237     * Concrete implementation of
238     * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
239     * all of the standard behavior for an input method.
240     */
241    public class InputMethodImpl extends AbstractInputMethodImpl {
242        /**
243         * Take care of attaching the given window token provided by the system.
244         */
245        public void attachToken(IBinder token) {
246            if (mToken == null) {
247                mToken = token;
248                mWindow.setToken(token);
249            }
250        }
251
252        /**
253         * Handle a new input binding, calling
254         * {@link InputMethodService#onBindInput InputMethodService.onBindInput()}
255         * when done.
256         */
257        public void bindInput(InputBinding binding) {
258            mInputBinding = binding;
259            mInputConnection = binding.getConnection();
260            onBindInput();
261        }
262
263        /**
264         * Clear the current input binding.
265         */
266        public void unbindInput() {
267            mInputStarted = false;
268            mInputBinding = null;
269            mInputConnection = null;
270        }
271
272        public void startInput(EditorInfo attribute) {
273            doStartInput(attribute, false);
274        }
275
276        public void restartInput(EditorInfo attribute) {
277            doStartInput(attribute, false);
278        }
279
280        /**
281         * Handle a request by the system to hide the soft input area.
282         */
283        public void hideSoftInput() {
284            if (DEBUG) Log.v(TAG, "hideSoftInput()");
285            mShowInputRequested = false;
286            hideWindow();
287        }
288
289        /**
290         * Handle a request by the system to show the soft input area.
291         */
292        public void showSoftInput(int flags) {
293            if (DEBUG) Log.v(TAG, "showSoftInput()");
294            onShowRequested(flags);
295        }
296    }
297
298    /**
299     * Concrete implementation of
300     * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
301     * all of the standard behavior for an input method session.
302     */
303    public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
304        public void finishInput() {
305            if (!isEnabled()) {
306                return;
307            }
308            onFinishInput();
309            mInputStarted = false;
310        }
311
312        /**
313         * Call {@link InputMethodService#onDisplayCompletions
314         * InputMethodService.onDisplayCompletions()}.
315         */
316        public void displayCompletions(CompletionInfo[] completions) {
317            if (!isEnabled()) {
318                return;
319            }
320            onDisplayCompletions(completions);
321        }
322
323        /**
324         * Call {@link InputMethodService#onUpdateExtractedText
325         * InputMethodService.onUpdateExtractedText()}.
326         */
327        public void updateExtractedText(int token, ExtractedText text) {
328            if (!isEnabled()) {
329                return;
330            }
331            onUpdateExtractedText(token, text);
332        }
333
334        /**
335         * Call {@link InputMethodService#onUpdateSelection
336         * InputMethodService.onUpdateSelection()}.
337         */
338        public void updateSelection(int oldSelStart, int oldSelEnd,
339                int newSelStart, int newSelEnd,
340                int candidatesStart, int candidatesEnd) {
341            if (!isEnabled()) {
342                return;
343            }
344            InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
345                    newSelStart, newSelEnd, candidatesStart, candidatesEnd);
346        }
347
348        /**
349         * Call {@link InputMethodService#onUpdateCursor
350         * InputMethodService.onUpdateCursor()}.
351         */
352        public void updateCursor(Rect newCursor) {
353            if (!isEnabled()) {
354                return;
355            }
356            InputMethodService.this.onUpdateCursor(newCursor);
357        }
358
359        /**
360         * Call {@link InputMethodService#onAppPrivateCommand
361         * InputMethodService.onAppPrivateCommand()}.
362         */
363        public void appPrivateCommand(String action, Bundle data) {
364            if (!isEnabled()) {
365                return;
366            }
367            InputMethodService.this.onAppPrivateCommand(action, data);
368        }
369    }
370
371    /**
372     * Information about where interesting parts of the input method UI appear.
373     */
374    public static final class Insets {
375        /**
376         * This is the top part of the UI that is the main content.  It is
377         * used to determine the basic space needed, to resize/pan the
378         * application behind.  It is assumed that this inset does not
379         * change very much, since any change will cause a full resize/pan
380         * of the application behind.  This value is relative to the top edge
381         * of the input method window.
382         */
383        int contentTopInsets;
384
385        /**
386         * This is the top part of the UI that is visibly covering the
387         * application behind it.  This provides finer-grained control over
388         * visibility, allowing you to change it relatively frequently (such
389         * as hiding or showing candidates) without disrupting the underlying
390         * UI too much.  For example, this will never resize the application
391         * UI, will only pan if needed to make the current focus visible, and
392         * will not aggressively move the pan position when this changes unless
393         * needed to make the focus visible.  This value is relative to the top edge
394         * of the input method window.
395         */
396        int visibleTopInsets;
397
398        /**
399         * Option for {@link #touchableInsets}: the entire window frame
400         * can be touched.
401         */
402        public static final int TOUCHABLE_INSETS_FRAME
403                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
404
405        /**
406         * Option for {@link #touchableInsets}: the area inside of
407         * the content insets can be touched.
408         */
409        public static final int TOUCHABLE_INSETS_CONTENT
410                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
411
412        /**
413         * Option for {@link #touchableInsets}: the area inside of
414         * the visible insets can be touched.
415         */
416        public static final int TOUCHABLE_INSETS_VISIBLE
417                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
418
419        /**
420         * Determine which area of the window is touchable by the user.  May
421         * be one of: {@link #TOUCHABLE_INSETS_FRAME},
422         * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_VISIBLE}.
423         */
424        public int touchableInsets;
425    }
426
427    @Override public void onCreate() {
428        super.onCreate();
429        mInflater = (LayoutInflater)getSystemService(
430                Context.LAYOUT_INFLATER_SERVICE);
431        mWindow = new SoftInputWindow(this);
432        initViews();
433        mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT);
434    }
435
436    void initViews() {
437        mWindowVisible = false;
438        mWindowCreated = false;
439        mShowInputRequested = false;
440        mShowCandidatesRequested = false;
441
442        mRootView = mInflater.inflate(
443                com.android.internal.R.layout.input_method, null);
444        mWindow.setContentView(mRootView);
445        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
446
447        mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
448        mExtractView = null;
449        mExtractEditText = null;
450        mFullscreenApplied = false;
451
452        mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
453        mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
454        mInputView = null;
455        mIsInputViewShown = false;
456
457        mExtractFrame.setVisibility(View.GONE);
458        mCandidatesFrame.setVisibility(View.GONE);
459        mInputFrame.setVisibility(View.GONE);
460    }
461
462    @Override public void onDestroy() {
463        super.onDestroy();
464        mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
465                mInsetsComputer);
466        if (mWindowAdded) {
467            mWindow.dismiss();
468        }
469    }
470
471    /**
472     * Implement to return our standard {@link InputMethodImpl}.  Subclasses
473     * can override to provide their own customized version.
474     */
475    public AbstractInputMethodImpl onCreateInputMethodInterface() {
476        return new InputMethodImpl();
477    }
478
479    /**
480     * Implement to return our standard {@link InputMethodSessionImpl}.  Subclasses
481     * can override to provide their own customized version.
482     */
483    public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
484        return new InputMethodSessionImpl();
485    }
486
487    public LayoutInflater getLayoutInflater() {
488        return mInflater;
489    }
490
491    public Dialog getWindow() {
492        return mWindow;
493    }
494
495    /**
496     * Return the currently active InputBinding for the input method, or
497     * null if there is none.
498     */
499    public InputBinding getCurrentInputBinding() {
500        return mInputBinding;
501    }
502
503    /**
504     * Retrieve the currently active InputConnection that is bound to
505     * the input method, or null if there is none.
506     */
507    public InputConnection getCurrentInputConnection() {
508        return mInputConnection;
509    }
510
511    public boolean getCurrentInputStarted() {
512        return mInputStarted;
513    }
514
515    public EditorInfo getCurrentInputEditorInfo() {
516        return mInputEditorInfo;
517    }
518
519    /**
520     * Re-evaluate whether the input method should be running in fullscreen
521     * mode, and update its UI if this has changed since the last time it
522     * was evaluated.  This will call {@link #onEvaluateFullscreenMode()} to
523     * determine whether it should currently run in fullscreen mode.  You
524     * can use {@link #isFullscreenMode()} to determine if the input method
525     * is currently running in fullscreen mode.
526     */
527    public void updateFullscreenMode() {
528        boolean isFullscreen = onEvaluateFullscreenMode();
529        if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
530            mIsFullscreen = isFullscreen;
531            mFullscreenApplied = true;
532            mWindow.getWindow().setBackgroundDrawable(
533                    onCreateBackgroundDrawable());
534            mExtractFrame.setVisibility(isFullscreen ? View.VISIBLE : View.GONE);
535            if (isFullscreen) {
536                if (mExtractView == null) {
537                    View v = onCreateExtractTextView();
538                    if (v != null) {
539                        setExtractView(v);
540                    }
541                }
542                startExtractingText();
543                mWindow.getWindow().setLayout(FILL_PARENT, FILL_PARENT);
544            } else {
545                mWindow.getWindow().setLayout(WRAP_CONTENT, WRAP_CONTENT);
546            }
547        }
548    }
549
550    /**
551     * Return whether the input method is <em>currently</em> running in
552     * fullscreen mode.  This is the mode that was last determined and
553     * applied by {@link #updateFullscreenMode()}.
554     */
555    public boolean isFullscreenMode() {
556        return mIsFullscreen;
557    }
558
559    /**
560     * Override this to control when the input method should run in
561     * fullscreen mode.  The default implementation runs in fullsceen only
562     * when the screen is in landscape mode and the input view is being
563     * shown ({@link #onEvaluateInputViewShown} returns true).  If you change what
564     * this returns, you will need to call {@link #updateFullscreenMode()}
565     * yourself whenever the returned value may have changed to have it
566     * re-evaluated and applied.
567     */
568    public boolean onEvaluateFullscreenMode() {
569        Configuration config = getResources().getConfiguration();
570        return config.orientation == Configuration.ORIENTATION_LANDSCAPE
571                && onEvaluateInputViewShown();
572    }
573
574    /**
575     * Compute the interesting insets into your UI.  The default implementation
576     * uses the top of the candidates frame for the visible insets, and the
577     * top of the input frame for the content insets.  The default touchable
578     * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
579     *
580     * <p>Note that this method is not called when in fullscreen mode, since
581     * in that case the application is left as-is behind the input method and
582     * not impacted by anything in its UI.
583     *
584     * @param outInsets Fill in with the current UI insets.
585     */
586    public void onComputeInsets(Insets outInsets) {
587        int[] loc = mTmpLocation;
588        if (mInputFrame.getVisibility() == View.VISIBLE) {
589            mInputFrame.getLocationInWindow(loc);
590        } else {
591            loc[1] = 0;
592        }
593        outInsets.contentTopInsets = loc[1];
594        if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
595            mCandidatesFrame.getLocationInWindow(loc);
596        }
597        outInsets.visibleTopInsets = loc[1];
598        outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
599    }
600
601    /**
602     * Re-evaluate whether the soft input area should currently be shown, and
603     * update its UI if this has changed since the last time it
604     * was evaluated.  This will call {@link #onEvaluateInputViewShown()} to
605     * determine whether the input view should currently be shown.  You
606     * can use {@link #isInputViewShown()} to determine if the input view
607     * is currently shown.
608     */
609    public void updateInputViewShown() {
610        boolean isShown = onEvaluateInputViewShown();
611        if (mIsInputViewShown != isShown && mWindowVisible) {
612            mIsInputViewShown = isShown;
613            mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
614            if (mInputView == null) {
615                View v = onCreateInputView();
616                if (v != null) {
617                    setInputView(v);
618                }
619            }
620        }
621    }
622
623    /**
624     * Return whether the soft input view is <em>currently</em> shown to the
625     * user.  This is the state that was last determined and
626     * applied by {@link #updateInputViewShown()}.
627     */
628    public boolean isInputViewShown() {
629        return mIsInputViewShown;
630    }
631
632    /**
633     * Override this to control when the soft input area should be shown to
634     * the user.  The default implementation only shows the input view when
635     * there is no hard keyboard or the keyboard is hidden.  If you change what
636     * this returns, you will need to call {@link #updateInputViewShown()}
637     * yourself whenever the returned value may have changed to have it
638     * re-evalauted and applied.
639     */
640    public boolean onEvaluateInputViewShown() {
641        Configuration config = getResources().getConfiguration();
642        return config.keyboard == Configuration.KEYBOARD_NOKEYS
643                || config.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES;
644    }
645
646    /**
647     * Controls the visibility of the candidates display area.  By default
648     * it is hidden.
649     */
650    public void setCandidatesViewShown(boolean shown) {
651        if (mShowCandidatesRequested != shown) {
652            mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
653            if (!mShowInputRequested) {
654                // If we are being asked to show the candidates view while the app
655                // has not asked for the input view to be shown, then we need
656                // to update whether the window is shown.
657                if (shown) {
658                    showWindow(false);
659                } else {
660                    hideWindow();
661                }
662            }
663            mShowCandidatesRequested = shown;
664        }
665    }
666
667    public void setStatusIcon(int iconResId) {
668        mStatusIcon = iconResId;
669        if (mInputConnection != null && mWindowVisible) {
670            mInputConnection.showStatusIcon(getPackageName(), iconResId);
671        }
672    }
673
674    /**
675     * Force switch to a new input method, as identified by <var>id</var>.  This
676     * input method will be destroyed, and the requested one started on the
677     * current input field.
678     *
679     * @param id Unique identifier of the new input method ot start.
680     */
681    public void switchInputMethod(String id) {
682        ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE))
683                .setInputMethod(mToken, id);
684    }
685
686    public void setExtractView(View view) {
687        mExtractFrame.removeAllViews();
688        mExtractFrame.addView(view, new FrameLayout.LayoutParams(
689                ViewGroup.LayoutParams.FILL_PARENT,
690                ViewGroup.LayoutParams.WRAP_CONTENT));
691        mExtractView = view;
692        if (view != null) {
693            mExtractEditText = (ExtractEditText)view.findViewById(
694                    com.android.internal.R.id.inputExtractEditText);
695            startExtractingText();
696        } else {
697            mExtractEditText = null;
698        }
699    }
700
701    /**
702     * Replaces the current candidates view with a new one.  You only need to
703     * call this when dynamically changing the view; normally, you should
704     * implement {@link #onCreateCandidatesView()} and create your view when
705     * first needed by the input method.
706     */
707    public void setCandidatesView(View view) {
708        mCandidatesFrame.removeAllViews();
709        mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
710                ViewGroup.LayoutParams.FILL_PARENT,
711                ViewGroup.LayoutParams.WRAP_CONTENT));
712    }
713
714    /**
715     * Replaces the current input view with a new one.  You only need to
716     * call this when dynamically changing the view; normally, you should
717     * implement {@link #onCreateInputView()} and create your view when
718     * first needed by the input method.
719     */
720    public void setInputView(View view) {
721        mInputFrame.removeAllViews();
722        mInputFrame.addView(view, new FrameLayout.LayoutParams(
723                ViewGroup.LayoutParams.FILL_PARENT,
724                ViewGroup.LayoutParams.WRAP_CONTENT));
725        mInputView = view;
726    }
727
728    /**
729     * Called by the framework to create a Drawable for the background of
730     * the input method window.  May return null for no background.  The default
731     * implementation returns a non-null standard background only when in
732     * fullscreen mode.
733     */
734    public Drawable onCreateBackgroundDrawable() {
735        if (isFullscreenMode()) {
736            return getResources().getDrawable(
737                    com.android.internal.R.drawable.input_method_fullscreen_background);
738        }
739        return null;
740    }
741
742    /**
743     * Called by the framework to create the layout for showing extacted text.
744     * Only called when in fullscreen mode.  The returned view hierarchy must
745     * have an {@link ExtractEditText} whose ID is
746     * {@link android.R.id#inputExtractEditText}.
747     */
748    public View onCreateExtractTextView() {
749        return mInflater.inflate(
750                com.android.internal.R.layout.input_method_extract_view, null);
751    }
752
753    /**
754     * Create and return the view hierarchy used to show candidates.  This will
755     * be called once, when the candidates are first displayed.  You can return
756     * null to have no candidates view; the default implementation returns null.
757     *
758     * <p>To control when the candidates view is displayed, use
759     * {@link #setCandidatesViewShown(boolean)}.
760     * To change the candidates view after the first one is created by this
761     * function, use {@link #setCandidatesView(View)}.
762     */
763    public View onCreateCandidatesView() {
764        return null;
765    }
766
767    /**
768     * Create and return the view hierarchy used for the input area (such as
769     * a soft keyboard).  This will be called once, when the input area is
770     * first displayed.  You can return null to have no input area; the default
771     * implementation returns null.
772     *
773     * <p>To control when the input view is displayed, implement
774     * {@link #onEvaluateInputViewShown()}.
775     * To change the input view after the first one is created by this
776     * function, use {@link #setInputView(View)}.
777     */
778    public View onCreateInputView() {
779        return null;
780    }
781
782    /**
783     * Called when an input session is starting or restarting.
784     *
785     * @param info Description of the type of text being edited.
786     * @param restarting Set to true if we are restarting input on the
787     * same text field as before.
788     */
789    public void onStartInputView(EditorInfo info, boolean restarting) {
790    }
791
792    @Override
793    public void onConfigurationChanged(Configuration newConfig) {
794        super.onConfigurationChanged(newConfig);
795
796        boolean visible = mWindowVisible;
797        boolean showingInput = mShowInputRequested;
798        boolean showingCandidates = mShowCandidatesRequested;
799        initViews();
800        if (visible) {
801            if (showingCandidates) {
802                setCandidatesViewShown(true);
803            }
804            showWindow(showingInput);
805        }
806    }
807
808    /**
809     * The system has decided that it may be time to show your input method.
810     * This is called due to a corresponding call to your
811     * {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)}
812     * method.  The default implementation simply calls
813     * {@link #showWindow(boolean)}, except if the
814     * {@link InputMethod#SHOW_EXPLICIT InputMethod.SHOW_EXPLICIT} flag is
815     * not set and the input method is running in fullscreen mode.
816     *
817     * @param flags Provides additional information about the show request,
818     * as per {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)}.
819     */
820    public void onShowRequested(int flags) {
821        if ((flags&InputMethod.SHOW_EXPLICIT) == 0 && onEvaluateFullscreenMode()) {
822            // Don't show if this is not explicit requested by the user and
823            // the input method is fullscreen.  That would be too disruptive.
824            return;
825        }
826        showWindow(true);
827    }
828
829    public void showWindow(boolean showInput) {
830        if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
831                + " mShowInputRequested=" + mShowInputRequested
832                + " mWindowAdded=" + mWindowAdded
833                + " mWindowCreated=" + mWindowCreated
834                + " mWindowVisible=" + mWindowVisible
835                + " mInputStarted=" + mInputStarted);
836        boolean doShowInput = false;
837        boolean wasVisible = mWindowVisible;
838        mWindowVisible = true;
839        if (!mShowInputRequested) {
840            doShowInput = true;
841            mShowInputRequested = true;
842        } else {
843            showInput = true;
844        }
845
846        if (doShowInput) {
847            if (DEBUG) Log.v(TAG, "showWindow: updating UI");
848            updateFullscreenMode();
849            updateInputViewShown();
850        }
851
852        if (!mWindowAdded || !mWindowCreated) {
853            mWindowAdded = true;
854            mWindowCreated = true;
855            View v = onCreateCandidatesView();
856            if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
857            if (v != null) {
858                setCandidatesView(v);
859            }
860        }
861        if (doShowInput) {
862            if (mInputStarted) {
863                if (DEBUG) Log.v(TAG, "showWindow: starting input view");
864                onStartInputView(mInputEditorInfo, false);
865            }
866            startExtractingText();
867        }
868
869        if (!wasVisible) {
870            if (DEBUG) Log.v(TAG, "showWindow: showing!");
871            mWindow.show();
872            if (mInputConnection != null) {
873                mInputConnection.showStatusIcon(getPackageName(), mStatusIcon);
874            }
875        }
876    }
877
878    public void hideWindow() {
879        if (mWindowVisible) {
880            mWindow.hide();
881            mWindowVisible = false;
882            if (mInputConnection != null) {
883                mInputConnection.hideStatusIcon();
884            }
885        }
886    }
887
888    public void onBindInput() {
889    }
890
891    public void onStartInput(EditorInfo attribute, boolean restarting) {
892    }
893
894    void doStartInput(EditorInfo attribute, boolean restarting) {
895        mInputStarted = true;
896        mInputEditorInfo = attribute;
897        onStartInput(attribute, restarting);
898        if (mWindowVisible) {
899            if (mWindowCreated) {
900                onStartInputView(mInputEditorInfo, restarting);
901            }
902            startExtractingText();
903        }
904    }
905
906    public void onFinishInput() {
907    }
908
909    /**
910     * Called when the application has reported auto-completion candidates that
911     * it would like to have the input method displayed.  Typically these are
912     * only used when an input method is running in full-screen mode, since
913     * otherwise the user can see and interact with the pop-up window of
914     * completions shown by the application.
915     *
916     * <p>The default implementation here does nothing.
917     */
918    public void onDisplayCompletions(CompletionInfo[] completions) {
919    }
920
921    /**
922     * Called when the application has reported new extracted text to be shown
923     * due to changes in its current text state.  The default implementation
924     * here places the new text in the extract edit text, when the input
925     * method is running in fullscreen mode.
926     */
927    public void onUpdateExtractedText(int token, ExtractedText text) {
928        if (mExtractedToken != token) {
929            return;
930        }
931        if (mExtractEditText != null && text != null) {
932            mExtractedText = text;
933            mExtractEditText.setExtractedText(text);
934        }
935    }
936
937    /**
938     * Called when the application has reported a new selection region of
939     * the text.  This is called whether or not the input method has requested
940     * extracted text updates, although if so it will not receive this call
941     * if the extracted text has changed as well.
942     *
943     * <p>The default implementation takes care of updating the cursor in
944     * the extract text, if it is being shown.
945     */
946    public void onUpdateSelection(int oldSelStart, int oldSelEnd,
947            int newSelStart, int newSelEnd,
948            int candidatesStart, int candidatesEnd) {
949        if (mExtractEditText != null && mExtractedText != null) {
950            final int off = mExtractedText.startOffset;
951            mExtractEditText.setSelection(newSelStart-off, newSelEnd-off);
952        }
953    }
954
955    /**
956     * Called when the application has reported a new location of its text
957     * cursor.  This is only called if explicitly requested by the input method.
958     * The default implementation does nothing.
959     */
960    public void onUpdateCursor(Rect newCursor) {
961    }
962
963    /**
964     * Close this input method's soft input area, removing it from the display.
965     * The input method will continue running, but the user can no longer use
966     * it to generate input by touching the screen.
967     * @param flags Provides additional operating flags.  Currently may be
968     * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
969     * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
970     */
971    public void dismissSoftInput(int flags) {
972        ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE))
973                .hideSoftInputFromInputMethod(mToken, flags);
974    }
975
976    public boolean onKeyDown(int keyCode, KeyEvent event) {
977        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
978                && event.getRepeatCount() == 0) {
979            if (mShowInputRequested) {
980                // If the soft input area is shown, back closes it and we
981                // consume the back key.
982                dismissSoftInput(0);
983                return true;
984            }
985            if (mShowCandidatesRequested) {
986                // If the candidates are shown, we just want to make sure
987                // they are now hidden but otherwise let the app execute
988                // the back.
989                // XXX this needs better interaction with the soft input
990                // implementation.
991                //setCandidatesViewShown(false);
992            }
993        }
994        return false;
995    }
996
997    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
998        return false;
999    }
1000
1001    public boolean onKeyUp(int keyCode, KeyEvent event) {
1002        return false;
1003    }
1004
1005    public boolean onTrackballEvent(MotionEvent event) {
1006        return false;
1007    }
1008
1009    public void onAppPrivateCommand(String action, Bundle data) {
1010    }
1011
1012    void startExtractingText() {
1013        if (mExtractEditText != null && getCurrentInputStarted()
1014                && isFullscreenMode()) {
1015            mExtractedToken++;
1016            ExtractedTextRequest req = new ExtractedTextRequest();
1017            req.token = mExtractedToken;
1018            req.hintMaxLines = 10;
1019            req.hintMaxChars = 10000;
1020            mExtractedText = mInputConnection.getExtractedText(req,
1021                    InputConnection.EXTRACTED_TEXT_MONITOR);
1022            if (mExtractedText != null) {
1023                mExtractEditText.setExtractedText(mExtractedText);
1024            }
1025            mExtractEditText.setInputType(getCurrentInputEditorInfo().inputType);
1026            mExtractEditText.setHint(mInputEditorInfo.hintText);
1027        }
1028    }
1029}
1030