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