2 * Copyright (C) 2007-2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package android.inputmethodservice;
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
22 import android.annotation.CallSuper;
23 import android.annotation.DrawableRes;
24 import android.annotation.IntDef;
25 import android.annotation.MainThread;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.app.ActivityManager;
29 import android.app.Dialog;
30 import android.content.Context;
31 import android.content.res.Configuration;
32 import android.content.res.Resources;
33 import android.content.res.TypedArray;
34 import android.database.ContentObserver;
35 import android.graphics.Rect;
36 import android.graphics.Region;
37 import android.net.Uri;
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.IBinder;
41 import android.os.ResultReceiver;
42 import android.os.SystemClock;
43 import android.provider.Settings;
44 import android.text.InputType;
45 import android.text.Layout;
46 import android.text.Spannable;
47 import android.text.method.MovementMethod;
48 import android.util.Log;
49 import android.util.PrintWriterPrinter;
50 import android.util.Printer;
51 import android.view.Gravity;
52 import android.view.KeyCharacterMap;
53 import android.view.KeyEvent;
54 import android.view.LayoutInflater;
55 import android.view.MotionEvent;
56 import android.view.View;
57 import android.view.ViewGroup;
58 import android.view.ViewTreeObserver;
59 import android.view.Window;
60 import android.view.WindowManager;
61 import android.view.WindowManager.BadTokenException;
62 import android.view.animation.AnimationUtils;
63 import android.view.inputmethod.CompletionInfo;
64 import android.view.inputmethod.CursorAnchorInfo;
65 import android.view.inputmethod.EditorInfo;
66 import android.view.inputmethod.ExtractedText;
67 import android.view.inputmethod.ExtractedTextRequest;
68 import android.view.inputmethod.InputBinding;
69 import android.view.inputmethod.InputConnection;
70 import android.view.inputmethod.InputContentInfo;
71 import android.view.inputmethod.InputMethod;
72 import android.view.inputmethod.InputMethodManager;
73 import android.view.inputmethod.InputMethodSubtype;
74 import android.widget.FrameLayout;
75 import android.widget.ImageButton;
76 import android.widget.LinearLayout;
77 import android.widget.TextView;
79 import java.io.FileDescriptor;
80 import java.io.PrintWriter;
81 import java.lang.annotation.Retention;
82 import java.lang.annotation.RetentionPolicy;
85 * InputMethodService provides a standard implementation of an InputMethod,
86 * which final implementations can derive from and customize. See the
87 * base class {@link AbstractInputMethodService} and the {@link InputMethod}
88 * interface for more information on the basics of writing input methods.
90 * <p>In addition to the normal Service lifecycle methods, this class
91 * introduces some new specific callbacks that most subclasses will want
94 * <li> {@link #onInitializeInterface()} for user-interface initialization,
95 * in particular to deal with configuration changes while the service is
97 * <li> {@link #onBindInput} to find out about switching to a new client.
98 * <li> {@link #onStartInput} to deal with an input session starting with
100 * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
101 * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
102 * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
103 * starting within the input area of the IME.
106 * <p>An input method has significant discretion in how it goes about its
107 * work: the {@link android.inputmethodservice.InputMethodService} provides
108 * a basic framework for standard UI elements (input view, candidates view,
109 * and running in fullscreen mode), but it is up to a particular implementor
110 * to decide how to use them. For example, one input method could implement
111 * an input area with a keyboard, another could allow the user to draw text,
112 * while a third could have no input area (and thus not be visible to the
113 * user) but instead listen to audio and perform text to speech conversion.</p>
115 * <p>In the implementation provided here, all of these elements are placed
116 * together in a single window managed by the InputMethodService. It will
117 * execute callbacks as it needs information about them, and provides APIs for
118 * programmatic control over them. They layout of these elements is explicitly
122 * <li>The soft input view, if available, is placed at the bottom of the
124 * <li>The candidates view, if currently shown, is placed above the soft
126 * <li>If not running fullscreen, the application is moved or resized to be
127 * above these views; if running fullscreen, the window will completely cover
128 * the application and its top part will contain the extract text of what is
129 * currently being edited by the application.
133 * <a name="SoftInputView"></a>
134 * <h3>Soft Input View</h3>
136 * <p>Central to most input methods is the soft input view. This is where most
137 * user interaction occurs: pressing on soft keys, drawing characters, or
138 * however else your input method wants to generate text. Most implementations
139 * will simply have their own view doing all of this work, and return a new
140 * instance of it when {@link #onCreateInputView()} is called. At that point,
141 * as long as the input view is visible, you will see user interaction in
142 * that view and can call back on the InputMethodService to interact with the
143 * application as appropriate.</p>
145 * <p>There are some situations where you want to decide whether or not your
146 * soft input view should be shown to the user. This is done by implementing
147 * the {@link #onEvaluateInputViewShown()} to return true or false based on
148 * whether it should be shown in the current environment. If any of your
149 * state has changed that may impact this, call
150 * {@link #updateInputViewShown()} to have it re-evaluated. The default
151 * implementation always shows the input view unless there is a hard
152 * keyboard available, which is the appropriate behavior for most input
156 * <a name="CandidatesView"></a>
157 * <h3>Candidates View</h3>
159 * <p>Often while the user is generating raw text, an input method wants to
160 * provide them with a list of possible interpretations of that text that can
161 * be selected for use. This is accomplished with the candidates view, and
162 * like the soft input view you implement {@link #onCreateCandidatesView()}
163 * to instantiate your own view implementing your candidates UI.</p>
165 * <p>Management of the candidates view is a little different than the input
166 * view, because the candidates view tends to be more transient, being shown
167 * only when there are possible candidates for the current text being entered
168 * by the user. To control whether the candidates view is shown, you use
169 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate
170 * view tends to be shown and hidden a lot, it does not impact the application
171 * UI in the same way as the soft input view: it will never cause application
172 * windows to resize, only cause them to be panned if needed for the user to
173 * see the current focus.</p>
176 * <a name="FullscreenMode"></a>
177 * <h3>Fullscreen Mode</h3>
179 * <p>Sometimes your input method UI is too large to integrate with the
180 * application UI, so you just want to take over the screen. This is
181 * accomplished by switching to full-screen mode, causing the input method
182 * window to fill the entire screen and add its own "extracted text" editor
183 * showing the user the text that is being typed. Unlike the other UI elements,
184 * there is a standard implementation for the extract editor that you should
185 * not need to change. The editor is placed at the top of the IME, above the
186 * input and candidates views.</p>
188 * <p>Similar to the input view, you control whether the IME is running in
189 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
190 * to return true or false based on
191 * whether it should be fullscreen in the current environment. If any of your
192 * state has changed that may impact this, call
193 * {@link #updateFullscreenMode()} to have it re-evaluated. The default
194 * implementation selects fullscreen mode when the screen is in a landscape
195 * orientation, which is appropriate behavior for most input methods that have
196 * a significant input area.</p>
198 * <p>When in fullscreen mode, you have some special requirements because the
199 * user can not see the application UI. In particular, you should implement
200 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
201 * generated by your application, typically in your candidates view like you
202 * would normally show candidates.
205 * <a name="GeneratingText"></a>
206 * <h3>Generating Text</h3>
208 * <p>The key part of an IME is of course generating text for the application.
209 * This is done through calls to the
210 * {@link android.view.inputmethod.InputConnection} interface to the
211 * application, which can be retrieved from {@link #getCurrentInputConnection()}.
212 * This interface allows you to generate raw key events or, if the target
213 * supports it, directly edit in strings of candidates and committed text.</p>
215 * <p>Information about what the target is expected and supports can be found
216 * through the {@link android.view.inputmethod.EditorInfo} class, which is
217 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most
218 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
219 * EditorInfo.inputType}; in particular, if this is
220 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
221 * then the target does not support complex edits and you need to only deliver
222 * raw key events to it. An input method will also want to look at other
223 * values here, to for example detect password mode, auto complete text views,
224 * phone number entry, etc.</p>
226 * <p>When the user switches between input targets, you will receive calls to
227 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
228 * You can use these to reset and initialize your input state for the current
229 * target. For example, you will often want to clear any input state, and
230 * update a soft keyboard to be appropriate for the new inputType.</p>
232 * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
233 * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
234 * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
236 public class InputMethodService extends AbstractInputMethodService {
237 static final String TAG = "InputMethodService";
238 static final boolean DEBUG = false;
241 * The back button will close the input window.
243 public static final int BACK_DISPOSITION_DEFAULT = 0; // based on window
246 * This input method will not consume the back key.
248 public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // back
251 * This input method will consume the back key.
253 public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // down
257 * The IME is active. It may or may not be visible.
259 public static final int IME_ACTIVE = 0x1;
263 * The IME is visible.
265 public static final int IME_VISIBLE = 0x2;
267 InputMethodManager mImm;
271 LayoutInflater mInflater;
272 TypedArray mThemeAttrs;
274 SoftInputWindow mWindow;
275 boolean mInitialized;
276 boolean mWindowCreated;
277 boolean mWindowAdded;
278 boolean mWindowVisible;
279 boolean mWindowWasVisible;
280 boolean mInShowWindow;
281 ViewGroup mFullscreenArea;
282 FrameLayout mExtractFrame;
283 FrameLayout mCandidatesFrame;
284 FrameLayout mInputFrame;
288 InputBinding mInputBinding;
289 InputConnection mInputConnection;
290 boolean mInputStarted;
291 boolean mInputViewStarted;
292 boolean mCandidatesViewStarted;
293 InputConnection mStartedInputConnection;
294 EditorInfo mInputEditorInfo;
297 * A token to keep tracking the last IPC that triggered
298 * {@link #doStartInput(InputConnection, EditorInfo, boolean)}. If
299 * {@link #doStartInput(InputConnection, EditorInfo, boolean)} was not caused by IPCs from
300 * {@link com.android.server.InputMethodManagerService}, this needs to remain unchanged.
302 * <p>Some IPCs to {@link com.android.server.InputMethodManagerService} require this token to
303 * disentangle event flows for various purposes such as better window animation and providing
304 * fine-grained debugging information.</p>
307 private IBinder mStartInputToken;
310 boolean mShowInputRequested;
311 boolean mLastShowInputRequested;
312 int mCandidatesVisibility;
313 CompletionInfo[] mCurCompletions;
315 boolean mFullscreenApplied;
316 boolean mIsFullscreen;
318 boolean mExtractViewHidden;
319 ExtractEditText mExtractEditText;
320 ViewGroup mExtractAccessories;
322 ExtractedText mExtractedText;
326 boolean mIsInputViewShown;
329 int mBackDisposition;
332 * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
333 * have not shown our own window yet. In this situation, the previous inset continues to be
334 * shown as an empty region until it is explicitly updated. Basically we can trigger the update
335 * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}.
337 boolean mShouldClearInsetOfPreviousIme;
339 final Insets mTmpInsets = new Insets();
340 final int[] mTmpLocation = new int[2];
342 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
343 new ViewTreeObserver.OnComputeInternalInsetsListener() {
344 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
345 if (isExtractViewShown()) {
346 // In true fullscreen mode, we just say the window isn't covering
347 // any content so we don't impact whatever is behind.
348 View decor = getWindow().getWindow().getDecorView();
349 info.contentInsets.top = info.visibleInsets.top
351 info.touchableRegion.setEmpty();
352 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
354 onComputeInsets(mTmpInsets);
355 info.contentInsets.top = mTmpInsets.contentTopInsets;
356 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
357 info.touchableRegion.set(mTmpInsets.touchableRegion);
358 info.setTouchableInsets(mTmpInsets.touchableInsets);
363 final View.OnClickListener mActionClickListener = new View.OnClickListener() {
364 public void onClick(View v) {
365 final EditorInfo ei = getCurrentInputEditorInfo();
366 final InputConnection ic = getCurrentInputConnection();
367 if (ei != null && ic != null) {
368 if (ei.actionId != 0) {
369 ic.performEditorAction(ei.actionId);
370 } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION)
371 != EditorInfo.IME_ACTION_NONE) {
372 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
379 * Concrete implementation of
380 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
381 * all of the standard behavior for an input method.
383 public class InputMethodImpl extends AbstractInputMethodImpl {
385 * Take care of attaching the given window token provided by the system.
387 public void attachToken(IBinder token) {
388 if (mToken == null) {
390 mWindow.setToken(token);
395 * Handle a new input binding, calling
396 * {@link InputMethodService#onBindInput InputMethodService.onBindInput()}
399 public void bindInput(InputBinding binding) {
400 mInputBinding = binding;
401 mInputConnection = binding.getConnection();
402 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
403 + " ic=" + mInputConnection);
404 if (mImm != null && mToken != null) {
405 mImm.reportFullscreenMode(mToken, mIsFullscreen);
412 * Clear the current input binding.
414 public void unbindInput() {
415 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
416 + " ic=" + mInputConnection);
418 mInputBinding = null;
419 mInputConnection = null;
422 public void startInput(InputConnection ic, EditorInfo attribute) {
423 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
424 doStartInput(ic, attribute, false);
427 public void restartInput(InputConnection ic, EditorInfo attribute) {
428 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
429 doStartInput(ic, attribute, true);
437 public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
438 @NonNull EditorInfo editorInfo, boolean restarting,
439 @NonNull IBinder startInputToken) {
440 mStartInputToken = startInputToken;
442 // This needs to be dispatched to interface methods rather than doStartInput().
443 // Otherwise IME developers who have overridden those interface methods will lose
445 super.dispatchStartInputWithToken(inputConnection, editorInfo, restarting,
450 * Handle a request by the system to hide the soft input area.
452 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
453 if (DEBUG) Log.v(TAG, "hideSoftInput()");
454 boolean wasVis = isInputViewShown();
456 mShowInputRequested = false;
458 clearInsetOfPreviousIme();
459 if (resultReceiver != null) {
460 resultReceiver.send(wasVis != isInputViewShown()
461 ? InputMethodManager.RESULT_HIDDEN
462 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
463 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
468 * Handle a request by the system to show the soft input area.
470 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
471 if (DEBUG) Log.v(TAG, "showSoftInput()");
472 boolean wasVis = isInputViewShown();
473 if (dispatchOnShowInputRequested(flags, false)) {
476 } catch (BadTokenException e) {
477 // We have ignored BadTokenException here since Jelly Bean MR-2 (API Level 18).
478 // We could ignore BadTokenException in InputMethodService#showWindow() instead,
479 // but it may break assumptions for those who override #showWindow() that we can
480 // detect errors in #showWindow() by checking BadTokenException.
481 // TODO: Investigate its feasibility. Update JavaDoc of #showWindow() of
482 // whether it's OK to override #showWindow() or not.
485 clearInsetOfPreviousIme();
486 // If user uses hard keyboard, IME button should always be shown.
487 boolean showing = isInputViewShown();
488 mImm.setImeWindowStatus(mToken, mStartInputToken,
489 IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
490 if (resultReceiver != null) {
491 resultReceiver.send(wasVis != isInputViewShown()
492 ? InputMethodManager.RESULT_SHOWN
493 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
494 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
498 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
499 onCurrentInputMethodSubtypeChanged(subtype);
504 * Concrete implementation of
505 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
506 * all of the standard behavior for an input method session.
508 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
509 public void finishInput() {
513 if (DEBUG) Log.v(TAG, "finishInput() in " + this);
518 * Call {@link InputMethodService#onDisplayCompletions
519 * InputMethodService.onDisplayCompletions()}.
521 public void displayCompletions(CompletionInfo[] completions) {
525 mCurCompletions = completions;
526 onDisplayCompletions(completions);
530 * Call {@link InputMethodService#onUpdateExtractedText
531 * InputMethodService.onUpdateExtractedText()}.
533 public void updateExtractedText(int token, ExtractedText text) {
537 onUpdateExtractedText(token, text);
541 * Call {@link InputMethodService#onUpdateSelection
542 * InputMethodService.onUpdateSelection()}.
544 public void updateSelection(int oldSelStart, int oldSelEnd,
545 int newSelStart, int newSelEnd,
546 int candidatesStart, int candidatesEnd) {
550 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
551 newSelStart, newSelEnd, candidatesStart, candidatesEnd);
555 public void viewClicked(boolean focusChanged) {
559 InputMethodService.this.onViewClicked(focusChanged);
563 * Call {@link InputMethodService#onUpdateCursor
564 * InputMethodService.onUpdateCursor()}.
566 public void updateCursor(Rect newCursor) {
570 InputMethodService.this.onUpdateCursor(newCursor);
574 * Call {@link InputMethodService#onAppPrivateCommand
575 * InputMethodService.onAppPrivateCommand()}.
577 public void appPrivateCommand(String action, Bundle data) {
581 InputMethodService.this.onAppPrivateCommand(action, data);
587 public void toggleSoftInput(int showFlags, int hideFlags) {
588 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
592 * Call {@link InputMethodService#onUpdateCursorAnchorInfo
593 * InputMethodService.onUpdateCursorAnchorInfo()}.
595 public void updateCursorAnchorInfo(CursorAnchorInfo info) {
599 InputMethodService.this.onUpdateCursorAnchorInfo(info);
604 * Information about where interesting parts of the input method UI appear.
606 public static final class Insets {
608 * This is the top part of the UI that is the main content. It is
609 * used to determine the basic space needed, to resize/pan the
610 * application behind. It is assumed that this inset does not
611 * change very much, since any change will cause a full resize/pan
612 * of the application behind. This value is relative to the top edge
613 * of the input method window.
615 public int contentTopInsets;
618 * This is the top part of the UI that is visibly covering the
619 * application behind it. This provides finer-grained control over
620 * visibility, allowing you to change it relatively frequently (such
621 * as hiding or showing candidates) without disrupting the underlying
622 * UI too much. For example, this will never resize the application
623 * UI, will only pan if needed to make the current focus visible, and
624 * will not aggressively move the pan position when this changes unless
625 * needed to make the focus visible. This value is relative to the top edge
626 * of the input method window.
628 public int visibleTopInsets;
631 * This is the region of the UI that is touchable. It is used when
632 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
633 * The region should be specified relative to the origin of the window frame.
635 public final Region touchableRegion = new Region();
638 * Option for {@link #touchableInsets}: the entire window frame
641 public static final int TOUCHABLE_INSETS_FRAME
642 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
645 * Option for {@link #touchableInsets}: the area inside of
646 * the content insets can be touched.
648 public static final int TOUCHABLE_INSETS_CONTENT
649 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
652 * Option for {@link #touchableInsets}: the area inside of
653 * the visible insets can be touched.
655 public static final int TOUCHABLE_INSETS_VISIBLE
656 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
659 * Option for {@link #touchableInsets}: the region specified by
660 * {@link #touchableRegion} can be touched.
662 public static final int TOUCHABLE_INSETS_REGION
663 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
666 * Determine which area of the window is touchable by the user. May
667 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
668 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
669 * or {@link #TOUCHABLE_INSETS_REGION}.
671 public int touchableInsets;
675 * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
677 * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
678 * Basically this functionality still needs to be considered as implementation details.</p>
681 private static final class SettingsObserver extends ContentObserver {
682 @Retention(RetentionPolicy.SOURCE)
684 ShowImeWithHardKeyboardType.UNKNOWN,
685 ShowImeWithHardKeyboardType.FALSE,
686 ShowImeWithHardKeyboardType.TRUE,
688 private @interface ShowImeWithHardKeyboardType {
693 @ShowImeWithHardKeyboardType
694 private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
696 private final InputMethodService mService;
698 private SettingsObserver(InputMethodService service) {
699 super(new Handler(service.getMainLooper()));
704 * A factory method that internally enforces two-phase initialization to make sure that the
705 * object reference will not be escaped until the object is properly constructed.
707 * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread. Hence
708 * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
710 * @param service {@link InputMethodService} that needs to receive the callback.
711 * @return {@link SettingsObserver} that is already registered to
712 * {@link android.content.ContentResolver}. The caller must call
713 * {@link SettingsObserver#unregister()}.
715 public static SettingsObserver createAndRegister(InputMethodService service) {
716 final SettingsObserver observer = new SettingsObserver(service);
717 // The observer is properly constructed. Let's start accepting the event.
718 service.getContentResolver().registerContentObserver(
719 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
725 mService.getContentResolver().unregisterContentObserver(this);
728 private boolean shouldShowImeWithHardKeyboard() {
729 // Lazily initialize as needed.
730 if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
731 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
732 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
733 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
735 switch (mShowImeWithHardKeyboard) {
736 case ShowImeWithHardKeyboardType.TRUE:
738 case ShowImeWithHardKeyboardType.FALSE:
741 Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
747 public void onChange(boolean selfChange, Uri uri) {
748 final Uri showImeWithHardKeyboardUri =
749 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
750 if (showImeWithHardKeyboardUri.equals(uri)) {
751 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
752 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
753 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
754 // In Android M and prior, state change of
755 // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered
756 // #onConfigurationChanged(). For compatibility reasons, we reset the internal
757 // state as if configuration was changed.
758 mService.resetStateForNewConfiguration();
763 public String toString() {
764 return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}";
767 private SettingsObserver mSettingsObserver;
770 * You can call this to customize the theme used by your IME's window.
771 * This theme should typically be one that derives from
772 * {@link android.R.style#Theme_InputMethod}, which is the default theme
773 * you will get. This must be set before {@link #onCreate}, so you
774 * will typically call it in your constructor with the resource ID
775 * of your custom theme.
778 public void setTheme(int theme) {
779 if (mWindow != null) {
780 throw new IllegalStateException("Must be called before onCreate()");
786 * You can call this to try to enable accelerated drawing for your IME. This must be set before
787 * {@link #onCreate()}, so you will typically call it in your constructor. It is not always
788 * possible to use hardware accelerated drawing in an IME (for example on low-end devices that
789 * do not have the resources to support this), so the call {@code true} if it succeeds otherwise
790 * {@code false} if you will need to draw in software. You must be able to handle either case.
792 * <p>In API 21 and later, system may automatically enable hardware accelerated drawing for your
793 * IME on capable devices even if this method is not explicitly called. Make sure that your IME
794 * is able to handle either case.</p>
796 * @return {@code true} if accelerated drawing is successfully enabled otherwise {@code false}.
797 * On API 21 and later devices the return value is basically just a hint and your IME
798 * does not need to change the behavior based on the it
799 * @deprecated Starting in API 21, hardware acceleration is always enabled on capable devices
802 public boolean enableHardwareAcceleration() {
803 if (mWindow != null) {
804 throw new IllegalStateException("Must be called before onCreate()");
806 return ActivityManager.isHighEndGfx();
809 @Override public void onCreate() {
810 mTheme = Resources.selectSystemTheme(mTheme,
811 getApplicationInfo().targetSdkVersion,
812 android.R.style.Theme_InputMethod,
813 android.R.style.Theme_Holo_InputMethod,
814 android.R.style.Theme_DeviceDefault_InputMethod,
815 android.R.style.Theme_DeviceDefault_InputMethod);
816 super.setTheme(mTheme);
818 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
819 mSettingsObserver = SettingsObserver.createAndRegister(this);
820 // If the previous IME has occupied non-empty inset in the screen, we need to decide whether
821 // we continue to use the same size of the inset or update it
822 mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
823 mInflater = (LayoutInflater)getSystemService(
824 Context.LAYOUT_INFLATER_SERVICE);
825 mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
826 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
828 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
832 * This is a hook that subclasses can use to perform initialization of
833 * their interface. It is called for you prior to any of your UI objects
834 * being created, both after the service is first created and after a
835 * configuration change happens.
837 public void onInitializeInterface() {
838 // Intentionally empty
844 onInitializeInterface();
849 mInitialized = false;
850 mWindowCreated = false;
851 mShowInputRequested = false;
854 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
855 mRootView = mInflater.inflate(
856 com.android.internal.R.layout.input_method, null);
857 mRootView.setSystemUiVisibility(
858 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
859 mWindow.setContentView(mRootView);
860 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
861 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
862 if (Settings.Global.getInt(getContentResolver(),
863 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
864 mWindow.getWindow().setWindowAnimations(
865 com.android.internal.R.style.Animation_InputMethodFancy);
867 mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
868 mExtractViewHidden = false;
869 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
871 mExtractEditText = null;
872 mExtractAccessories = null;
873 mExtractAction = null;
874 mFullscreenApplied = false;
876 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
877 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
879 mIsInputViewShown = false;
881 mExtractFrame.setVisibility(View.GONE);
882 mCandidatesVisibility = getCandidatesHiddenVisibility();
883 mCandidatesFrame.setVisibility(mCandidatesVisibility);
884 mInputFrame.setVisibility(View.GONE);
887 @Override public void onDestroy() {
889 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
893 // Disable exit animation for the current IME window
894 // to avoid the race condition between the exit and enter animations
895 // when the current IME is being switched to another one.
896 mWindow.getWindow().setWindowAnimations(0);
899 if (mSettingsObserver != null) {
900 mSettingsObserver.unregister();
901 mSettingsObserver = null;
906 * Take care of handling configuration changes. Subclasses of
907 * InputMethodService generally don't need to deal directly with
908 * this on their own; the standard implementation here takes care of
909 * regenerating the input method UI as a result of the configuration
910 * change, so you can rely on your {@link #onCreateInputView} and
911 * other methods being called as appropriate due to a configuration change.
913 * <p>When a configuration change does happen,
914 * {@link #onInitializeInterface()} is guaranteed to be called the next
915 * time prior to any of the other input or UI creation callbacks. The
916 * following will be called immediately depending if appropriate for current
917 * state: {@link #onStartInput} if input is active, and
918 * {@link #onCreateInputView} and {@link #onStartInputView} and related
919 * appropriate functions if the UI is displayed.
921 @Override public void onConfigurationChanged(Configuration newConfig) {
922 super.onConfigurationChanged(newConfig);
923 resetStateForNewConfiguration();
926 private void resetStateForNewConfiguration() {
927 boolean visible = mWindowVisible;
928 int showFlags = mShowInputFlags;
929 boolean showingInput = mShowInputRequested;
930 CompletionInfo[] completions = mCurCompletions;
932 mInputViewStarted = false;
933 mCandidatesViewStarted = false;
935 doStartInput(getCurrentInputConnection(),
936 getCurrentInputEditorInfo(), true);
940 // If we were last showing the soft keyboard, try to do so again.
941 if (dispatchOnShowInputRequested(showFlags, true)) {
943 if (completions != null) {
944 mCurCompletions = completions;
945 onDisplayCompletions(completions);
950 } else if (mCandidatesVisibility == View.VISIBLE) {
951 // If the candidates are currently visible, make sure the
952 // window is shown for them.
955 // Otherwise hide the window.
958 // If user uses hard keyboard, IME button should always be shown.
959 boolean showing = onEvaluateInputViewShown();
960 mImm.setImeWindowStatus(mToken, mStartInputToken,
961 IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
966 * Implement to return our standard {@link InputMethodImpl}. Subclasses
967 * can override to provide their own customized version.
970 public AbstractInputMethodImpl onCreateInputMethodInterface() {
971 return new InputMethodImpl();
975 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
976 * can override to provide their own customized version.
979 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
980 return new InputMethodSessionImpl();
983 public LayoutInflater getLayoutInflater() {
987 public Dialog getWindow() {
991 public void setBackDisposition(int disposition) {
992 mBackDisposition = disposition;
995 public int getBackDisposition() {
996 return mBackDisposition;
1000 * Return the maximum width, in pixels, available the input method.
1001 * Input methods are positioned at the bottom of the screen and, unless
1002 * running in fullscreen, will generally want to be as short as possible
1003 * so should compute their height based on their contents. However, they
1004 * can stretch as much as needed horizontally. The function returns to
1005 * you the maximum amount of space available horizontally, which you can
1006 * use if needed for UI placement.
1008 * <p>In many cases this is not needed, you can just rely on the normal
1009 * view layout mechanisms to position your views within the full horizontal
1010 * space given to the input method.
1012 * <p>Note that this value can change dynamically, in particular when the
1013 * screen orientation changes.
1015 public int getMaxWidth() {
1016 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
1017 return wm.getDefaultDisplay().getWidth();
1021 * Return the currently active InputBinding for the input method, or
1022 * null if there is none.
1024 public InputBinding getCurrentInputBinding() {
1025 return mInputBinding;
1029 * Retrieve the currently active InputConnection that is bound to
1030 * the input method, or null if there is none.
1032 public InputConnection getCurrentInputConnection() {
1033 InputConnection ic = mStartedInputConnection;
1037 return mInputConnection;
1040 public boolean getCurrentInputStarted() {
1041 return mInputStarted;
1044 public EditorInfo getCurrentInputEditorInfo() {
1045 return mInputEditorInfo;
1049 * Re-evaluate whether the input method should be running in fullscreen
1050 * mode, and update its UI if this has changed since the last time it
1051 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to
1052 * determine whether it should currently run in fullscreen mode. You
1053 * can use {@link #isFullscreenMode()} to determine if the input method
1054 * is currently running in fullscreen mode.
1056 public void updateFullscreenMode() {
1057 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
1058 boolean changed = mLastShowInputRequested != mShowInputRequested;
1059 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
1061 mIsFullscreen = isFullscreen;
1062 if (mImm != null && mToken != null) {
1063 mImm.reportFullscreenMode(mToken, mIsFullscreen);
1065 mFullscreenApplied = true;
1067 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1068 mFullscreenArea.getLayoutParams();
1070 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
1071 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
1075 mFullscreenArea.setBackgroundDrawable(null);
1076 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
1079 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
1080 mFullscreenArea, lp);
1082 if (mExtractView == null) {
1083 View v = onCreateExtractTextView();
1088 startExtractingText(false);
1090 updateExtractFrameVisibility();
1094 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
1095 mLastShowInputRequested = mShowInputRequested;
1100 * Update the given window's parameters for the given mode. This is called
1101 * when the window is first displayed and each time the fullscreen or
1102 * candidates only mode changes.
1104 * <p>The default implementation makes the layout for the window
1105 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
1106 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
1108 * @param win The input method's window.
1109 * @param isFullscreen If true, the window is running in fullscreen mode
1110 * and intended to cover the entire application display.
1111 * @param isCandidatesOnly If true, the window is only showing the
1112 * candidates view and none of the rest of its UI. This is mutually
1113 * exclusive with fullscreen mode.
1115 public void onConfigureWindow(Window win, boolean isFullscreen,
1116 boolean isCandidatesOnly) {
1117 final int currentHeight = mWindow.getWindow().getAttributes().height;
1118 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
1119 if (mIsInputViewShown && currentHeight != newHeight) {
1121 Log.w(TAG,"Window size has been changed. This may cause jankiness of resizing "
1122 + "window: " + currentHeight + " -> " + newHeight);
1125 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
1129 * Return whether the input method is <em>currently</em> running in
1130 * fullscreen mode. This is the mode that was last determined and
1131 * applied by {@link #updateFullscreenMode()}.
1133 public boolean isFullscreenMode() {
1134 return mIsFullscreen;
1138 * Override this to control when the input method should run in
1139 * fullscreen mode. The default implementation runs in fullsceen only
1140 * when the screen is in landscape mode. If you change what
1141 * this returns, you will need to call {@link #updateFullscreenMode()}
1142 * yourself whenever the returned value may have changed to have it
1143 * re-evaluated and applied.
1145 public boolean onEvaluateFullscreenMode() {
1146 Configuration config = getResources().getConfiguration();
1147 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
1150 if (mInputEditorInfo != null
1151 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
1158 * Controls the visibility of the extracted text area. This only applies
1159 * when the input method is in fullscreen mode, and thus showing extracted
1160 * text. When false, the extracted text will not be shown, allowing some
1161 * of the application to be seen behind. This is normally set for you
1162 * by {@link #onUpdateExtractingVisibility}. This controls the visibility
1163 * of both the extracted text and candidate view; the latter since it is
1164 * not useful if there is no text to see.
1166 public void setExtractViewShown(boolean shown) {
1167 if (mExtractViewHidden == shown) {
1168 mExtractViewHidden = !shown;
1169 updateExtractFrameVisibility();
1174 * Return whether the fullscreen extract view is shown. This will only
1175 * return true if {@link #isFullscreenMode()} returns true, and in that
1176 * case its value depends on the last call to
1177 * {@link #setExtractViewShown(boolean)}. This effectively lets you
1178 * determine if the application window is entirely covered (when this
1179 * returns true) or if some part of it may be shown (if this returns
1180 * false, though if {@link #isFullscreenMode()} returns true in that case
1181 * then it is probably only a sliver of the application).
1183 public boolean isExtractViewShown() {
1184 return mIsFullscreen && !mExtractViewHidden;
1187 void updateExtractFrameVisibility() {
1189 if (isFullscreenMode()) {
1190 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
1191 // "vis" should be applied for the extract frame as well in the fullscreen mode.
1192 mExtractFrame.setVisibility(vis);
1195 mExtractFrame.setVisibility(View.GONE);
1197 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
1198 if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
1199 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1200 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1201 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1204 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1208 mFullscreenArea.setVisibility(vis);
1212 * Compute the interesting insets into your UI. The default implementation
1213 * uses the top of the candidates frame for the visible insets, and the
1214 * top of the input frame for the content insets. The default touchable
1215 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1217 * <p>Note that this method is not called when
1218 * {@link #isExtractViewShown} returns true, since
1219 * in that case the application is left as-is behind the input method and
1220 * not impacted by anything in its UI.
1222 * @param outInsets Fill in with the current UI insets.
1224 public void onComputeInsets(Insets outInsets) {
1225 int[] loc = mTmpLocation;
1226 if (mInputFrame.getVisibility() == View.VISIBLE) {
1227 mInputFrame.getLocationInWindow(loc);
1229 View decor = getWindow().getWindow().getDecorView();
1230 loc[1] = decor.getHeight();
1232 if (isFullscreenMode()) {
1233 // In fullscreen mode, we never resize the underlying window.
1234 View decor = getWindow().getWindow().getDecorView();
1235 outInsets.contentTopInsets = decor.getHeight();
1237 outInsets.contentTopInsets = loc[1];
1239 if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1240 mCandidatesFrame.getLocationInWindow(loc);
1242 outInsets.visibleTopInsets = loc[1];
1243 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
1244 outInsets.touchableRegion.setEmpty();
1248 * Re-evaluate whether the soft input area should currently be shown, and
1249 * update its UI if this has changed since the last time it
1250 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to
1251 * determine whether the input view should currently be shown. You
1252 * can use {@link #isInputViewShown()} to determine if the input view
1253 * is currently shown.
1255 public void updateInputViewShown() {
1256 boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
1257 if (mIsInputViewShown != isShown && mWindowVisible) {
1258 mIsInputViewShown = isShown;
1259 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1260 if (mInputView == null) {
1262 View v = onCreateInputView();
1271 * Returns true if we have been asked to show our input view.
1273 public boolean isShowInputRequested() {
1274 return mShowInputRequested;
1278 * Return whether the soft input view is <em>currently</em> shown to the
1279 * user. This is the state that was last determined and
1280 * applied by {@link #updateInputViewShown()}.
1282 public boolean isInputViewShown() {
1283 return mIsInputViewShown && mWindowVisible;
1287 * Override this to control when the soft input area should be shown to the user. The default
1288 * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
1289 * unless the user shows an intention to use software keyboard. If you change what this
1290 * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
1291 * value may have changed to have it re-evaluated and applied.
1293 * <p>When you override this method, it is recommended to call
1294 * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
1298 public boolean onEvaluateInputViewShown() {
1299 if (mSettingsObserver == null) {
1300 Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
1303 if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
1306 Configuration config = getResources().getConfiguration();
1307 return config.keyboard == Configuration.KEYBOARD_NOKEYS
1308 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
1312 * Controls the visibility of the candidates display area. By default
1315 public void setCandidatesViewShown(boolean shown) {
1316 updateCandidatesVisibility(shown);
1317 if (!mShowInputRequested && mWindowVisible != shown) {
1318 // If we are being asked to show the candidates view while the app
1319 // has not asked for the input view to be shown, then we need
1320 // to update whether the window is shown.
1329 void updateCandidatesVisibility(boolean shown) {
1330 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1331 if (mCandidatesVisibility != vis) {
1332 mCandidatesFrame.setVisibility(vis);
1333 mCandidatesVisibility = vis;
1338 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1339 * or {@link View#GONE View.GONE}) of the candidates view when it is not
1340 * shown. The default implementation returns GONE when
1341 * {@link #isExtractViewShown} returns true,
1342 * otherwise VISIBLE. Be careful if you change this to return GONE in
1343 * other situations -- if showing or hiding the candidates view causes
1344 * your window to resize, this can cause temporary drawing artifacts as
1345 * the resize takes place.
1347 public int getCandidatesHiddenVisibility() {
1348 return isExtractViewShown() ? View.GONE : View.INVISIBLE;
1351 public void showStatusIcon(@DrawableRes int iconResId) {
1352 mStatusIcon = iconResId;
1353 mImm.showStatusIcon(mToken, getPackageName(), iconResId);
1356 public void hideStatusIcon() {
1358 mImm.hideStatusIcon(mToken);
1362 * Force switch to a new input method, as identified by <var>id</var>. This
1363 * input method will be destroyed, and the requested one started on the
1364 * current input field.
1366 * @param id Unique identifier of the new input method ot start.
1368 public void switchInputMethod(String id) {
1369 mImm.setInputMethod(mToken, id);
1372 public void setExtractView(View view) {
1373 mExtractFrame.removeAllViews();
1374 mExtractFrame.addView(view, new FrameLayout.LayoutParams(
1375 ViewGroup.LayoutParams.MATCH_PARENT,
1376 ViewGroup.LayoutParams.MATCH_PARENT));
1377 mExtractView = view;
1379 mExtractEditText = (ExtractEditText)view.findViewById(
1380 com.android.internal.R.id.inputExtractEditText);
1381 mExtractEditText.setIME(this);
1382 mExtractAction = view.findViewById(
1383 com.android.internal.R.id.inputExtractAction);
1384 if (mExtractAction != null) {
1385 mExtractAccessories = (ViewGroup)view.findViewById(
1386 com.android.internal.R.id.inputExtractAccessories);
1388 startExtractingText(false);
1390 mExtractEditText = null;
1391 mExtractAccessories = null;
1392 mExtractAction = null;
1397 * Replaces the current candidates view with a new one. You only need to
1398 * call this when dynamically changing the view; normally, you should
1399 * implement {@link #onCreateCandidatesView()} and create your view when
1400 * first needed by the input method.
1402 public void setCandidatesView(View view) {
1403 mCandidatesFrame.removeAllViews();
1404 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
1405 ViewGroup.LayoutParams.MATCH_PARENT,
1406 ViewGroup.LayoutParams.WRAP_CONTENT));
1410 * Replaces the current input view with a new one. You only need to
1411 * call this when dynamically changing the view; normally, you should
1412 * implement {@link #onCreateInputView()} and create your view when
1413 * first needed by the input method.
1415 public void setInputView(View view) {
1416 mInputFrame.removeAllViews();
1417 mInputFrame.addView(view, new FrameLayout.LayoutParams(
1418 ViewGroup.LayoutParams.MATCH_PARENT,
1419 ViewGroup.LayoutParams.WRAP_CONTENT));
1424 * Called by the framework to create the layout for showing extacted text.
1425 * Only called when in fullscreen mode. The returned view hierarchy must
1426 * have an {@link ExtractEditText} whose ID is
1427 * {@link android.R.id#inputExtractEditText}.
1429 public View onCreateExtractTextView() {
1430 return mInflater.inflate(
1431 com.android.internal.R.layout.input_method_extract_view, null);
1435 * Create and return the view hierarchy used to show candidates. This will
1436 * be called once, when the candidates are first displayed. You can return
1437 * null to have no candidates view; the default implementation returns null.
1439 * <p>To control when the candidates view is displayed, use
1440 * {@link #setCandidatesViewShown(boolean)}.
1441 * To change the candidates view after the first one is created by this
1442 * function, use {@link #setCandidatesView(View)}.
1444 public View onCreateCandidatesView() {
1449 * Create and return the view hierarchy used for the input area (such as
1450 * a soft keyboard). This will be called once, when the input area is
1451 * first displayed. You can return null to have no input area; the default
1452 * implementation returns null.
1454 * <p>To control when the input view is displayed, implement
1455 * {@link #onEvaluateInputViewShown()}.
1456 * To change the input view after the first one is created by this
1457 * function, use {@link #setInputView(View)}.
1459 public View onCreateInputView() {
1464 * Called when the input view is being shown and input has started on
1465 * a new editor. This will always be called after {@link #onStartInput},
1466 * allowing you to do your general setup there and just view-specific
1467 * setup here. You are guaranteed that {@link #onCreateInputView()} will
1468 * have been called some time before this function is called.
1470 * @param info Description of the type of text being edited.
1471 * @param restarting Set to true if we are restarting input on the
1472 * same text field as before.
1474 public void onStartInputView(EditorInfo info, boolean restarting) {
1475 // Intentionally empty
1479 * Called when the input view is being hidden from the user. This will
1480 * be called either prior to hiding the window, or prior to switching to
1481 * another target for editing.
1484 * implementation uses the InputConnection to clear any active composing
1485 * text; you can override this (not calling the base class implementation)
1486 * to perform whatever behavior you would like.
1488 * @param finishingInput If true, {@link #onFinishInput} will be
1489 * called immediately after.
1491 public void onFinishInputView(boolean finishingInput) {
1492 if (!finishingInput) {
1493 InputConnection ic = getCurrentInputConnection();
1495 ic.finishComposingText();
1501 * Called when only the candidates view has been shown for showing
1502 * processing as the user enters text through a hard keyboard.
1503 * This will always be called after {@link #onStartInput},
1504 * allowing you to do your general setup there and just view-specific
1505 * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
1506 * will have been called some time before this function is called.
1508 * <p>Note that this will <em>not</em> be called when the input method
1509 * is running in full editing mode, and thus receiving
1510 * {@link #onStartInputView} to initiate that operation. This is only
1511 * for the case when candidates are being shown while the input method
1512 * editor is hidden but wants to show its candidates UI as text is
1513 * entered through some other mechanism.
1515 * @param info Description of the type of text being edited.
1516 * @param restarting Set to true if we are restarting input on the
1517 * same text field as before.
1519 public void onStartCandidatesView(EditorInfo info, boolean restarting) {
1520 // Intentionally empty
1524 * Called when the candidates view is being hidden from the user. This will
1525 * be called either prior to hiding the window, or prior to switching to
1526 * another target for editing.
1529 * implementation uses the InputConnection to clear any active composing
1530 * text; you can override this (not calling the base class implementation)
1531 * to perform whatever behavior you would like.
1533 * @param finishingInput If true, {@link #onFinishInput} will be
1534 * called immediately after.
1536 public void onFinishCandidatesView(boolean finishingInput) {
1537 if (!finishingInput) {
1538 InputConnection ic = getCurrentInputConnection();
1540 ic.finishComposingText();
1546 * The system has decided that it may be time to show your input method.
1547 * This is called due to a corresponding call to your
1548 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
1549 * method. The default implementation uses
1550 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
1551 * and the current configuration to decide whether the input view should
1552 * be shown at this point.
1554 * @param flags Provides additional information about the show request,
1555 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
1556 * @param configChange This is true if we are re-showing due to a
1557 * configuration change.
1558 * @return Returns true to indicate that the window should be shown.
1560 public boolean onShowInputRequested(int flags, boolean configChange) {
1561 if (!onEvaluateInputViewShown()) {
1564 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
1565 if (!configChange && onEvaluateFullscreenMode()) {
1566 // Don't show if this is not explicitly requested by the user and
1567 // the input method is fullscreen. That would be too disruptive.
1568 // However, we skip this change for a config change, since if
1569 // the IME is already shown we do want to go into fullscreen
1570 // mode at this point.
1573 if (!mSettingsObserver.shouldShowImeWithHardKeyboard() &&
1574 getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS) {
1575 // And if the device has a hard keyboard, even if it is
1576 // currently hidden, don't show the input method implicitly.
1577 // These kinds of devices don't need it that much.
1585 * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal
1586 * states depending on its result. Since {@link #onShowInputRequested(int, boolean)} is
1587 * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
1588 * to have this method to ensure that those internal states are always updated no matter how
1589 * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
1590 * @param flags Provides additional information about the show request,
1591 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
1592 * @param configChange This is true if we are re-showing due to a
1593 * configuration change.
1594 * @return Returns true to indicate that the window should be shown.
1595 * @see #onShowInputRequested(int, boolean)
1597 private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
1598 final boolean result = onShowInputRequested(flags, configChange);
1600 mShowInputFlags = flags;
1602 mShowInputFlags = 0;
1607 public void showWindow(boolean showInput) {
1608 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
1609 + " mShowInputRequested=" + mShowInputRequested
1610 + " mWindowAdded=" + mWindowAdded
1611 + " mWindowCreated=" + mWindowCreated
1612 + " mWindowVisible=" + mWindowVisible
1613 + " mInputStarted=" + mInputStarted
1614 + " mShowInputFlags=" + mShowInputFlags);
1616 if (mInShowWindow) {
1617 Log.w(TAG, "Re-entrance in to showWindow");
1622 mWindowWasVisible = mWindowVisible;
1623 mInShowWindow = true;
1624 showWindowInner(showInput);
1625 } catch (BadTokenException e) {
1626 // BadTokenException is a normal consequence in certain situations, e.g., swapping IMEs
1627 // while there is a DO_SHOW_SOFT_INPUT message in the IIMethodWrapper queue.
1628 if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
1629 mWindowVisible = false;
1630 mWindowAdded = false;
1631 // Rethrow the exception to preserve the existing behavior. Some IMEs may have directly
1632 // called this method and relied on this exception for some clean-up tasks.
1633 // TODO: Give developers a clear guideline of whether it's OK to call this method or
1634 // InputMethodManager#showSoftInputFromInputMethod() should always be used instead.
1637 // TODO: Is it OK to set true when we get BadTokenException?
1638 mWindowWasVisible = true;
1639 mInShowWindow = false;
1643 void showWindowInner(boolean showInput) {
1644 boolean doShowInput = false;
1645 final int previousImeWindowStatus =
1646 (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
1647 mWindowVisible = true;
1648 if (!mShowInputRequested && mInputStarted && showInput) {
1650 mShowInputRequested = true;
1653 if (DEBUG) Log.v(TAG, "showWindow: updating UI");
1655 updateFullscreenMode();
1656 updateInputViewShown();
1658 if (!mWindowAdded || !mWindowCreated) {
1659 mWindowAdded = true;
1660 mWindowCreated = true;
1662 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
1663 View v = onCreateCandidatesView();
1664 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
1666 setCandidatesView(v);
1669 if (mShowInputRequested) {
1670 if (!mInputViewStarted) {
1671 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1672 mInputViewStarted = true;
1673 onStartInputView(mInputEditorInfo, false);
1675 } else if (!mCandidatesViewStarted) {
1676 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1677 mCandidatesViewStarted = true;
1678 onStartCandidatesView(mInputEditorInfo, false);
1682 startExtractingText(false);
1685 final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
1686 if (previousImeWindowStatus != nextImeWindowStatus) {
1687 mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
1690 if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
1691 if (DEBUG) Log.v(TAG, "showWindow: showing!");
1694 // Put here rather than in onWindowShown() in case people forget to call
1695 // super.onWindowShown().
1696 mShouldClearInsetOfPreviousIme = false;
1700 private void finishViews() {
1701 if (mInputViewStarted) {
1702 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1703 onFinishInputView(false);
1704 } else if (mCandidatesViewStarted) {
1705 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1706 onFinishCandidatesView(false);
1708 mInputViewStarted = false;
1709 mCandidatesViewStarted = false;
1712 private void doHideWindow() {
1713 mImm.setImeWindowStatus(mToken, mStartInputToken, 0, mBackDisposition);
1717 public void hideWindow() {
1719 if (mWindowVisible) {
1721 mWindowVisible = false;
1723 mWindowWasVisible = false;
1725 updateFullscreenMode();
1729 * Called when the input method window has been shown to the user, after
1730 * previously not being visible. This is done after all of the UI setup
1731 * for the window has occurred (creating its views etc).
1733 public void onWindowShown() {
1734 // Intentionally empty
1738 * Called when the input method window has been hidden from the user,
1739 * after previously being visible.
1741 public void onWindowHidden() {
1742 // Intentionally empty
1746 * Reset the inset occupied the previous IME when and only when
1747 * {@link #mShouldClearInsetOfPreviousIme} is {@code true}.
1749 private void clearInsetOfPreviousIme() {
1750 if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() "
1751 + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
1752 if (!mShouldClearInsetOfPreviousIme) return;
1754 mImm.clearLastInputMethodWindowForTransition(mToken);
1755 mShouldClearInsetOfPreviousIme = false;
1759 * Called when a new client has bound to the input method. This
1760 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
1761 * and {@link #onFinishInput()} calls as the user navigates through its
1762 * UI. Upon this call you know that {@link #getCurrentInputBinding}
1763 * and {@link #getCurrentInputConnection} return valid objects.
1765 public void onBindInput() {
1766 // Intentionally empty
1770 * Called when the previous bound client is no longer associated
1771 * with the input method. After returning {@link #getCurrentInputBinding}
1772 * and {@link #getCurrentInputConnection} will no longer return
1775 public void onUnbindInput() {
1776 // Intentionally empty
1780 * Called to inform the input method that text input has started in an
1781 * editor. You should use this callback to initialize the state of your
1782 * input to match the state of the editor given to it.
1784 * @param attribute The attributes of the editor that input is starting
1786 * @param restarting Set to true if input is restarting in the same
1787 * editor such as because the application has changed the text in
1788 * the editor. Otherwise will be false, indicating this is a new
1789 * session with the editor.
1791 public void onStartInput(EditorInfo attribute, boolean restarting) {
1792 // Intentionally empty
1795 void doFinishInput() {
1796 if (mInputViewStarted) {
1797 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1798 onFinishInputView(true);
1799 } else if (mCandidatesViewStarted) {
1800 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1801 onFinishCandidatesView(true);
1803 mInputViewStarted = false;
1804 mCandidatesViewStarted = false;
1805 if (mInputStarted) {
1806 if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
1809 mInputStarted = false;
1810 mStartedInputConnection = null;
1811 mCurCompletions = null;
1814 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
1818 mInputStarted = true;
1819 mStartedInputConnection = ic;
1820 mInputEditorInfo = attribute;
1822 if (DEBUG) Log.v(TAG, "CALL: onStartInput");
1823 onStartInput(attribute, restarting);
1824 if (mWindowVisible) {
1825 if (mShowInputRequested) {
1826 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1827 mInputViewStarted = true;
1828 onStartInputView(mInputEditorInfo, restarting);
1829 startExtractingText(true);
1830 } else if (mCandidatesVisibility == View.VISIBLE) {
1831 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1832 mCandidatesViewStarted = true;
1833 onStartCandidatesView(mInputEditorInfo, restarting);
1839 * Called to inform the input method that text input has finished in
1840 * the last editor. At this point there may be a call to
1841 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
1842 * new editor, or the input method may be left idle. This method is
1843 * <em>not</em> called when input restarts in the same editor.
1846 * implementation uses the InputConnection to clear any active composing
1847 * text; you can override this (not calling the base class implementation)
1848 * to perform whatever behavior you would like.
1850 public void onFinishInput() {
1851 InputConnection ic = getCurrentInputConnection();
1853 ic.finishComposingText();
1858 * Called when the application has reported auto-completion candidates that
1859 * it would like to have the input method displayed. Typically these are
1860 * only used when an input method is running in full-screen mode, since
1861 * otherwise the user can see and interact with the pop-up window of
1862 * completions shown by the application.
1864 * <p>The default implementation here does nothing.
1866 public void onDisplayCompletions(CompletionInfo[] completions) {
1867 // Intentionally empty
1871 * Called when the application has reported new extracted text to be shown
1872 * due to changes in its current text state. The default implementation
1873 * here places the new text in the extract edit text, when the input
1874 * method is running in fullscreen mode.
1876 public void onUpdateExtractedText(int token, ExtractedText text) {
1877 if (mExtractedToken != token) {
1881 if (mExtractEditText != null) {
1882 mExtractedText = text;
1883 mExtractEditText.setExtractedText(text);
1889 * Called when the application has reported a new selection region of
1890 * the text. This is called whether or not the input method has requested
1891 * extracted text updates, although if so it will not receive this call
1892 * if the extracted text has changed as well.
1894 * <p>Be careful about changing the text in reaction to this call with
1895 * methods such as setComposingText, commitText or
1896 * deleteSurroundingText. If the cursor moves as a result, this method
1897 * will be called again, which may result in an infinite loop.
1899 * <p>The default implementation takes care of updating the cursor in
1900 * the extract text, if it is being shown.
1902 public void onUpdateSelection(int oldSelStart, int oldSelEnd,
1903 int newSelStart, int newSelEnd,
1904 int candidatesStart, int candidatesEnd) {
1905 final ExtractEditText eet = mExtractEditText;
1906 if (eet != null && isFullscreenMode() && mExtractedText != null) {
1907 final int off = mExtractedText.startOffset;
1908 eet.startInternalChanges();
1911 final int len = eet.getText().length();
1912 if (newSelStart < 0) newSelStart = 0;
1913 else if (newSelStart > len) newSelStart = len;
1914 if (newSelEnd < 0) newSelEnd = 0;
1915 else if (newSelEnd > len) newSelEnd = len;
1916 eet.setSelection(newSelStart, newSelEnd);
1917 eet.finishInternalChanges();
1922 * Called when the user tapped or clicked a text view.
1923 * IMEs can't rely on this method being called because this was not part of the original IME
1924 * protocol, so applications with custom text editing written before this method appeared will
1925 * not call to inform the IME of this interaction.
1926 * @param focusChanged true if the user changed the focused view by this click.
1928 public void onViewClicked(boolean focusChanged) {
1929 // Intentionally empty
1933 * Called when the application has reported a new location of its text
1934 * cursor. This is only called if explicitly requested by the input method.
1935 * The default implementation does nothing.
1936 * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
1939 public void onUpdateCursor(Rect newCursor) {
1940 // Intentionally empty
1944 * Called when the application has reported a new location of its text insertion point and
1945 * characters in the composition string. This is only called if explicitly requested by the
1946 * input method. The default implementation does nothing.
1947 * @param cursorAnchorInfo The positional information of the text insertion point and the
1948 * composition string.
1950 public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
1951 // Intentionally empty
1955 * Close this input method's soft input area, removing it from the display.
1956 * The input method will continue running, but the user can no longer use
1957 * it to generate input by touching the screen.
1958 * @param flags Provides additional operating flags. Currently may be
1959 * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
1960 * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
1962 public void requestHideSelf(int flags) {
1963 mImm.hideSoftInputFromInputMethod(mToken, flags);
1967 * Show the input method. This is a call back to the
1968 * IMF to handle showing the input method.
1969 * @param flags Provides additional operating flags. Currently may be
1970 * 0 or have the {@link InputMethodManager#SHOW_FORCED
1971 * InputMethodManager.} bit set.
1973 private void requestShowSelf(int flags) {
1974 mImm.showSoftInputFromInputMethod(mToken, flags);
1977 private boolean handleBack(boolean doIt) {
1978 if (mShowInputRequested) {
1979 // If the soft input area is shown, back closes it and we
1980 // consume the back key.
1981 if (doIt) requestHideSelf(0);
1983 } else if (mWindowVisible) {
1984 if (mCandidatesVisibility == View.VISIBLE) {
1985 // If we are showing candidates even if no input area, then
1987 if (doIt) setCandidatesViewShown(false);
1989 // If we have the window visible for some other reason --
1990 // most likely to show candidates -- then just get rid
1991 // of it. This really shouldn't happen, but just in case...
1992 if (doIt) doHideWindow();
2000 * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise
2001 * {@code null} is returned.
2003 private ExtractEditText getExtractEditTextIfVisible() {
2004 if (!isExtractViewShown() || !isInputViewShown()) {
2007 return mExtractEditText;
2011 * Override this to intercept key down events before they are processed by the
2012 * application. If you return true, the application will not
2013 * process the event itself. If you return false, the normal application processing
2014 * will occur as if the IME had not seen the event at all.
2016 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
2017 * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
2018 * possibly hide it when the key goes up (if not canceled or long pressed). In
2019 * addition, in fullscreen mode only, it will consume DPAD movement
2020 * events to move the cursor in the extracted text view, not allowing
2021 * them to perform navigation in the underlying application.
2023 public boolean onKeyDown(int keyCode, KeyEvent event) {
2024 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
2025 final ExtractEditText eet = getExtractEditTextIfVisible();
2026 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2029 if (handleBack(false)) {
2030 event.startTracking();
2035 return doMovementKey(keyCode, event, MOVEMENT_DOWN);
2039 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
2040 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
2043 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
2048 * Override this to intercept special key multiple events before they are
2050 * application. If you return true, the application will not itself
2051 * process the event. If you return false, the normal application processing
2052 * will occur as if the IME had not seen the event at all.
2054 * <p>The default implementation always returns false, except when
2055 * in fullscreen mode, where it will consume DPAD movement
2056 * events to move the cursor in the extracted text view, not allowing
2057 * them to perform navigation in the underlying application.
2059 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
2060 return doMovementKey(keyCode, event, count);
2064 * Override this to intercept key up events before they are processed by the
2065 * application. If you return true, the application will not itself
2066 * process the event. If you return false, the normal application processing
2067 * will occur as if the IME had not seen the event at all.
2069 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
2070 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
2071 * addition, in fullscreen mode only, it will consume DPAD movement
2072 * events to move the cursor in the extracted text view, not allowing
2073 * them to perform navigation in the underlying application.
2075 public boolean onKeyUp(int keyCode, KeyEvent event) {
2076 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
2077 final ExtractEditText eet = getExtractEditTextIfVisible();
2078 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2081 if (event.isTracking() && !event.isCanceled()) {
2082 return handleBack(true);
2085 return doMovementKey(keyCode, event, MOVEMENT_UP);
2089 * Override this to intercept trackball motion events before they are
2090 * processed by the application.
2091 * If you return true, the application will not itself process the event.
2092 * If you return false, the normal application processing will occur as if
2093 * the IME had not seen the event at all.
2096 public boolean onTrackballEvent(MotionEvent event) {
2097 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
2102 * Override this to intercept generic motion events before they are
2103 * processed by the application.
2104 * If you return true, the application will not itself process the event.
2105 * If you return false, the normal application processing will occur as if
2106 * the IME had not seen the event at all.
2109 public boolean onGenericMotionEvent(MotionEvent event) {
2110 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
2114 public void onAppPrivateCommand(String action, Bundle data) {
2118 * Handle a request by the system to toggle the soft input area.
2120 private void onToggleSoftInput(int showFlags, int hideFlags) {
2121 if (DEBUG) Log.v(TAG, "toggleSoftInput()");
2122 if (isInputViewShown()) {
2123 requestHideSelf(hideFlags);
2125 requestShowSelf(showFlags);
2129 static final int MOVEMENT_DOWN = -1;
2130 static final int MOVEMENT_UP = -2;
2132 void reportExtractedMovement(int keyCode, int count) {
2135 case KeyEvent.KEYCODE_DPAD_LEFT:
2138 case KeyEvent.KEYCODE_DPAD_RIGHT:
2141 case KeyEvent.KEYCODE_DPAD_UP:
2144 case KeyEvent.KEYCODE_DPAD_DOWN:
2148 onExtractedCursorMovement(dx, dy);
2151 boolean doMovementKey(int keyCode, KeyEvent event, int count) {
2152 final ExtractEditText eet = getExtractEditTextIfVisible();
2154 // If we are in fullscreen mode, the cursor will move around
2155 // the extract edit text, but should NOT cause focus to move
2157 MovementMethod movement = eet.getMovementMethod();
2158 Layout layout = eet.getLayout();
2159 if (movement != null && layout != null) {
2160 // We want our own movement method to handle the key, so the
2161 // cursor will properly move in our own word wrapping.
2162 if (count == MOVEMENT_DOWN) {
2163 if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) {
2164 reportExtractedMovement(keyCode, 1);
2167 } else if (count == MOVEMENT_UP) {
2168 if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) {
2172 if (movement.onKeyOther(eet, eet.getText(), event)) {
2173 reportExtractedMovement(keyCode, count);
2175 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
2176 if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) {
2177 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
2178 movement.onKeyUp(eet, eet.getText(), keyCode, up);
2179 while (--count > 0) {
2180 movement.onKeyDown(eet, eet.getText(), keyCode, down);
2181 movement.onKeyUp(eet, eet.getText(), keyCode, up);
2183 reportExtractedMovement(keyCode, count);
2188 // Regardless of whether the movement method handled the key,
2189 // we never allow DPAD navigation to the application.
2191 case KeyEvent.KEYCODE_DPAD_LEFT:
2192 case KeyEvent.KEYCODE_DPAD_RIGHT:
2193 case KeyEvent.KEYCODE_DPAD_UP:
2194 case KeyEvent.KEYCODE_DPAD_DOWN:
2203 * Send the given key event code (as defined by {@link KeyEvent}) to the
2204 * current input connection is a key down + key up event pair. The sent
2205 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
2206 * set, so that the recipient can identify them as coming from a software
2208 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
2209 * that they don't impact the current touch mode of the UI.
2211 * <p>Note that it's discouraged to send such key events in normal operation;
2212 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
2213 * text fields, or for non-rich input methods. A reasonably capable software
2214 * input method should use the
2215 * {@link android.view.inputmethod.InputConnection#commitText} family of methods
2216 * to send text to an application, rather than sending key events.</p>
2218 * @param keyEventCode The raw key code to send, as defined by
2221 public void sendDownUpKeyEvents(int keyEventCode) {
2222 InputConnection ic = getCurrentInputConnection();
2223 if (ic == null) return;
2224 long eventTime = SystemClock.uptimeMillis();
2225 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
2226 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
2227 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2228 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
2229 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
2230 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2234 * Ask the input target to execute its default action via
2235 * {@link InputConnection#performEditorAction
2236 * InputConnection.performEditorAction()}.
2238 * @param fromEnterKey If true, this will be executed as if the user had
2239 * pressed an enter key on the keyboard, that is it will <em>not</em>
2240 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
2241 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
2242 * sent regardless of how the editor has set that flag.
2244 * @return Returns a boolean indicating whether an action has been sent.
2245 * If false, either the editor did not specify a default action or it
2246 * does not want an action from the enter key. If true, the action was
2247 * sent (or there was no input connection at all).
2249 public boolean sendDefaultEditorAction(boolean fromEnterKey) {
2250 EditorInfo ei = getCurrentInputEditorInfo();
2252 (!fromEnterKey || (ei.imeOptions &
2253 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
2254 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
2255 EditorInfo.IME_ACTION_NONE) {
2256 // If the enter key was pressed, and the editor has a default
2257 // action associated with pressing enter, then send it that
2258 // explicit action instead of the key event.
2259 InputConnection ic = getCurrentInputConnection();
2261 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
2270 * Send the given UTF-16 character to the current input connection. Most
2271 * characters will be delivered simply by calling
2272 * {@link InputConnection#commitText InputConnection.commitText()} with
2273 * the character; some, however, may be handled different. In particular,
2274 * the enter character ('\n') will either be delivered as an action code
2275 * or a raw key event, as appropriate. Consider this as a convenience
2276 * method for IMEs that do not have a full implementation of actions; a
2277 * fully complying IME will decide of the right action for each event and
2278 * will likely never call this method except maybe to handle events coming
2279 * from an actual hardware keyboard.
2281 * @param charCode The UTF-16 character code to send.
2283 public void sendKeyChar(char charCode) {
2285 case '\n': // Apps may be listening to an enter key to perform an action
2286 if (!sendDefaultEditorAction(true)) {
2287 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2291 // Make sure that digits go through any text watcher on the client side.
2292 if (charCode >= '0' && charCode <= '9') {
2293 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2295 InputConnection ic = getCurrentInputConnection();
2297 ic.commitText(String.valueOf(charCode), 1);
2305 * This is called when the user has moved the cursor in the extracted
2306 * text view, when running in fullsreen mode. The default implementation
2307 * performs the corresponding selection change on the underlying text
2310 public void onExtractedSelectionChanged(int start, int end) {
2311 InputConnection conn = getCurrentInputConnection();
2313 conn.setSelection(start, end);
2320 public void onExtractedDeleteText(int start, int end) {
2321 InputConnection conn = getCurrentInputConnection();
2323 conn.finishComposingText();
2324 conn.setSelection(start, start);
2325 conn.deleteSurroundingText(0, end - start);
2332 public void onExtractedReplaceText(int start, int end, CharSequence text) {
2333 InputConnection conn = getCurrentInputConnection();
2335 conn.setComposingRegion(start, end);
2336 conn.commitText(text, 1);
2343 public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2344 InputConnection conn = getCurrentInputConnection();
2346 if (!conn.setSelection(start, end)) return;
2347 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2348 if (text instanceof Spannable) {
2349 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2350 conn.setComposingRegion(start, end);
2351 conn.commitText(text, 1);
2357 * This is called when the user has clicked on the extracted text view,
2358 * when running in fullscreen mode. The default implementation hides
2359 * the candidates view when this happens, but only if the extracted text
2360 * editor has a vertical scroll bar because its text doesn't fit.
2361 * Re-implement this to provide whatever behavior you want.
2363 public void onExtractedTextClicked() {
2364 if (mExtractEditText == null) {
2367 if (mExtractEditText.hasVerticalScrollBar()) {
2368 setCandidatesViewShown(false);
2373 * This is called when the user has performed a cursor movement in the
2374 * extracted text view, when it is running in fullscreen mode. The default
2375 * implementation hides the candidates view when a vertical movement
2376 * happens, but only if the extracted text editor has a vertical scroll bar
2377 * because its text doesn't fit.
2378 * Re-implement this to provide whatever behavior you want.
2379 * @param dx The amount of cursor movement in the x dimension.
2380 * @param dy The amount of cursor movement in the y dimension.
2382 public void onExtractedCursorMovement(int dx, int dy) {
2383 if (mExtractEditText == null || dy == 0) {
2386 if (mExtractEditText.hasVerticalScrollBar()) {
2387 setCandidatesViewShown(false);
2392 * This is called when the user has selected a context menu item from the
2393 * extracted text view, when running in fullscreen mode. The default
2394 * implementation sends this action to the current InputConnection's
2395 * {@link InputConnection#performContextMenuAction(int)}, for it
2396 * to be processed in underlying "real" editor. Re-implement this to
2397 * provide whatever behavior you want.
2399 public boolean onExtractTextContextMenuItem(int id) {
2400 InputConnection ic = getCurrentInputConnection();
2402 ic.performContextMenuAction(id);
2408 * Return text that can be used as a button label for the given
2409 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
2410 * if there is no action requested. Note that there is no guarantee that
2411 * the returned text will be relatively short, so you probably do not
2412 * want to use it as text on a soft keyboard key label.
2414 * @param imeOptions The value from {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
2416 * @return Returns a label to use, or null if there is no action.
2418 public CharSequence getTextForImeAction(int imeOptions) {
2419 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2420 case EditorInfo.IME_ACTION_NONE:
2422 case EditorInfo.IME_ACTION_GO:
2423 return getText(com.android.internal.R.string.ime_action_go);
2424 case EditorInfo.IME_ACTION_SEARCH:
2425 return getText(com.android.internal.R.string.ime_action_search);
2426 case EditorInfo.IME_ACTION_SEND:
2427 return getText(com.android.internal.R.string.ime_action_send);
2428 case EditorInfo.IME_ACTION_NEXT:
2429 return getText(com.android.internal.R.string.ime_action_next);
2430 case EditorInfo.IME_ACTION_DONE:
2431 return getText(com.android.internal.R.string.ime_action_done);
2432 case EditorInfo.IME_ACTION_PREVIOUS:
2433 return getText(com.android.internal.R.string.ime_action_previous);
2435 return getText(com.android.internal.R.string.ime_action_default);
2440 * Return a drawable resource id that can be used as a button icon for the given
2441 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
2443 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
2445 * @return Returns a drawable resource id to use.
2448 private int getIconForImeAction(int imeOptions) {
2449 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2450 case EditorInfo.IME_ACTION_GO:
2451 return com.android.internal.R.drawable.ic_input_extract_action_go;
2452 case EditorInfo.IME_ACTION_SEARCH:
2453 return com.android.internal.R.drawable.ic_input_extract_action_search;
2454 case EditorInfo.IME_ACTION_SEND:
2455 return com.android.internal.R.drawable.ic_input_extract_action_send;
2456 case EditorInfo.IME_ACTION_NEXT:
2457 return com.android.internal.R.drawable.ic_input_extract_action_next;
2458 case EditorInfo.IME_ACTION_DONE:
2459 return com.android.internal.R.drawable.ic_input_extract_action_done;
2460 case EditorInfo.IME_ACTION_PREVIOUS:
2461 return com.android.internal.R.drawable.ic_input_extract_action_previous;
2463 return com.android.internal.R.drawable.ic_input_extract_action_return;
2468 * Called when the fullscreen-mode extracting editor info has changed,
2469 * to determine whether the extracting (extract text and candidates) portion
2470 * of the UI should be shown. The standard implementation hides or shows
2471 * the extract area depending on whether it makes sense for the
2472 * current editor. In particular, a {@link InputType#TYPE_NULL}
2473 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
2474 * turn off the extract area since there is no text to be shown.
2476 public void onUpdateExtractingVisibility(EditorInfo ei) {
2477 if (ei.inputType == InputType.TYPE_NULL ||
2478 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
2479 // No reason to show extract UI!
2480 setExtractViewShown(false);
2484 setExtractViewShown(true);
2488 * Called when the fullscreen-mode extracting editor info has changed,
2489 * to update the state of its UI such as the action buttons shown.
2490 * You do not need to deal with this if you are using the standard
2491 * full screen extract UI. If replacing it, you will need to re-implement
2492 * this to put the appropriate action button in your own UI and handle it,
2493 * and perform any other changes.
2495 * <p>The standard implementation turns on or off its accessory area
2496 * depending on whether there is an action button, and hides or shows
2497 * the entire extract area depending on whether it makes sense for the
2498 * current editor. In particular, a {@link InputType#TYPE_NULL} or
2499 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
2500 * extract area since there is no text to be shown.
2502 public void onUpdateExtractingViews(EditorInfo ei) {
2503 if (!isExtractViewShown()) {
2507 if (mExtractAccessories == null) {
2510 final boolean hasAction = ei.actionLabel != null || (
2511 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
2512 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
2513 ei.inputType != InputType.TYPE_NULL);
2515 mExtractAccessories.setVisibility(View.VISIBLE);
2516 if (mExtractAction != null) {
2517 if (mExtractAction instanceof ImageButton) {
2518 ((ImageButton) mExtractAction)
2519 .setImageResource(getIconForImeAction(ei.imeOptions));
2520 if (ei.actionLabel != null) {
2521 mExtractAction.setContentDescription(ei.actionLabel);
2523 mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
2526 if (ei.actionLabel != null) {
2527 ((TextView) mExtractAction).setText(ei.actionLabel);
2529 ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
2532 mExtractAction.setOnClickListener(mActionClickListener);
2535 mExtractAccessories.setVisibility(View.GONE);
2536 if (mExtractAction != null) {
2537 mExtractAction.setOnClickListener(null);
2543 * This is called when, while currently displayed in extract mode, the
2544 * current input target changes. The default implementation will
2545 * auto-hide the IME if the new target is not a full editor, since this
2546 * can be a confusing experience for the user.
2548 public void onExtractingInputChanged(EditorInfo ei) {
2549 if (ei.inputType == InputType.TYPE_NULL) {
2550 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
2554 void startExtractingText(boolean inputChanged) {
2555 final ExtractEditText eet = mExtractEditText;
2556 if (eet != null && getCurrentInputStarted()
2557 && isFullscreenMode()) {
2559 ExtractedTextRequest req = new ExtractedTextRequest();
2560 req.token = mExtractedToken;
2561 req.flags = InputConnection.GET_TEXT_WITH_STYLES;
2562 req.hintMaxLines = 10;
2563 req.hintMaxChars = 10000;
2564 InputConnection ic = getCurrentInputConnection();
2565 mExtractedText = ic == null? null
2566 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
2567 if (mExtractedText == null || ic == null) {
2568 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
2569 + mExtractedText + ", input connection = " + ic);
2571 final EditorInfo ei = getCurrentInputEditorInfo();
2574 eet.startInternalChanges();
2575 onUpdateExtractingVisibility(ei);
2576 onUpdateExtractingViews(ei);
2577 int inputType = ei.inputType;
2578 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
2579 == EditorInfo.TYPE_CLASS_TEXT) {
2580 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
2581 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2584 eet.setInputType(inputType);
2585 eet.setHint(ei.hintText);
2586 if (mExtractedText != null) {
2587 eet.setEnabled(true);
2588 eet.setExtractedText(mExtractedText);
2590 eet.setEnabled(false);
2594 eet.finishInternalChanges();
2598 onExtractingInputChanged(ei);
2603 // TODO: Handle the subtype change event
2605 * Called when the subtype was changed.
2606 * @param newSubtype the subtype which is being changed to.
2608 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
2610 int nameResId = newSubtype.getNameResId();
2611 String mode = newSubtype.getMode();
2612 String output = "changeInputMethodSubtype:"
2613 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
2615 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
2616 Log.v(TAG, "--- " + output);
2621 * @return The recommended height of the input method window.
2622 * An IME author can get the last input method's height as the recommended height
2623 * by calling this in
2624 * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}.
2625 * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME
2626 * switching by using this value as a visible inset height. It's efficient for the smooth
2627 * transition between different IMEs. However, note that this may return 0 (or possibly
2628 * unexpectedly low height). You should thus avoid relying on the return value of this method
2629 * all the time. Please make sure to use a reasonable height for the IME.
2631 public int getInputMethodWindowRecommendedHeight() {
2632 return mImm.getInputMethodWindowVisibleHeight();
2636 * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
2637 * permission to the content.
2639 * @param inputContentInfo Content to be temporarily exposed from the input method to the
2641 * This cannot be {@code null}.
2642 * @param inputConnection {@link InputConnection} with which
2643 * {@link InputConnection#commitContent(InputContentInfo, Bundle)} will be called.
2647 public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
2648 @NonNull InputConnection inputConnection) {
2649 if (inputConnection == null) {
2652 if (getCurrentInputConnection() != inputConnection) {
2655 mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo());
2659 * Performs a dump of the InputMethodService's internal state. Override
2660 * to add your own information to the dump.
2662 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2663 final Printer p = new PrintWriterPrinter(fout);
2664 p.println("Input method service state for " + this + ":");
2665 p.println(" mWindowCreated=" + mWindowCreated
2666 + " mWindowAdded=" + mWindowAdded);
2667 p.println(" mWindowVisible=" + mWindowVisible
2668 + " mWindowWasVisible=" + mWindowWasVisible
2669 + " mInShowWindow=" + mInShowWindow);
2670 p.println(" Configuration=" + getResources().getConfiguration());
2671 p.println(" mToken=" + mToken);
2672 p.println(" mInputBinding=" + mInputBinding);
2673 p.println(" mInputConnection=" + mInputConnection);
2674 p.println(" mStartedInputConnection=" + mStartedInputConnection);
2675 p.println(" mInputStarted=" + mInputStarted
2676 + " mInputViewStarted=" + mInputViewStarted
2677 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
2678 p.println(" mStartInputToken=" + mStartInputToken);
2680 if (mInputEditorInfo != null) {
2681 p.println(" mInputEditorInfo:");
2682 mInputEditorInfo.dump(p, " ");
2684 p.println(" mInputEditorInfo: null");
2687 p.println(" mShowInputRequested=" + mShowInputRequested
2688 + " mLastShowInputRequested=" + mLastShowInputRequested
2689 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
2690 p.println(" mCandidatesVisibility=" + mCandidatesVisibility
2691 + " mFullscreenApplied=" + mFullscreenApplied
2692 + " mIsFullscreen=" + mIsFullscreen
2693 + " mExtractViewHidden=" + mExtractViewHidden);
2695 if (mExtractedText != null) {
2696 p.println(" mExtractedText:");
2697 p.println(" text=" + mExtractedText.text.length() + " chars"
2698 + " startOffset=" + mExtractedText.startOffset);
2699 p.println(" selectionStart=" + mExtractedText.selectionStart
2700 + " selectionEnd=" + mExtractedText.selectionEnd
2701 + " flags=0x" + Integer.toHexString(mExtractedText.flags));
2703 p.println(" mExtractedText: null");
2705 p.println(" mExtractedToken=" + mExtractedToken);
2706 p.println(" mIsInputViewShown=" + mIsInputViewShown
2707 + " mStatusIcon=" + mStatusIcon);
2708 p.println("Last computed insets:");
2709 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
2710 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
2711 + " touchableInsets=" + mTmpInsets.touchableInsets
2712 + " touchableRegion=" + mTmpInsets.touchableRegion);
2713 p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
2714 p.println(" mSettingsObserver=" + mSettingsObserver);