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