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