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