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