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