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