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