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