blob: 9d53a0001a84b5581ec9ad8950f876e1a56cb7a5 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007-2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package android.inputmethodservice;
18
Romain Guy980a9382010-01-08 15:06:28 -080019import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21
Yohei Yukawa7b739a82015-12-21 13:30:44 -080022import android.annotation.CallSuper;
Tor Norbye7b9c9122013-05-30 16:48:33 -070023import android.annotation.DrawableRes;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080024import android.annotation.IntDef;
25import android.annotation.MainThread;
Dianne Hackborn836531b2012-08-01 19:00:38 -070026import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.app.Dialog;
28import android.content.Context;
29import android.content.res.Configuration;
Dianne Hackbornd922ae02011-01-14 11:43:24 -080030import android.content.res.Resources;
The Android Open Source Project10592532009-03-18 17:39:46 -070031import android.content.res.TypedArray;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080032import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.graphics.Rect;
Jeff Brownfbf09772011-01-16 14:06:57 -080034import android.graphics.Region;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080035import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.os.Bundle;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080037import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.IBinder;
The Android Open Source Project4df24232009-03-05 14:34:35 -080039import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.SystemClock;
41import android.provider.Settings;
42import android.text.InputType;
43import android.text.Layout;
44import android.text.Spannable;
45import android.text.method.MovementMethod;
46import android.util.Log;
47import android.util.PrintWriterPrinter;
48import android.util.Printer;
Dianne Hackborne30e02f2014-05-27 18:24:45 -070049import android.view.Gravity;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080050import android.view.KeyCharacterMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.view.KeyEvent;
52import android.view.LayoutInflater;
53import android.view.MotionEvent;
54import android.view.View;
55import android.view.ViewGroup;
56import android.view.ViewTreeObserver;
57import android.view.Window;
58import android.view.WindowManager;
Craig Mautnere4bbb1c2013-03-15 11:38:44 -070059import android.view.WindowManager.BadTokenException;
The Android Open Source Project10592532009-03-18 17:39:46 -070060import android.view.animation.AnimationUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.view.inputmethod.CompletionInfo;
Yohei Yukawac2ddd602014-05-06 21:22:49 +090062import android.view.inputmethod.CursorAnchorInfo;
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080063import android.view.inputmethod.EditorInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.view.inputmethod.ExtractedText;
65import android.view.inputmethod.ExtractedTextRequest;
66import android.view.inputmethod.InputBinding;
67import android.view.inputmethod.InputConnection;
68import android.view.inputmethod.InputMethod;
69import android.view.inputmethod.InputMethodManager;
satokab751aa2010-09-14 19:17:36 +090070import android.view.inputmethod.InputMethodSubtype;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.widget.Button;
72import android.widget.FrameLayout;
The Android Open Source Project10592532009-03-18 17:39:46 -070073import android.widget.LinearLayout;
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import java.io.FileDescriptor;
76import java.io.PrintWriter;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080077import java.lang.annotation.Retention;
78import java.lang.annotation.RetentionPolicy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
80/**
81 * InputMethodService provides a standard implementation of an InputMethod,
82 * which final implementations can derive from and customize. See the
83 * base class {@link AbstractInputMethodService} and the {@link InputMethod}
84 * interface for more information on the basics of writing input methods.
85 *
86 * <p>In addition to the normal Service lifecycle methods, this class
87 * introduces some new specific callbacks that most subclasses will want
88 * to make use of:</p>
89 * <ul>
90 * <li> {@link #onInitializeInterface()} for user-interface initialization,
91 * in particular to deal with configuration changes while the service is
92 * running.
93 * <li> {@link #onBindInput} to find out about switching to a new client.
94 * <li> {@link #onStartInput} to deal with an input session starting with
95 * the client.
96 * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
97 * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
98 * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
99 * starting within the input area of the IME.
100 * </ul>
101 *
102 * <p>An input method has significant discretion in how it goes about its
103 * work: the {@link android.inputmethodservice.InputMethodService} provides
104 * a basic framework for standard UI elements (input view, candidates view,
105 * and running in fullscreen mode), but it is up to a particular implementor
106 * to decide how to use them. For example, one input method could implement
107 * an input area with a keyboard, another could allow the user to draw text,
108 * while a third could have no input area (and thus not be visible to the
109 * user) but instead listen to audio and perform text to speech conversion.</p>
110 *
111 * <p>In the implementation provided here, all of these elements are placed
112 * together in a single window managed by the InputMethodService. It will
113 * execute callbacks as it needs information about them, and provides APIs for
114 * programmatic control over them. They layout of these elements is explicitly
115 * defined:</p>
116 *
117 * <ul>
118 * <li>The soft input view, if available, is placed at the bottom of the
119 * screen.
120 * <li>The candidates view, if currently shown, is placed above the soft
121 * input view.
122 * <li>If not running fullscreen, the application is moved or resized to be
123 * above these views; if running fullscreen, the window will completely cover
124 * the application and its top part will contain the extract text of what is
125 * currently being edited by the application.
126 * </ul>
127 *
128 *
129 * <a name="SoftInputView"></a>
130 * <h3>Soft Input View</h3>
131 *
132 * <p>Central to most input methods is the soft input view. This is where most
133 * user interaction occurs: pressing on soft keys, drawing characters, or
134 * however else your input method wants to generate text. Most implementations
135 * will simply have their own view doing all of this work, and return a new
136 * instance of it when {@link #onCreateInputView()} is called. At that point,
137 * as long as the input view is visible, you will see user interaction in
138 * that view and can call back on the InputMethodService to interact with the
139 * application as appropriate.</p>
140 *
141 * <p>There are some situations where you want to decide whether or not your
142 * soft input view should be shown to the user. This is done by implementing
143 * the {@link #onEvaluateInputViewShown()} to return true or false based on
144 * whether it should be shown in the current environment. If any of your
145 * state has changed that may impact this, call
146 * {@link #updateInputViewShown()} to have it re-evaluated. The default
147 * implementation always shows the input view unless there is a hard
148 * keyboard available, which is the appropriate behavior for most input
149 * methods.</p>
150 *
151 *
152 * <a name="CandidatesView"></a>
153 * <h3>Candidates View</h3>
154 *
155 * <p>Often while the user is generating raw text, an input method wants to
156 * provide them with a list of possible interpretations of that text that can
157 * be selected for use. This is accomplished with the candidates view, and
158 * like the soft input view you implement {@link #onCreateCandidatesView()}
159 * to instantiate your own view implementing your candidates UI.</p>
160 *
161 * <p>Management of the candidates view is a little different than the input
162 * view, because the candidates view tends to be more transient, being shown
163 * only when there are possible candidates for the current text being entered
164 * by the user. To control whether the candidates view is shown, you use
165 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate
166 * view tends to be shown and hidden a lot, it does not impact the application
167 * UI in the same way as the soft input view: it will never cause application
168 * windows to resize, only cause them to be panned if needed for the user to
169 * see the current focus.</p>
170 *
171 *
172 * <a name="FullscreenMode"></a>
173 * <h3>Fullscreen Mode</h3>
174 *
175 * <p>Sometimes your input method UI is too large to integrate with the
176 * application UI, so you just want to take over the screen. This is
177 * accomplished by switching to full-screen mode, causing the input method
178 * window to fill the entire screen and add its own "extracted text" editor
179 * showing the user the text that is being typed. Unlike the other UI elements,
180 * there is a standard implementation for the extract editor that you should
181 * not need to change. The editor is placed at the top of the IME, above the
182 * input and candidates views.</p>
183 *
184 * <p>Similar to the input view, you control whether the IME is running in
185 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
186 * to return true or false based on
187 * whether it should be fullscreen in the current environment. If any of your
188 * state has changed that may impact this, call
189 * {@link #updateFullscreenMode()} to have it re-evaluated. The default
190 * implementation selects fullscreen mode when the screen is in a landscape
191 * orientation, which is appropriate behavior for most input methods that have
192 * a significant input area.</p>
193 *
194 * <p>When in fullscreen mode, you have some special requirements because the
195 * user can not see the application UI. In particular, you should implement
196 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
197 * generated by your application, typically in your candidates view like you
198 * would normally show candidates.
199 *
200 *
201 * <a name="GeneratingText"></a>
202 * <h3>Generating Text</h3>
203 *
204 * <p>The key part of an IME is of course generating text for the application.
205 * This is done through calls to the
206 * {@link android.view.inputmethod.InputConnection} interface to the
207 * application, which can be retrieved from {@link #getCurrentInputConnection()}.
208 * This interface allows you to generate raw key events or, if the target
209 * supports it, directly edit in strings of candidates and committed text.</p>
210 *
211 * <p>Information about what the target is expected and supports can be found
212 * through the {@link android.view.inputmethod.EditorInfo} class, which is
213 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most
214 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
215 * EditorInfo.inputType}; in particular, if this is
216 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
217 * then the target does not support complex edits and you need to only deliver
218 * raw key events to it. An input method will also want to look at other
219 * values here, to for example detect password mode, auto complete text views,
220 * phone number entry, etc.</p>
221 *
222 * <p>When the user switches between input targets, you will receive calls to
223 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
224 * You can use these to reset and initialize your input state for the current
225 * target. For example, you will often want to clear any input state, and
226 * update a soft keyboard to be appropriate for the new inputType.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -0700227 *
228 * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
229 * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
230 * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 */
232public class InputMethodService extends AbstractInputMethodService {
233 static final String TAG = "InputMethodService";
234 static final boolean DEBUG = false;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800235
236 /**
237 * The back button will close the input window.
238 */
239 public static final int BACK_DISPOSITION_DEFAULT = 0; // based on window
240
241 /**
242 * This input method will not consume the back key.
243 */
244 public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // back
245
246 /**
247 * This input method will consume the back key.
248 */
249 public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // down
250
251 /**
252 * @hide
253 * The IME is active. It may or may not be visible.
254 */
255 public static final int IME_ACTIVE = 0x1;
256
257 /**
258 * @hide
259 * The IME is visible.
260 */
261 public static final int IME_VISIBLE = 0x2;
262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 InputMethodManager mImm;
264
Dianne Hackbornd922ae02011-01-14 11:43:24 -0800265 int mTheme = 0;
Dianne Hackborn836531b2012-08-01 19:00:38 -0700266 boolean mHardwareAccelerated = false;
The Android Open Source Project10592532009-03-18 17:39:46 -0700267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 LayoutInflater mInflater;
The Android Open Source Project10592532009-03-18 17:39:46 -0700269 TypedArray mThemeAttrs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 View mRootView;
271 SoftInputWindow mWindow;
272 boolean mInitialized;
273 boolean mWindowCreated;
274 boolean mWindowAdded;
275 boolean mWindowVisible;
The Android Open Source Project10592532009-03-18 17:39:46 -0700276 boolean mWindowWasVisible;
277 boolean mInShowWindow;
278 ViewGroup mFullscreenArea;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 FrameLayout mExtractFrame;
280 FrameLayout mCandidatesFrame;
281 FrameLayout mInputFrame;
282
283 IBinder mToken;
284
285 InputBinding mInputBinding;
286 InputConnection mInputConnection;
287 boolean mInputStarted;
288 boolean mInputViewStarted;
289 boolean mCandidatesViewStarted;
290 InputConnection mStartedInputConnection;
291 EditorInfo mInputEditorInfo;
292
293 int mShowInputFlags;
294 boolean mShowInputRequested;
295 boolean mLastShowInputRequested;
296 int mCandidatesVisibility;
297 CompletionInfo[] mCurCompletions;
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700298
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 boolean mFullscreenApplied;
300 boolean mIsFullscreen;
301 View mExtractView;
The Android Open Source Project10592532009-03-18 17:39:46 -0700302 boolean mExtractViewHidden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 ExtractEditText mExtractEditText;
304 ViewGroup mExtractAccessories;
305 Button mExtractAction;
306 ExtractedText mExtractedText;
307 int mExtractedToken;
308
309 View mInputView;
310 boolean mIsInputViewShown;
311
312 int mStatusIcon;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800313 int mBackDisposition;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314
Yohei Yukawa2977eb72015-05-27 18:54:18 -0700315 /**
316 * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
317 * have not shown our own window yet. In this situation, the previous inset continues to be
318 * shown as an empty region until it is explicitly updated. Basically we can trigger the update
319 * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}.
320 */
321 boolean mShouldClearInsetOfPreviousIme;
322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 final Insets mTmpInsets = new Insets();
324 final int[] mTmpLocation = new int[2];
satokab751aa2010-09-14 19:17:36 +0900325
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
327 new ViewTreeObserver.OnComputeInternalInsetsListener() {
328 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700329 if (isExtractViewShown()) {
330 // In true fullscreen mode, we just say the window isn't covering
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 // any content so we don't impact whatever is behind.
332 View decor = getWindow().getWindow().getDecorView();
333 info.contentInsets.top = info.visibleInsets.top
334 = decor.getHeight();
Jeff Brownfbf09772011-01-16 14:06:57 -0800335 info.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
337 } else {
338 onComputeInsets(mTmpInsets);
339 info.contentInsets.top = mTmpInsets.contentTopInsets;
340 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -0800341 info.touchableRegion.set(mTmpInsets.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 info.setTouchableInsets(mTmpInsets.touchableInsets);
343 }
344 }
345 };
346
347 final View.OnClickListener mActionClickListener = new View.OnClickListener() {
348 public void onClick(View v) {
349 final EditorInfo ei = getCurrentInputEditorInfo();
350 final InputConnection ic = getCurrentInputConnection();
351 if (ei != null && ic != null) {
352 if (ei.actionId != 0) {
353 ic.performEditorAction(ei.actionId);
354 } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION)
355 != EditorInfo.IME_ACTION_NONE) {
356 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
357 }
358 }
359 }
360 };
361
362 /**
363 * Concrete implementation of
364 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
365 * all of the standard behavior for an input method.
366 */
367 public class InputMethodImpl extends AbstractInputMethodImpl {
368 /**
369 * Take care of attaching the given window token provided by the system.
370 */
371 public void attachToken(IBinder token) {
372 if (mToken == null) {
373 mToken = token;
374 mWindow.setToken(token);
375 }
376 }
377
378 /**
379 * Handle a new input binding, calling
380 * {@link InputMethodService#onBindInput InputMethodService.onBindInput()}
381 * when done.
382 */
383 public void bindInput(InputBinding binding) {
384 mInputBinding = binding;
385 mInputConnection = binding.getConnection();
386 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
387 + " ic=" + mInputConnection);
388 InputConnection ic = getCurrentInputConnection();
389 if (ic != null) ic.reportFullscreenMode(mIsFullscreen);
390 initialize();
391 onBindInput();
392 }
393
394 /**
395 * Clear the current input binding.
396 */
397 public void unbindInput() {
398 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
399 + " ic=" + mInputConnection);
400 onUnbindInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 mInputBinding = null;
402 mInputConnection = null;
403 }
404
405 public void startInput(InputConnection ic, EditorInfo attribute) {
406 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
407 doStartInput(ic, attribute, false);
408 }
409
410 public void restartInput(InputConnection ic, EditorInfo attribute) {
411 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
412 doStartInput(ic, attribute, true);
413 }
414
415 /**
416 * Handle a request by the system to hide the soft input area.
417 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800418 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 if (DEBUG) Log.v(TAG, "hideSoftInput()");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800420 boolean wasVis = isInputViewShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 mShowInputFlags = 0;
422 mShowInputRequested = false;
satok2f913d92012-05-10 01:48:03 +0900423 doHideWindow();
Yohei Yukawa2977eb72015-05-27 18:54:18 -0700424 clearInsetOfPreviousIme();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800425 if (resultReceiver != null) {
426 resultReceiver.send(wasVis != isInputViewShown()
427 ? InputMethodManager.RESULT_HIDDEN
428 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
429 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
430 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 }
432
433 /**
434 * Handle a request by the system to show the soft input area.
435 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800436 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700437 if (DEBUG) Log.v(TAG, "showSoftInput()");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800438 boolean wasVis = isInputViewShown();
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700439 if (dispatchOnShowInputRequested(flags, false)) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700440 try {
441 showWindow(true);
442 } catch (BadTokenException e) {
Yohei Yukawa6fcbb562015-09-14 16:48:15 -0700443 // We have ignored BadTokenException here since Jelly Bean MR-2 (API Level 18).
444 // We could ignore BadTokenException in InputMethodService#showWindow() instead,
445 // but it may break assumptions for those who override #showWindow() that we can
446 // detect errors in #showWindow() by checking BadTokenException.
447 // TODO: Investigate its feasibility. Update JavaDoc of #showWindow() of
448 // whether it's OK to override #showWindow() or not.
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700449 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -0700451 clearInsetOfPreviousIme();
satok865b9772011-01-21 02:45:06 +0900452 // If user uses hard keyboard, IME button should always be shown.
jungheang.lee217fd292013-02-26 16:53:22 +0900453 boolean showing = isInputViewShown();
Joe Onorato857fd9b2011-01-27 15:08:35 -0800454 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
455 mBackDisposition);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800456 if (resultReceiver != null) {
457 resultReceiver.send(wasVis != isInputViewShown()
458 ? InputMethodManager.RESULT_SHOWN
459 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
460 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 }
satokab751aa2010-09-14 19:17:36 +0900463
464 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
465 onCurrentInputMethodSubtypeChanged(subtype);
466 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 }
satokab751aa2010-09-14 19:17:36 +0900468
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 /**
470 * Concrete implementation of
471 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
472 * all of the standard behavior for an input method session.
473 */
474 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
475 public void finishInput() {
476 if (!isEnabled()) {
477 return;
478 }
479 if (DEBUG) Log.v(TAG, "finishInput() in " + this);
480 doFinishInput();
481 }
482
483 /**
484 * Call {@link InputMethodService#onDisplayCompletions
485 * InputMethodService.onDisplayCompletions()}.
486 */
487 public void displayCompletions(CompletionInfo[] completions) {
488 if (!isEnabled()) {
489 return;
490 }
491 mCurCompletions = completions;
492 onDisplayCompletions(completions);
493 }
494
495 /**
496 * Call {@link InputMethodService#onUpdateExtractedText
497 * InputMethodService.onUpdateExtractedText()}.
498 */
499 public void updateExtractedText(int token, ExtractedText text) {
500 if (!isEnabled()) {
501 return;
502 }
503 onUpdateExtractedText(token, text);
504 }
505
506 /**
507 * Call {@link InputMethodService#onUpdateSelection
508 * InputMethodService.onUpdateSelection()}.
509 */
510 public void updateSelection(int oldSelStart, int oldSelEnd,
511 int newSelStart, int newSelEnd,
512 int candidatesStart, int candidatesEnd) {
513 if (!isEnabled()) {
514 return;
515 }
516 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
517 newSelStart, newSelEnd, candidatesStart, candidatesEnd);
518 }
satok863fcd62011-06-21 17:38:02 +0900519
520 @Override
521 public void viewClicked(boolean focusChanged) {
522 if (!isEnabled()) {
523 return;
524 }
525 InputMethodService.this.onViewClicked(focusChanged);
526 }
527
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 /**
529 * Call {@link InputMethodService#onUpdateCursor
530 * InputMethodService.onUpdateCursor()}.
531 */
532 public void updateCursor(Rect newCursor) {
533 if (!isEnabled()) {
534 return;
535 }
536 InputMethodService.this.onUpdateCursor(newCursor);
537 }
538
539 /**
540 * Call {@link InputMethodService#onAppPrivateCommand
541 * InputMethodService.onAppPrivateCommand()}.
542 */
543 public void appPrivateCommand(String action, Bundle data) {
544 if (!isEnabled()) {
545 return;
546 }
547 InputMethodService.this.onAppPrivateCommand(action, data);
548 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800549
550 /**
551 *
552 */
553 public void toggleSoftInput(int showFlags, int hideFlags) {
554 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
555 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900556
557 /**
558 * Call {@link InputMethodService#onUpdateCursorAnchorInfo
559 * InputMethodService.onUpdateCursorAnchorInfo()}.
560 */
561 public void updateCursorAnchorInfo(CursorAnchorInfo info) {
562 if (!isEnabled()) {
563 return;
564 }
565 InputMethodService.this.onUpdateCursorAnchorInfo(info);
566 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 }
568
569 /**
570 * Information about where interesting parts of the input method UI appear.
571 */
572 public static final class Insets {
573 /**
574 * This is the top part of the UI that is the main content. It is
575 * used to determine the basic space needed, to resize/pan the
576 * application behind. It is assumed that this inset does not
577 * change very much, since any change will cause a full resize/pan
578 * of the application behind. This value is relative to the top edge
579 * of the input method window.
580 */
581 public int contentTopInsets;
582
583 /**
584 * This is the top part of the UI that is visibly covering the
585 * application behind it. This provides finer-grained control over
586 * visibility, allowing you to change it relatively frequently (such
587 * as hiding or showing candidates) without disrupting the underlying
588 * UI too much. For example, this will never resize the application
589 * UI, will only pan if needed to make the current focus visible, and
590 * will not aggressively move the pan position when this changes unless
591 * needed to make the focus visible. This value is relative to the top edge
592 * of the input method window.
593 */
594 public int visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -0800595
596 /**
597 * This is the region of the UI that is touchable. It is used when
598 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
599 * The region should be specified relative to the origin of the window frame.
600 */
601 public final Region touchableRegion = new Region();
602
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 /**
604 * Option for {@link #touchableInsets}: the entire window frame
605 * can be touched.
606 */
607 public static final int TOUCHABLE_INSETS_FRAME
608 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
609
610 /**
611 * Option for {@link #touchableInsets}: the area inside of
612 * the content insets can be touched.
613 */
614 public static final int TOUCHABLE_INSETS_CONTENT
615 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
616
617 /**
618 * Option for {@link #touchableInsets}: the area inside of
619 * the visible insets can be touched.
620 */
621 public static final int TOUCHABLE_INSETS_VISIBLE
622 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -0800623
624 /**
625 * Option for {@link #touchableInsets}: the region specified by
626 * {@link #touchableRegion} can be touched.
627 */
628 public static final int TOUCHABLE_INSETS_REGION
629 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
630
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 /**
632 * Determine which area of the window is touchable by the user. May
633 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
Jeff Brownfbf09772011-01-16 14:06:57 -0800634 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
635 * or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 */
637 public int touchableInsets;
638 }
satok865b9772011-01-21 02:45:06 +0900639
The Android Open Source Project10592532009-03-18 17:39:46 -0700640 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -0800641 * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
642 *
643 * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
644 * Basically this functionality still needs to be considered as implementation details.</p>
645 */
646 @MainThread
647 private static final class SettingsObserver extends ContentObserver {
648 @Retention(RetentionPolicy.SOURCE)
649 @IntDef({
650 ShowImeWithHardKeyboardType.UNKNOWN,
651 ShowImeWithHardKeyboardType.FALSE,
652 ShowImeWithHardKeyboardType.TRUE,
653 })
654 private @interface ShowImeWithHardKeyboardType {
655 int UNKNOWN = 0;
656 int FALSE = 1;
657 int TRUE = 2;
658 }
659 @ShowImeWithHardKeyboardType
660 private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
661
662 private final InputMethodService mService;
663
664 private SettingsObserver(InputMethodService service) {
665 super(new Handler(service.getMainLooper()));
666 mService = service;
667 }
668
669 /**
670 * A factory method that internally enforces two-phase initialization to make sure that the
671 * object reference will not be escaped until the object is properly constructed.
672 *
673 * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread. Hence
674 * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
675 *
676 * @param service {@link InputMethodService} that needs to receive the callback.
677 * @return {@link SettingsObserver} that is already registered to
678 * {@link android.content.ContentResolver}. The caller must call
679 * {@link SettingsObserver#unregister()}.
680 */
681 public static SettingsObserver createAndRegister(InputMethodService service) {
682 final SettingsObserver observer = new SettingsObserver(service);
683 // The observer is properly constructed. Let's start accepting the event.
684 service.getContentResolver().registerContentObserver(
685 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
686 false, observer);
687 return observer;
688 }
689
690 void unregister() {
691 mService.getContentResolver().unregisterContentObserver(this);
692 }
693
694 private boolean shouldShowImeWithHardKeyboard() {
695 // Lazily initialize as needed.
696 if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
697 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
698 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
699 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
700 }
701 switch (mShowImeWithHardKeyboard) {
702 case ShowImeWithHardKeyboardType.TRUE:
703 return true;
704 case ShowImeWithHardKeyboardType.FALSE:
705 return false;
706 default:
707 Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
708 return false;
709 }
710 }
711
712 @Override
713 public void onChange(boolean selfChange, Uri uri) {
714 final Uri showImeWithHardKeyboardUri =
715 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
716 if (showImeWithHardKeyboardUri.equals(uri)) {
717 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
718 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
719 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
720 mService.updateInputViewShown();
721 }
722 }
723
724 @Override
725 public String toString() {
726 return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}";
727 }
728 }
729 private SettingsObserver mSettingsObserver;
730
731 /**
The Android Open Source Project10592532009-03-18 17:39:46 -0700732 * You can call this to customize the theme used by your IME's window.
733 * This theme should typically be one that derives from
734 * {@link android.R.style#Theme_InputMethod}, which is the default theme
735 * you will get. This must be set before {@link #onCreate}, so you
736 * will typically call it in your constructor with the resource ID
737 * of your custom theme.
738 */
satokab751aa2010-09-14 19:17:36 +0900739 @Override
The Android Open Source Project10592532009-03-18 17:39:46 -0700740 public void setTheme(int theme) {
741 if (mWindow != null) {
742 throw new IllegalStateException("Must be called before onCreate()");
743 }
744 mTheme = theme;
745 }
satok865b9772011-01-21 02:45:06 +0900746
Dianne Hackborn836531b2012-08-01 19:00:38 -0700747 /**
748 * You can call this to try to enable hardware accelerated drawing for
749 * your IME. This must be set before {@link #onCreate}, so you
750 * will typically call it in your constructor. It is not always possible
Alan Viverettee07b5952014-08-13 19:13:54 -0700751 * to use hardware accelerated drawing in an IME (for example on low-end
Dianne Hackborn836531b2012-08-01 19:00:38 -0700752 * devices that do not have the resources to support this), so the call
753 * returns true if it succeeds otherwise false if you will need to draw
754 * in software. You must be able to handle either case.
Alan Viverettee07b5952014-08-13 19:13:54 -0700755 *
756 * @deprecated Starting in API 21, hardware acceleration is always enabled
757 * on capable devices.
Dianne Hackborn836531b2012-08-01 19:00:38 -0700758 */
759 public boolean enableHardwareAcceleration() {
760 if (mWindow != null) {
761 throw new IllegalStateException("Must be called before onCreate()");
762 }
Jeff Brown98365d72012-08-19 20:30:52 -0700763 if (ActivityManager.isHighEndGfx()) {
Dianne Hackborn836531b2012-08-01 19:00:38 -0700764 mHardwareAccelerated = true;
765 return true;
766 }
767 return false;
768 }
769
Alan Viverette5effd7e2014-05-05 12:25:33 -0700770 @Override public void onCreate() {
771 mTheme = Resources.selectSystemTheme(mTheme,
772 getApplicationInfo().targetSdkVersion,
773 android.R.style.Theme_InputMethod,
774 android.R.style.Theme_Holo_InputMethod,
775 android.R.style.Theme_DeviceDefault_InputMethod,
776 android.R.style.Theme_DeviceDefault_InputMethod);
The Android Open Source Project10592532009-03-18 17:39:46 -0700777 super.setTheme(mTheme);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 super.onCreate();
779 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
Yohei Yukawa7b739a82015-12-21 13:30:44 -0800780 mSettingsObserver = SettingsObserver.createAndRegister(this);
Yohei Yukawa2977eb72015-05-27 18:54:18 -0700781 // If the previous IME has occupied non-empty inset in the screen, we need to decide whether
782 // we continue to use the same size of the inset or update it
783 mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 mInflater = (LayoutInflater)getSystemService(
785 Context.LAYOUT_INFLATER_SERVICE);
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700786 mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
Dianne Hackborne30e02f2014-05-27 18:24:45 -0700787 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
Dianne Hackborn836531b2012-08-01 19:00:38 -0700788 if (mHardwareAccelerated) {
789 mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
790 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 initViews();
Romain Guy980a9382010-01-08 15:06:28 -0800792 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
satokab751aa2010-09-14 19:17:36 +0900794
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 /**
796 * This is a hook that subclasses can use to perform initialization of
797 * their interface. It is called for you prior to any of your UI objects
798 * being created, both after the service is first created and after a
799 * configuration change happens.
800 */
801 public void onInitializeInterface() {
Gilles Debunne34703b62011-09-08 11:16:25 -0700802 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 }
satokab751aa2010-09-14 19:17:36 +0900804
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 void initialize() {
806 if (!mInitialized) {
807 mInitialized = true;
808 onInitializeInterface();
809 }
810 }
satokab751aa2010-09-14 19:17:36 +0900811
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 void initViews() {
813 mInitialized = false;
814 mWindowCreated = false;
815 mShowInputRequested = false;
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700816 mShowInputFlags = 0;
817
The Android Open Source Project10592532009-03-18 17:39:46 -0700818 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 mRootView = mInflater.inflate(
820 com.android.internal.R.layout.input_method, null);
John Spurlockc68d5772013-10-08 11:47:58 -0400821 mRootView.setSystemUiVisibility(
822 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 mWindow.setContentView(mRootView);
Seonggoo Kang72745ff2014-12-24 13:55:50 +0900824 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
Jeff Sharkey6e2bee72012-10-01 13:39:08 -0700826 if (Settings.Global.getInt(getContentResolver(),
827 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 mWindow.getWindow().setWindowAnimations(
829 com.android.internal.R.style.Animation_InputMethodFancy);
830 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700831 mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
832 mExtractViewHidden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
834 mExtractView = null;
835 mExtractEditText = null;
836 mExtractAccessories = null;
837 mExtractAction = null;
838 mFullscreenApplied = false;
839
840 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
841 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
842 mInputView = null;
843 mIsInputViewShown = false;
844
845 mExtractFrame.setVisibility(View.GONE);
846 mCandidatesVisibility = getCandidatesHiddenVisibility();
847 mCandidatesFrame.setVisibility(mCandidatesVisibility);
848 mInputFrame.setVisibility(View.GONE);
849 }
satokab751aa2010-09-14 19:17:36 +0900850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 @Override public void onDestroy() {
852 super.onDestroy();
853 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
854 mInsetsComputer);
Satoshi Kataokac56191f2013-05-30 13:14:47 +0900855 doFinishInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 if (mWindowAdded) {
satokc0c87652011-09-12 12:08:05 +0900857 // Disable exit animation for the current IME window
858 // to avoid the race condition between the exit and enter animations
859 // when the current IME is being switched to another one.
860 mWindow.getWindow().setWindowAnimations(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 mWindow.dismiss();
862 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -0800863 if (mSettingsObserver != null) {
864 mSettingsObserver.unregister();
865 mSettingsObserver = null;
866 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 }
satokf17db9f2011-09-14 18:55:58 +0900868
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 /**
870 * Take care of handling configuration changes. Subclasses of
871 * InputMethodService generally don't need to deal directly with
872 * this on their own; the standard implementation here takes care of
873 * regenerating the input method UI as a result of the configuration
874 * change, so you can rely on your {@link #onCreateInputView} and
875 * other methods being called as appropriate due to a configuration change.
876 *
877 * <p>When a configuration change does happen,
878 * {@link #onInitializeInterface()} is guaranteed to be called the next
879 * time prior to any of the other input or UI creation callbacks. The
880 * following will be called immediately depending if appropriate for current
881 * state: {@link #onStartInput} if input is active, and
882 * {@link #onCreateInputView} and {@link #onStartInputView} and related
883 * appropriate functions if the UI is displayed.
884 */
885 @Override public void onConfigurationChanged(Configuration newConfig) {
886 super.onConfigurationChanged(newConfig);
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700887
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 boolean visible = mWindowVisible;
889 int showFlags = mShowInputFlags;
890 boolean showingInput = mShowInputRequested;
891 CompletionInfo[] completions = mCurCompletions;
892 initViews();
893 mInputViewStarted = false;
894 mCandidatesViewStarted = false;
895 if (mInputStarted) {
896 doStartInput(getCurrentInputConnection(),
897 getCurrentInputEditorInfo(), true);
898 }
899 if (visible) {
900 if (showingInput) {
901 // If we were last showing the soft keyboard, try to do so again.
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700902 if (dispatchOnShowInputRequested(showFlags, true)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 showWindow(true);
904 if (completions != null) {
905 mCurCompletions = completions;
906 onDisplayCompletions(completions);
907 }
908 } else {
satok2f913d92012-05-10 01:48:03 +0900909 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 }
911 } else if (mCandidatesVisibility == View.VISIBLE) {
912 // If the candidates are currently visible, make sure the
913 // window is shown for them.
914 showWindow(false);
915 } else {
916 // Otherwise hide the window.
satok2f913d92012-05-10 01:48:03 +0900917 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 }
satok865b9772011-01-21 02:45:06 +0900919 // If user uses hard keyboard, IME button should always be shown.
Joe Onorato857fd9b2011-01-27 15:08:35 -0800920 boolean showing = onEvaluateInputViewShown();
921 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
922 mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 }
924 }
925
926 /**
927 * Implement to return our standard {@link InputMethodImpl}. Subclasses
928 * can override to provide their own customized version.
929 */
satokab751aa2010-09-14 19:17:36 +0900930 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 public AbstractInputMethodImpl onCreateInputMethodInterface() {
932 return new InputMethodImpl();
933 }
934
935 /**
936 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
937 * can override to provide their own customized version.
938 */
satokab751aa2010-09-14 19:17:36 +0900939 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
941 return new InputMethodSessionImpl();
942 }
943
944 public LayoutInflater getLayoutInflater() {
945 return mInflater;
946 }
947
948 public Dialog getWindow() {
949 return mWindow;
950 }
951
Joe Onorato857fd9b2011-01-27 15:08:35 -0800952 public void setBackDisposition(int disposition) {
953 mBackDisposition = disposition;
954 }
955
956 public int getBackDisposition() {
957 return mBackDisposition;
958 }
959
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 /**
961 * Return the maximum width, in pixels, available the input method.
962 * Input methods are positioned at the bottom of the screen and, unless
963 * running in fullscreen, will generally want to be as short as possible
964 * so should compute their height based on their contents. However, they
965 * can stretch as much as needed horizontally. The function returns to
966 * you the maximum amount of space available horizontally, which you can
967 * use if needed for UI placement.
968 *
969 * <p>In many cases this is not needed, you can just rely on the normal
970 * view layout mechanisms to position your views within the full horizontal
971 * space given to the input method.
972 *
973 * <p>Note that this value can change dynamically, in particular when the
974 * screen orientation changes.
975 */
976 public int getMaxWidth() {
977 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
978 return wm.getDefaultDisplay().getWidth();
979 }
980
981 /**
982 * Return the currently active InputBinding for the input method, or
983 * null if there is none.
984 */
985 public InputBinding getCurrentInputBinding() {
986 return mInputBinding;
987 }
988
989 /**
990 * Retrieve the currently active InputConnection that is bound to
991 * the input method, or null if there is none.
992 */
993 public InputConnection getCurrentInputConnection() {
994 InputConnection ic = mStartedInputConnection;
995 if (ic != null) {
996 return ic;
997 }
998 return mInputConnection;
999 }
1000
1001 public boolean getCurrentInputStarted() {
1002 return mInputStarted;
1003 }
1004
1005 public EditorInfo getCurrentInputEditorInfo() {
1006 return mInputEditorInfo;
1007 }
1008
1009 /**
1010 * Re-evaluate whether the input method should be running in fullscreen
1011 * mode, and update its UI if this has changed since the last time it
1012 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to
1013 * determine whether it should currently run in fullscreen mode. You
1014 * can use {@link #isFullscreenMode()} to determine if the input method
1015 * is currently running in fullscreen mode.
1016 */
1017 public void updateFullscreenMode() {
1018 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
1019 boolean changed = mLastShowInputRequested != mShowInputRequested;
1020 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
1021 changed = true;
1022 mIsFullscreen = isFullscreen;
1023 InputConnection ic = getCurrentInputConnection();
1024 if (ic != null) ic.reportFullscreenMode(isFullscreen);
1025 mFullscreenApplied = true;
1026 initialize();
The Android Open Source Project10592532009-03-18 17:39:46 -07001027 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1028 mFullscreenArea.getLayoutParams();
1029 if (isFullscreen) {
1030 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
1031 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
1032 lp.height = 0;
1033 lp.weight = 1;
1034 } else {
1035 mFullscreenArea.setBackgroundDrawable(null);
1036 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
1037 lp.weight = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001039 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
1040 mFullscreenArea, lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 if (isFullscreen) {
1042 if (mExtractView == null) {
1043 View v = onCreateExtractTextView();
1044 if (v != null) {
1045 setExtractView(v);
1046 }
1047 }
1048 startExtractingText(false);
1049 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001050 updateExtractFrameVisibility();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051 }
1052
1053 if (changed) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001054 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 mLastShowInputRequested = mShowInputRequested;
1056 }
1057 }
1058
1059 /**
1060 * Update the given window's parameters for the given mode. This is called
1061 * when the window is first displayed and each time the fullscreen or
1062 * candidates only mode changes.
1063 *
1064 * <p>The default implementation makes the layout for the window
Romain Guy980a9382010-01-08 15:06:28 -08001065 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
1066 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 *
1068 * @param win The input method's window.
1069 * @param isFullscreen If true, the window is running in fullscreen mode
1070 * and intended to cover the entire application display.
1071 * @param isCandidatesOnly If true, the window is only showing the
1072 * candidates view and none of the rest of its UI. This is mutually
1073 * exclusive with fullscreen mode.
1074 */
1075 public void onConfigureWindow(Window win, boolean isFullscreen,
1076 boolean isCandidatesOnly) {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001077 final int currentHeight = mWindow.getWindow().getAttributes().height;
1078 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
1079 if (mIsInputViewShown && currentHeight != newHeight) {
1080 Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: "
1081 + currentHeight + " -> " + newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 }
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001083 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 }
1085
1086 /**
1087 * Return whether the input method is <em>currently</em> running in
1088 * fullscreen mode. This is the mode that was last determined and
1089 * applied by {@link #updateFullscreenMode()}.
1090 */
1091 public boolean isFullscreenMode() {
1092 return mIsFullscreen;
1093 }
1094
1095 /**
1096 * Override this to control when the input method should run in
1097 * fullscreen mode. The default implementation runs in fullsceen only
1098 * when the screen is in landscape mode. If you change what
1099 * this returns, you will need to call {@link #updateFullscreenMode()}
1100 * yourself whenever the returned value may have changed to have it
1101 * re-evaluated and applied.
1102 */
1103 public boolean onEvaluateFullscreenMode() {
1104 Configuration config = getResources().getConfiguration();
Leon Scroggins2edd6822010-01-12 11:36:13 -05001105 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
1106 return false;
1107 }
1108 if (mInputEditorInfo != null
1109 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
1110 return false;
1111 }
1112 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 }
Gilles Debunne34703b62011-09-08 11:16:25 -07001114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001116 * Controls the visibility of the extracted text area. This only applies
1117 * when the input method is in fullscreen mode, and thus showing extracted
1118 * text. When false, the extracted text will not be shown, allowing some
1119 * of the application to be seen behind. This is normally set for you
1120 * by {@link #onUpdateExtractingVisibility}. This controls the visibility
1121 * of both the extracted text and candidate view; the latter since it is
1122 * not useful if there is no text to see.
1123 */
1124 public void setExtractViewShown(boolean shown) {
1125 if (mExtractViewHidden == shown) {
1126 mExtractViewHidden = !shown;
1127 updateExtractFrameVisibility();
1128 }
1129 }
1130
1131 /**
1132 * Return whether the fullscreen extract view is shown. This will only
1133 * return true if {@link #isFullscreenMode()} returns true, and in that
1134 * case its value depends on the last call to
1135 * {@link #setExtractViewShown(boolean)}. This effectively lets you
1136 * determine if the application window is entirely covered (when this
1137 * returns true) or if some part of it may be shown (if this returns
1138 * false, though if {@link #isFullscreenMode()} returns true in that case
1139 * then it is probably only a sliver of the application).
1140 */
1141 public boolean isExtractViewShown() {
1142 return mIsFullscreen && !mExtractViewHidden;
1143 }
1144
1145 void updateExtractFrameVisibility() {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001146 final int vis;
The Android Open Source Project10592532009-03-18 17:39:46 -07001147 if (isFullscreenMode()) {
1148 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001149 // "vis" should be applied for the extract frame as well in the fullscreen mode.
1150 mExtractFrame.setVisibility(vis);
The Android Open Source Project10592532009-03-18 17:39:46 -07001151 } else {
1152 vis = View.VISIBLE;
1153 mExtractFrame.setVisibility(View.GONE);
1154 }
1155 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
1156 if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
1157 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1158 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1159 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1160 0);
1161 if (animRes != 0) {
1162 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1163 this, animRes));
1164 }
1165 }
1166 mFullscreenArea.setVisibility(vis);
1167 }
1168
1169 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 * Compute the interesting insets into your UI. The default implementation
1171 * uses the top of the candidates frame for the visible insets, and the
1172 * top of the input frame for the content insets. The default touchable
1173 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1174 *
The Android Open Source Project10592532009-03-18 17:39:46 -07001175 * <p>Note that this method is not called when
1176 * {@link #isExtractViewShown} returns true, since
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 * in that case the application is left as-is behind the input method and
1178 * not impacted by anything in its UI.
1179 *
1180 * @param outInsets Fill in with the current UI insets.
1181 */
1182 public void onComputeInsets(Insets outInsets) {
1183 int[] loc = mTmpLocation;
1184 if (mInputFrame.getVisibility() == View.VISIBLE) {
1185 mInputFrame.getLocationInWindow(loc);
1186 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001187 View decor = getWindow().getWindow().getDecorView();
1188 loc[1] = decor.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001190 if (isFullscreenMode()) {
1191 // In fullscreen mode, we never resize the underlying window.
1192 View decor = getWindow().getWindow().getDecorView();
1193 outInsets.contentTopInsets = decor.getHeight();
1194 } else {
1195 outInsets.contentTopInsets = loc[1];
1196 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1198 mCandidatesFrame.getLocationInWindow(loc);
1199 }
1200 outInsets.visibleTopInsets = loc[1];
1201 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001202 outInsets.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 }
1204
1205 /**
1206 * Re-evaluate whether the soft input area should currently be shown, and
1207 * update its UI if this has changed since the last time it
1208 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to
1209 * determine whether the input view should currently be shown. You
1210 * can use {@link #isInputViewShown()} to determine if the input view
1211 * is currently shown.
1212 */
1213 public void updateInputViewShown() {
1214 boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
1215 if (mIsInputViewShown != isShown && mWindowVisible) {
1216 mIsInputViewShown = isShown;
1217 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1218 if (mInputView == null) {
1219 initialize();
1220 View v = onCreateInputView();
1221 if (v != null) {
1222 setInputView(v);
1223 }
1224 }
1225 }
1226 }
1227
1228 /**
1229 * Returns true if we have been asked to show our input view.
1230 */
1231 public boolean isShowInputRequested() {
1232 return mShowInputRequested;
1233 }
1234
1235 /**
1236 * Return whether the soft input view is <em>currently</em> shown to the
1237 * user. This is the state that was last determined and
1238 * applied by {@link #updateInputViewShown()}.
1239 */
1240 public boolean isInputViewShown() {
1241 return mIsInputViewShown && mWindowVisible;
1242 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001245 * Override this to control when the soft input area should be shown to the user. The default
1246 * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
1247 * unless the user shows an intention to use software keyboard. If you change what this
1248 * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
1249 * value may have changed to have it re-evaluated and applied.
1250 *
1251 * <p>When you override this method, it is recommended to call
1252 * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
1253 * returned.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001254 */
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001255 @CallSuper
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 public boolean onEvaluateInputViewShown() {
Yohei Yukawacf8403b2016-01-12 11:54:58 -08001257 if (mSettingsObserver == null) {
1258 Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
1259 return false;
1260 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001261 if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
1262 return true;
1263 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 Configuration config = getResources().getConfiguration();
1265 return config.keyboard == Configuration.KEYBOARD_NOKEYS
Ken Wakasa8710e762011-01-30 11:02:09 +09001266 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001268
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 /**
1270 * Controls the visibility of the candidates display area. By default
1271 * it is hidden.
1272 */
1273 public void setCandidatesViewShown(boolean shown) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001274 updateCandidatesVisibility(shown);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 if (!mShowInputRequested && mWindowVisible != shown) {
1276 // If we are being asked to show the candidates view while the app
1277 // has not asked for the input view to be shown, then we need
1278 // to update whether the window is shown.
1279 if (shown) {
1280 showWindow(false);
1281 } else {
satok2f913d92012-05-10 01:48:03 +09001282 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 }
1284 }
1285 }
1286
The Android Open Source Project10592532009-03-18 17:39:46 -07001287 void updateCandidatesVisibility(boolean shown) {
1288 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1289 if (mCandidatesVisibility != vis) {
1290 mCandidatesFrame.setVisibility(vis);
1291 mCandidatesVisibility = vis;
1292 }
1293 }
1294
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 /**
1296 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1297 * or {@link View#GONE View.GONE}) of the candidates view when it is not
The Android Open Source Project10592532009-03-18 17:39:46 -07001298 * shown. The default implementation returns GONE when
1299 * {@link #isExtractViewShown} returns true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 * otherwise VISIBLE. Be careful if you change this to return GONE in
1301 * other situations -- if showing or hiding the candidates view causes
1302 * your window to resize, this can cause temporary drawing artifacts as
1303 * the resize takes place.
1304 */
1305 public int getCandidatesHiddenVisibility() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001306 return isExtractViewShown() ? View.GONE : View.INVISIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 }
1308
Tor Norbye7b9c9122013-05-30 16:48:33 -07001309 public void showStatusIcon(@DrawableRes int iconResId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 mStatusIcon = iconResId;
1311 mImm.showStatusIcon(mToken, getPackageName(), iconResId);
1312 }
1313
1314 public void hideStatusIcon() {
1315 mStatusIcon = 0;
1316 mImm.hideStatusIcon(mToken);
1317 }
1318
1319 /**
1320 * Force switch to a new input method, as identified by <var>id</var>. This
1321 * input method will be destroyed, and the requested one started on the
1322 * current input field.
1323 *
1324 * @param id Unique identifier of the new input method ot start.
1325 */
1326 public void switchInputMethod(String id) {
1327 mImm.setInputMethod(mToken, id);
1328 }
1329
1330 public void setExtractView(View view) {
1331 mExtractFrame.removeAllViews();
1332 mExtractFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001333 ViewGroup.LayoutParams.MATCH_PARENT,
1334 ViewGroup.LayoutParams.MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335 mExtractView = view;
1336 if (view != null) {
1337 mExtractEditText = (ExtractEditText)view.findViewById(
1338 com.android.internal.R.id.inputExtractEditText);
1339 mExtractEditText.setIME(this);
1340 mExtractAction = (Button)view.findViewById(
1341 com.android.internal.R.id.inputExtractAction);
1342 if (mExtractAction != null) {
1343 mExtractAccessories = (ViewGroup)view.findViewById(
1344 com.android.internal.R.id.inputExtractAccessories);
1345 }
1346 startExtractingText(false);
1347 } else {
1348 mExtractEditText = null;
1349 mExtractAccessories = null;
1350 mExtractAction = null;
1351 }
1352 }
1353
1354 /**
1355 * Replaces the current candidates view with a new one. You only need to
1356 * call this when dynamically changing the view; normally, you should
1357 * implement {@link #onCreateCandidatesView()} and create your view when
1358 * first needed by the input method.
1359 */
1360 public void setCandidatesView(View view) {
1361 mCandidatesFrame.removeAllViews();
1362 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001363 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001364 ViewGroup.LayoutParams.WRAP_CONTENT));
1365 }
1366
1367 /**
1368 * Replaces the current input view with a new one. You only need to
1369 * call this when dynamically changing the view; normally, you should
1370 * implement {@link #onCreateInputView()} and create your view when
1371 * first needed by the input method.
1372 */
1373 public void setInputView(View view) {
1374 mInputFrame.removeAllViews();
1375 mInputFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001376 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 ViewGroup.LayoutParams.WRAP_CONTENT));
1378 mInputView = view;
1379 }
1380
1381 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 * Called by the framework to create the layout for showing extacted text.
1383 * Only called when in fullscreen mode. The returned view hierarchy must
1384 * have an {@link ExtractEditText} whose ID is
1385 * {@link android.R.id#inputExtractEditText}.
1386 */
1387 public View onCreateExtractTextView() {
1388 return mInflater.inflate(
1389 com.android.internal.R.layout.input_method_extract_view, null);
1390 }
1391
1392 /**
1393 * Create and return the view hierarchy used to show candidates. This will
1394 * be called once, when the candidates are first displayed. You can return
1395 * null to have no candidates view; the default implementation returns null.
1396 *
1397 * <p>To control when the candidates view is displayed, use
1398 * {@link #setCandidatesViewShown(boolean)}.
1399 * To change the candidates view after the first one is created by this
1400 * function, use {@link #setCandidatesView(View)}.
1401 */
1402 public View onCreateCandidatesView() {
1403 return null;
1404 }
1405
1406 /**
1407 * Create and return the view hierarchy used for the input area (such as
1408 * a soft keyboard). This will be called once, when the input area is
1409 * first displayed. You can return null to have no input area; the default
1410 * implementation returns null.
1411 *
1412 * <p>To control when the input view is displayed, implement
1413 * {@link #onEvaluateInputViewShown()}.
1414 * To change the input view after the first one is created by this
1415 * function, use {@link #setInputView(View)}.
1416 */
1417 public View onCreateInputView() {
1418 return null;
1419 }
1420
1421 /**
1422 * Called when the input view is being shown and input has started on
1423 * a new editor. This will always be called after {@link #onStartInput},
1424 * allowing you to do your general setup there and just view-specific
1425 * setup here. You are guaranteed that {@link #onCreateInputView()} will
1426 * have been called some time before this function is called.
1427 *
1428 * @param info Description of the type of text being edited.
1429 * @param restarting Set to true if we are restarting input on the
1430 * same text field as before.
1431 */
1432 public void onStartInputView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001433 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001434 }
1435
1436 /**
1437 * Called when the input view is being hidden from the user. This will
1438 * be called either prior to hiding the window, or prior to switching to
1439 * another target for editing.
1440 *
1441 * <p>The default
1442 * implementation uses the InputConnection to clear any active composing
1443 * text; you can override this (not calling the base class implementation)
1444 * to perform whatever behavior you would like.
1445 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001446 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001447 * called immediately after.
1448 */
1449 public void onFinishInputView(boolean finishingInput) {
1450 if (!finishingInput) {
1451 InputConnection ic = getCurrentInputConnection();
1452 if (ic != null) {
1453 ic.finishComposingText();
1454 }
1455 }
1456 }
1457
1458 /**
1459 * Called when only the candidates view has been shown for showing
1460 * processing as the user enters text through a hard keyboard.
1461 * This will always be called after {@link #onStartInput},
1462 * allowing you to do your general setup there and just view-specific
1463 * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
1464 * will have been called some time before this function is called.
1465 *
1466 * <p>Note that this will <em>not</em> be called when the input method
1467 * is running in full editing mode, and thus receiving
1468 * {@link #onStartInputView} to initiate that operation. This is only
1469 * for the case when candidates are being shown while the input method
1470 * editor is hidden but wants to show its candidates UI as text is
1471 * entered through some other mechanism.
1472 *
1473 * @param info Description of the type of text being edited.
1474 * @param restarting Set to true if we are restarting input on the
1475 * same text field as before.
1476 */
1477 public void onStartCandidatesView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001478 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 }
1480
1481 /**
1482 * Called when the candidates view is being hidden from the user. This will
1483 * be called either prior to hiding the window, or prior to switching to
1484 * another target for editing.
1485 *
1486 * <p>The default
1487 * implementation uses the InputConnection to clear any active composing
1488 * text; you can override this (not calling the base class implementation)
1489 * to perform whatever behavior you would like.
1490 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001491 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 * called immediately after.
1493 */
1494 public void onFinishCandidatesView(boolean finishingInput) {
1495 if (!finishingInput) {
1496 InputConnection ic = getCurrentInputConnection();
1497 if (ic != null) {
1498 ic.finishComposingText();
1499 }
1500 }
1501 }
1502
1503 /**
1504 * The system has decided that it may be time to show your input method.
1505 * This is called due to a corresponding call to your
The Android Open Source Project4df24232009-03-05 14:34:35 -08001506 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 * method. The default implementation uses
1508 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
1509 * and the current configuration to decide whether the input view should
1510 * be shown at this point.
1511 *
1512 * @param flags Provides additional information about the show request,
The Android Open Source Project4df24232009-03-05 14:34:35 -08001513 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001514 * @param configChange This is true if we are re-showing due to a
1515 * configuration change.
1516 * @return Returns true to indicate that the window should be shown.
1517 */
1518 public boolean onShowInputRequested(int flags, boolean configChange) {
1519 if (!onEvaluateInputViewShown()) {
1520 return false;
1521 }
1522 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
1523 if (!configChange && onEvaluateFullscreenMode()) {
1524 // Don't show if this is not explicitly requested by the user and
1525 // the input method is fullscreen. That would be too disruptive.
1526 // However, we skip this change for a config change, since if
1527 // the IME is already shown we do want to go into fullscreen
1528 // mode at this point.
1529 return false;
1530 }
1531 Configuration config = getResources().getConfiguration();
1532 if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
1533 // And if the device has a hard keyboard, even if it is
1534 // currently hidden, don't show the input method implicitly.
1535 // These kinds of devices don't need it that much.
1536 return false;
1537 }
1538 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 return true;
1540 }
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001541
1542 /**
1543 * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal
1544 * states depending on its result. Since {@link #onShowInputRequested(int, boolean)} is
1545 * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
1546 * to have this method to ensure that those internal states are always updated no matter how
1547 * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
1548 * @param flags Provides additional information about the show request,
1549 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
1550 * @param configChange This is true if we are re-showing due to a
1551 * configuration change.
1552 * @return Returns true to indicate that the window should be shown.
1553 * @see #onShowInputRequested(int, boolean)
1554 */
1555 private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
1556 final boolean result = onShowInputRequested(flags, configChange);
1557 if (result) {
1558 mShowInputFlags = flags;
1559 } else {
1560 mShowInputFlags = 0;
1561 }
1562 return result;
1563 }
1564
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001565 public void showWindow(boolean showInput) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07001566 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 + " mShowInputRequested=" + mShowInputRequested
1568 + " mWindowAdded=" + mWindowAdded
1569 + " mWindowCreated=" + mWindowCreated
1570 + " mWindowVisible=" + mWindowVisible
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001571 + " mInputStarted=" + mInputStarted
1572 + " mShowInputFlags=" + mShowInputFlags);
1573
The Android Open Source Project10592532009-03-18 17:39:46 -07001574 if (mInShowWindow) {
1575 Log.w(TAG, "Re-entrance in to showWindow");
1576 return;
1577 }
1578
1579 try {
1580 mWindowWasVisible = mWindowVisible;
1581 mInShowWindow = true;
1582 showWindowInner(showInput);
Yohei Yukawa6fcbb562015-09-14 16:48:15 -07001583 } catch (BadTokenException e) {
1584 // BadTokenException is a normal consequence in certain situations, e.g., swapping IMEs
1585 // while there is a DO_SHOW_SOFT_INPUT message in the IIMethodWrapper queue.
1586 if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
1587 mWindowVisible = false;
1588 mWindowAdded = false;
1589 // Rethrow the exception to preserve the existing behavior. Some IMEs may have directly
1590 // called this method and relied on this exception for some clean-up tasks.
1591 // TODO: Give developers a clear guideline of whether it's OK to call this method or
1592 // InputMethodManager#showSoftInputFromInputMethod() should always be used instead.
1593 throw e;
The Android Open Source Project10592532009-03-18 17:39:46 -07001594 } finally {
Yohei Yukawa6fcbb562015-09-14 16:48:15 -07001595 // TODO: Is it OK to set true when we get BadTokenException?
The Android Open Source Project10592532009-03-18 17:39:46 -07001596 mWindowWasVisible = true;
1597 mInShowWindow = false;
1598 }
1599 }
satok06487a52010-10-29 11:37:18 +09001600
The Android Open Source Project10592532009-03-18 17:39:46 -07001601 void showWindowInner(boolean showInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001602 boolean doShowInput = false;
Seigo Nonaka98d88022015-04-15 18:31:32 +09001603 final int previousImeWindowStatus =
1604 (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 mWindowVisible = true;
Yohei Yukawaac8bdd22015-09-11 18:17:11 -07001606 if (!mShowInputRequested && mInputStarted && showInput) {
1607 doShowInput = true;
1608 mShowInputRequested = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 }
satok06487a52010-10-29 11:37:18 +09001610
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611 if (DEBUG) Log.v(TAG, "showWindow: updating UI");
1612 initialize();
1613 updateFullscreenMode();
1614 updateInputViewShown();
1615
1616 if (!mWindowAdded || !mWindowCreated) {
1617 mWindowAdded = true;
1618 mWindowCreated = true;
1619 initialize();
1620 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
1621 View v = onCreateCandidatesView();
1622 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
1623 if (v != null) {
1624 setCandidatesView(v);
1625 }
1626 }
1627 if (mShowInputRequested) {
1628 if (!mInputViewStarted) {
1629 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1630 mInputViewStarted = true;
1631 onStartInputView(mInputEditorInfo, false);
1632 }
1633 } else if (!mCandidatesViewStarted) {
1634 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1635 mCandidatesViewStarted = true;
1636 onStartCandidatesView(mInputEditorInfo, false);
1637 }
1638
1639 if (doShowInput) {
1640 startExtractingText(false);
1641 }
satok06487a52010-10-29 11:37:18 +09001642
Seigo Nonaka98d88022015-04-15 18:31:32 +09001643 final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
1644 if (previousImeWindowStatus != nextImeWindowStatus) {
1645 mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
1646 }
1647 if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 if (DEBUG) Log.v(TAG, "showWindow: showing!");
1649 onWindowShown();
1650 mWindow.show();
Yohei Yukawa2977eb72015-05-27 18:54:18 -07001651 // Put here rather than in onWindowShown() in case people forget to call
1652 // super.onWindowShown().
1653 mShouldClearInsetOfPreviousIme = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 }
1655 }
satok06487a52010-10-29 11:37:18 +09001656
satokf17db9f2011-09-14 18:55:58 +09001657 private void finishViews() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 if (mInputViewStarted) {
1659 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1660 onFinishInputView(false);
1661 } else if (mCandidatesViewStarted) {
1662 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1663 onFinishCandidatesView(false);
1664 }
1665 mInputViewStarted = false;
1666 mCandidatesViewStarted = false;
satokf17db9f2011-09-14 18:55:58 +09001667 }
1668
satok2f913d92012-05-10 01:48:03 +09001669 private void doHideWindow() {
1670 mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
1671 hideWindow();
1672 }
1673
satokf17db9f2011-09-14 18:55:58 +09001674 public void hideWindow() {
1675 finishViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 if (mWindowVisible) {
1677 mWindow.hide();
1678 mWindowVisible = false;
1679 onWindowHidden();
The Android Open Source Project10592532009-03-18 17:39:46 -07001680 mWindowWasVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001681 }
Seigo Nonaka93c47ea2015-07-14 15:05:04 +09001682 updateFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001683 }
satok06487a52010-10-29 11:37:18 +09001684
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 /**
1686 * Called when the input method window has been shown to the user, after
1687 * previously not being visible. This is done after all of the UI setup
1688 * for the window has occurred (creating its views etc).
1689 */
1690 public void onWindowShown() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001691 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001692 }
1693
1694 /**
1695 * Called when the input method window has been hidden from the user,
1696 * after previously being visible.
1697 */
1698 public void onWindowHidden() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001699 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001700 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -07001701
1702 /**
1703 * Reset the inset occupied the previous IME when and only when
1704 * {@link #mShouldClearInsetOfPreviousIme} is {@code true}.
1705 */
1706 private void clearInsetOfPreviousIme() {
1707 if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() "
1708 + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
1709 if (!mShouldClearInsetOfPreviousIme || mWindow == null) return;
Seigo Nonakae9372162015-06-04 17:46:27 +09001710 try {
1711 // We do not call onWindowShown() and onWindowHidden() so as not to make the IME author
1712 // confused.
1713 // TODO: Find out a better way which has less side-effect.
1714 mWindow.show();
1715 mWindow.hide();
1716 } catch (WindowManager.BadTokenException e) {
1717 if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme: BadTokenException: IME is done.");
1718 mWindowVisible = false;
1719 mWindowAdded = false;
1720 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -07001721 mShouldClearInsetOfPreviousIme = false;
1722 }
1723
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724 /**
1725 * Called when a new client has bound to the input method. This
1726 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
1727 * and {@link #onFinishInput()} calls as the user navigates through its
1728 * UI. Upon this call you know that {@link #getCurrentInputBinding}
1729 * and {@link #getCurrentInputConnection} return valid objects.
1730 */
1731 public void onBindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001732 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 }
1734
1735 /**
1736 * Called when the previous bound client is no longer associated
1737 * with the input method. After returning {@link #getCurrentInputBinding}
1738 * and {@link #getCurrentInputConnection} will no longer return
1739 * valid objects.
1740 */
1741 public void onUnbindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001742 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 }
1744
1745 /**
1746 * Called to inform the input method that text input has started in an
1747 * editor. You should use this callback to initialize the state of your
1748 * input to match the state of the editor given to it.
1749 *
1750 * @param attribute The attributes of the editor that input is starting
1751 * in.
1752 * @param restarting Set to true if input is restarting in the same
1753 * editor such as because the application has changed the text in
1754 * the editor. Otherwise will be false, indicating this is a new
1755 * session with the editor.
1756 */
1757 public void onStartInput(EditorInfo attribute, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001758 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001759 }
1760
1761 void doFinishInput() {
1762 if (mInputViewStarted) {
1763 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1764 onFinishInputView(true);
1765 } else if (mCandidatesViewStarted) {
1766 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1767 onFinishCandidatesView(true);
1768 }
1769 mInputViewStarted = false;
1770 mCandidatesViewStarted = false;
1771 if (mInputStarted) {
1772 if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
1773 onFinishInput();
1774 }
1775 mInputStarted = false;
1776 mStartedInputConnection = null;
1777 mCurCompletions = null;
1778 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001779
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001780 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
1781 if (!restarting) {
1782 doFinishInput();
1783 }
1784 mInputStarted = true;
1785 mStartedInputConnection = ic;
1786 mInputEditorInfo = attribute;
1787 initialize();
1788 if (DEBUG) Log.v(TAG, "CALL: onStartInput");
1789 onStartInput(attribute, restarting);
1790 if (mWindowVisible) {
1791 if (mShowInputRequested) {
1792 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1793 mInputViewStarted = true;
1794 onStartInputView(mInputEditorInfo, restarting);
1795 startExtractingText(true);
1796 } else if (mCandidatesVisibility == View.VISIBLE) {
1797 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1798 mCandidatesViewStarted = true;
1799 onStartCandidatesView(mInputEditorInfo, restarting);
1800 }
1801 }
1802 }
1803
1804 /**
1805 * Called to inform the input method that text input has finished in
1806 * the last editor. At this point there may be a call to
1807 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
1808 * new editor, or the input method may be left idle. This method is
1809 * <em>not</em> called when input restarts in the same editor.
1810 *
1811 * <p>The default
1812 * implementation uses the InputConnection to clear any active composing
1813 * text; you can override this (not calling the base class implementation)
1814 * to perform whatever behavior you would like.
1815 */
1816 public void onFinishInput() {
1817 InputConnection ic = getCurrentInputConnection();
1818 if (ic != null) {
1819 ic.finishComposingText();
1820 }
1821 }
1822
1823 /**
1824 * Called when the application has reported auto-completion candidates that
1825 * it would like to have the input method displayed. Typically these are
1826 * only used when an input method is running in full-screen mode, since
1827 * otherwise the user can see and interact with the pop-up window of
1828 * completions shown by the application.
1829 *
1830 * <p>The default implementation here does nothing.
1831 */
1832 public void onDisplayCompletions(CompletionInfo[] completions) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001833 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001834 }
1835
1836 /**
1837 * Called when the application has reported new extracted text to be shown
1838 * due to changes in its current text state. The default implementation
1839 * here places the new text in the extract edit text, when the input
1840 * method is running in fullscreen mode.
1841 */
1842 public void onUpdateExtractedText(int token, ExtractedText text) {
1843 if (mExtractedToken != token) {
1844 return;
1845 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001846 if (text != null) {
1847 if (mExtractEditText != null) {
1848 mExtractedText = text;
1849 mExtractEditText.setExtractedText(text);
1850 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 }
1852 }
1853
1854 /**
1855 * Called when the application has reported a new selection region of
1856 * the text. This is called whether or not the input method has requested
1857 * extracted text updates, although if so it will not receive this call
1858 * if the extracted text has changed as well.
Jean Chalardc743cb92013-09-12 16:28:45 +09001859 *
1860 * <p>Be careful about changing the text in reaction to this call with
1861 * methods such as setComposingText, commitText or
1862 * deleteSurroundingText. If the cursor moves as a result, this method
1863 * will be called again, which may result in an infinite loop.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 *
1865 * <p>The default implementation takes care of updating the cursor in
1866 * the extract text, if it is being shown.
1867 */
1868 public void onUpdateSelection(int oldSelStart, int oldSelEnd,
1869 int newSelStart, int newSelEnd,
1870 int candidatesStart, int candidatesEnd) {
1871 final ExtractEditText eet = mExtractEditText;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001872 if (eet != null && isFullscreenMode() && mExtractedText != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001873 final int off = mExtractedText.startOffset;
1874 eet.startInternalChanges();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001875 newSelStart -= off;
1876 newSelEnd -= off;
1877 final int len = eet.getText().length();
1878 if (newSelStart < 0) newSelStart = 0;
1879 else if (newSelStart > len) newSelStart = len;
1880 if (newSelEnd < 0) newSelEnd = 0;
1881 else if (newSelEnd > len) newSelEnd = len;
1882 eet.setSelection(newSelStart, newSelEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883 eet.finishInternalChanges();
1884 }
1885 }
1886
1887 /**
satok863fcd62011-06-21 17:38:02 +09001888 * Called when the user tapped or clicked a text view.
1889 * IMEs can't rely on this method being called because this was not part of the original IME
1890 * protocol, so applications with custom text editing written before this method appeared will
1891 * not call to inform the IME of this interaction.
1892 * @param focusChanged true if the user changed the focused view by this click.
1893 */
1894 public void onViewClicked(boolean focusChanged) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001895 // Intentionally empty
satok863fcd62011-06-21 17:38:02 +09001896 }
1897
1898 /**
Yohei Yukawaa277db22014-08-21 18:38:44 -07001899 * Called when the application has reported a new location of its text
1900 * cursor. This is only called if explicitly requested by the input method.
1901 * The default implementation does nothing.
1902 * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 */
Yohei Yukawaa277db22014-08-21 18:38:44 -07001904 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001905 public void onUpdateCursor(Rect newCursor) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001906 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001907 }
1908
1909 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +09001910 * Called when the application has reported a new location of its text insertion point and
1911 * characters in the composition string. This is only called if explicitly requested by the
1912 * input method. The default implementation does nothing.
1913 * @param cursorAnchorInfo The positional information of the text insertion point and the
1914 * composition string.
1915 */
1916 public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
1917 // Intentionally empty
1918 }
1919
1920 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001921 * Close this input method's soft input area, removing it from the display.
1922 * The input method will continue running, but the user can no longer use
1923 * it to generate input by touching the screen.
1924 * @param flags Provides additional operating flags. Currently may be
1925 * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
1926 * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
1927 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001928 public void requestHideSelf(int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001929 mImm.hideSoftInputFromInputMethod(mToken, flags);
1930 }
1931
1932 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -08001933 * Show the input method. This is a call back to the
1934 * IMF to handle showing the input method.
The Android Open Source Project4df24232009-03-05 14:34:35 -08001935 * @param flags Provides additional operating flags. Currently may be
1936 * 0 or have the {@link InputMethodManager#SHOW_FORCED
1937 * InputMethodManager.} bit set.
1938 */
1939 private void requestShowSelf(int flags) {
1940 mImm.showSoftInputFromInputMethod(mToken, flags);
1941 }
Andrei Stingaceanu1036c742015-06-11 14:34:50 +00001942
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001943 private boolean handleBack(boolean doIt) {
1944 if (mShowInputRequested) {
1945 // If the soft input area is shown, back closes it and we
1946 // consume the back key.
1947 if (doIt) requestHideSelf(0);
1948 return true;
1949 } else if (mWindowVisible) {
1950 if (mCandidatesVisibility == View.VISIBLE) {
1951 // If we are showing candidates even if no input area, then
1952 // hide them.
1953 if (doIt) setCandidatesViewShown(false);
1954 } else {
1955 // If we have the window visible for some other reason --
1956 // most likely to show candidates -- then just get rid
1957 // of it. This really shouldn't happen, but just in case...
satok2f913d92012-05-10 01:48:03 +09001958 if (doIt) doHideWindow();
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001959 }
1960 return true;
1961 }
1962 return false;
1963 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07001964
1965 /**
1966 * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise
1967 * {@code null} is returned.
1968 */
1969 private ExtractEditText getExtractEditTextIfVisible() {
1970 if (!isExtractViewShown() || !isInputViewShown()) {
1971 return null;
1972 }
1973 return mExtractEditText;
1974 }
1975
The Android Open Source Project4df24232009-03-05 14:34:35 -08001976 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001977 * Override this to intercept key down events before they are processed by the
Quddus Chongee71b1f2012-04-12 11:49:37 -07001978 * application. If you return true, the application will not
1979 * process the event itself. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980 * will occur as if the IME had not seen the event at all.
1981 *
1982 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001983 * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
1984 * possibly hide it when the key goes up (if not canceled or long pressed). In
1985 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001986 * events to move the cursor in the extracted text view, not allowing
1987 * them to perform navigation in the underlying application.
1988 */
1989 public boolean onKeyDown(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001990 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07001991 final ExtractEditText eet = getExtractEditTextIfVisible();
1992 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
1993 return true;
1994 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001995 if (handleBack(false)) {
1996 event.startTracking();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001997 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001998 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001999 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002000 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002001 return doMovementKey(keyCode, event, MOVEMENT_DOWN);
2002 }
2003
2004 /**
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002005 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
2006 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
2007 * the event).
2008 */
2009 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
2010 return false;
2011 }
2012
2013 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002014 * Override this to intercept special key multiple events before they are
2015 * processed by the
2016 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002017 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002018 * will occur as if the IME had not seen the event at all.
2019 *
2020 * <p>The default implementation always returns false, except when
2021 * in fullscreen mode, where it will consume DPAD movement
2022 * events to move the cursor in the extracted text view, not allowing
2023 * them to perform navigation in the underlying application.
2024 */
2025 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
2026 return doMovementKey(keyCode, event, count);
2027 }
2028
2029 /**
2030 * Override this to intercept key up events before they are processed by the
2031 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002032 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 * will occur as if the IME had not seen the event at all.
2034 *
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002035 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
2036 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
2037 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 * events to move the cursor in the extracted text view, not allowing
2039 * them to perform navigation in the underlying application.
2040 */
2041 public boolean onKeyUp(int keyCode, KeyEvent event) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002042 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
2043 final ExtractEditText eet = getExtractEditTextIfVisible();
2044 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2045 return true;
2046 }
2047 if (event.isTracking() && !event.isCanceled()) {
2048 return handleBack(true);
2049 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002050 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002051 return doMovementKey(keyCode, event, MOVEMENT_UP);
2052 }
2053
Victoria Leaseb38070c2012-08-24 13:46:02 -07002054 /**
2055 * Override this to intercept trackball motion events before they are
2056 * processed by the application.
2057 * If you return true, the application will not itself process the event.
2058 * If you return false, the normal application processing will occur as if
2059 * the IME had not seen the event at all.
2060 */
satokab751aa2010-09-14 19:17:36 +09002061 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002062 public boolean onTrackballEvent(MotionEvent event) {
Victoria Leaseb38070c2012-08-24 13:46:02 -07002063 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
2064 return false;
2065 }
2066
2067 /**
2068 * Override this to intercept generic motion events before they are
2069 * processed by the application.
2070 * If you return true, the application will not itself process the event.
2071 * If you return false, the normal application processing will occur as if
2072 * the IME had not seen the event at all.
2073 */
2074 @Override
2075 public boolean onGenericMotionEvent(MotionEvent event) {
2076 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002077 return false;
2078 }
2079
2080 public void onAppPrivateCommand(String action, Bundle data) {
2081 }
2082
The Android Open Source Project4df24232009-03-05 14:34:35 -08002083 /**
2084 * Handle a request by the system to toggle the soft input area.
2085 */
2086 private void onToggleSoftInput(int showFlags, int hideFlags) {
2087 if (DEBUG) Log.v(TAG, "toggleSoftInput()");
2088 if (isInputViewShown()) {
2089 requestHideSelf(hideFlags);
2090 } else {
2091 requestShowSelf(showFlags);
2092 }
2093 }
2094
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002095 static final int MOVEMENT_DOWN = -1;
2096 static final int MOVEMENT_UP = -2;
2097
2098 void reportExtractedMovement(int keyCode, int count) {
2099 int dx = 0, dy = 0;
2100 switch (keyCode) {
2101 case KeyEvent.KEYCODE_DPAD_LEFT:
2102 dx = -count;
2103 break;
2104 case KeyEvent.KEYCODE_DPAD_RIGHT:
2105 dx = count;
2106 break;
2107 case KeyEvent.KEYCODE_DPAD_UP:
2108 dy = -count;
2109 break;
2110 case KeyEvent.KEYCODE_DPAD_DOWN:
2111 dy = count;
2112 break;
2113 }
satokab751aa2010-09-14 19:17:36 +09002114 onExtractedCursorMovement(dx, dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002115 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002117 boolean doMovementKey(int keyCode, KeyEvent event, int count) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002118 final ExtractEditText eet = getExtractEditTextIfVisible();
2119 if (eet != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002120 // If we are in fullscreen mode, the cursor will move around
2121 // the extract edit text, but should NOT cause focus to move
2122 // to other fields.
2123 MovementMethod movement = eet.getMovementMethod();
2124 Layout layout = eet.getLayout();
2125 if (movement != null && layout != null) {
2126 // We want our own movement method to handle the key, so the
2127 // cursor will properly move in our own word wrapping.
2128 if (count == MOVEMENT_DOWN) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002129 if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002130 reportExtractedMovement(keyCode, 1);
2131 return true;
2132 }
2133 } else if (count == MOVEMENT_UP) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002134 if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002135 return true;
2136 }
2137 } else {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002138 if (movement.onKeyOther(eet, eet.getText(), event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002139 reportExtractedMovement(keyCode, count);
2140 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07002141 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002142 if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002143 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002144 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002145 while (--count > 0) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002146 movement.onKeyDown(eet, eet.getText(), keyCode, down);
2147 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002148 }
2149 reportExtractedMovement(keyCode, count);
2150 }
2151 }
2152 }
2153 }
2154 // Regardless of whether the movement method handled the key,
2155 // we never allow DPAD navigation to the application.
2156 switch (keyCode) {
2157 case KeyEvent.KEYCODE_DPAD_LEFT:
2158 case KeyEvent.KEYCODE_DPAD_RIGHT:
2159 case KeyEvent.KEYCODE_DPAD_UP:
2160 case KeyEvent.KEYCODE_DPAD_DOWN:
2161 return true;
2162 }
2163 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002164
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002165 return false;
2166 }
2167
2168 /**
2169 * Send the given key event code (as defined by {@link KeyEvent}) to the
2170 * current input connection is a key down + key up event pair. The sent
2171 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
2172 * set, so that the recipient can identify them as coming from a software
2173 * input method, and
2174 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
2175 * that they don't impact the current touch mode of the UI.
2176 *
Jean Chalard405bc512012-05-29 19:12:34 +09002177 * <p>Note that it's discouraged to send such key events in normal operation;
2178 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
2179 * text fields, or for non-rich input methods. A reasonably capable software
2180 * input method should use the
2181 * {@link android.view.inputmethod.InputConnection#commitText} family of methods
2182 * to send text to an application, rather than sending key events.</p>
2183 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002184 * @param keyEventCode The raw key code to send, as defined by
2185 * {@link KeyEvent}.
2186 */
2187 public void sendDownUpKeyEvents(int keyEventCode) {
2188 InputConnection ic = getCurrentInputConnection();
2189 if (ic == null) return;
2190 long eventTime = SystemClock.uptimeMillis();
2191 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002192 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002193 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
Tadashi G. Takaokacb95cd62012-10-26 17:20:59 +09002194 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002195 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002196 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2197 }
2198
2199 /**
2200 * Ask the input target to execute its default action via
2201 * {@link InputConnection#performEditorAction
2202 * InputConnection.performEditorAction()}.
2203 *
2204 * @param fromEnterKey If true, this will be executed as if the user had
2205 * pressed an enter key on the keyboard, that is it will <em>not</em>
2206 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
2207 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
2208 * sent regardless of how the editor has set that flag.
2209 *
2210 * @return Returns a boolean indicating whether an action has been sent.
2211 * If false, either the editor did not specify a default action or it
2212 * does not want an action from the enter key. If true, the action was
2213 * sent (or there was no input connection at all).
2214 */
2215 public boolean sendDefaultEditorAction(boolean fromEnterKey) {
2216 EditorInfo ei = getCurrentInputEditorInfo();
2217 if (ei != null &&
2218 (!fromEnterKey || (ei.imeOptions &
2219 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
2220 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
2221 EditorInfo.IME_ACTION_NONE) {
2222 // If the enter key was pressed, and the editor has a default
2223 // action associated with pressing enter, then send it that
2224 // explicit action instead of the key event.
2225 InputConnection ic = getCurrentInputConnection();
2226 if (ic != null) {
2227 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
2228 }
2229 return true;
2230 }
2231
2232 return false;
2233 }
2234
2235 /**
2236 * Send the given UTF-16 character to the current input connection. Most
2237 * characters will be delivered simply by calling
2238 * {@link InputConnection#commitText InputConnection.commitText()} with
2239 * the character; some, however, may be handled different. In particular,
2240 * the enter character ('\n') will either be delivered as an action code
Jean Chalard405bc512012-05-29 19:12:34 +09002241 * or a raw key event, as appropriate. Consider this as a convenience
2242 * method for IMEs that do not have a full implementation of actions; a
2243 * fully complying IME will decide of the right action for each event and
2244 * will likely never call this method except maybe to handle events coming
2245 * from an actual hardware keyboard.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002246 *
2247 * @param charCode The UTF-16 character code to send.
2248 */
2249 public void sendKeyChar(char charCode) {
2250 switch (charCode) {
2251 case '\n': // Apps may be listening to an enter key to perform an action
2252 if (!sendDefaultEditorAction(true)) {
2253 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2254 }
2255 break;
2256 default:
2257 // Make sure that digits go through any text watcher on the client side.
2258 if (charCode >= '0' && charCode <= '9') {
2259 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2260 } else {
2261 InputConnection ic = getCurrentInputConnection();
2262 if (ic != null) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002263 ic.commitText(String.valueOf(charCode), 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002264 }
2265 }
2266 break;
2267 }
2268 }
2269
2270 /**
2271 * This is called when the user has moved the cursor in the extracted
2272 * text view, when running in fullsreen mode. The default implementation
2273 * performs the corresponding selection change on the underlying text
2274 * editor.
2275 */
2276 public void onExtractedSelectionChanged(int start, int end) {
2277 InputConnection conn = getCurrentInputConnection();
2278 if (conn != null) {
2279 conn.setSelection(start, end);
2280 }
2281 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002282
2283 /**
2284 * @hide
2285 */
2286 public void onExtractedDeleteText(int start, int end) {
2287 InputConnection conn = getCurrentInputConnection();
2288 if (conn != null) {
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002289 conn.finishComposingText();
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002290 conn.setSelection(start, start);
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002291 conn.deleteSurroundingText(0, end - start);
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002292 }
2293 }
2294
2295 /**
2296 * @hide
2297 */
2298 public void onExtractedReplaceText(int start, int end, CharSequence text) {
2299 InputConnection conn = getCurrentInputConnection();
2300 if (conn != null) {
2301 conn.setComposingRegion(start, end);
2302 conn.commitText(text, 1);
2303 }
2304 }
2305
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002306 /**
Gilles Debunnee300be92011-12-06 10:15:56 -08002307 * @hide
2308 */
2309 public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2310 InputConnection conn = getCurrentInputConnection();
2311 if (conn != null) {
2312 if (!conn.setSelection(start, end)) return;
2313 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2314 if (text instanceof Spannable) {
2315 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2316 conn.setComposingRegion(start, end);
2317 conn.commitText(text, 1);
2318 }
2319 }
2320 }
2321
2322 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002323 * This is called when the user has clicked on the extracted text view,
2324 * when running in fullscreen mode. The default implementation hides
2325 * the candidates view when this happens, but only if the extracted text
2326 * editor has a vertical scroll bar because its text doesn't fit.
2327 * Re-implement this to provide whatever behavior you want.
2328 */
2329 public void onExtractedTextClicked() {
2330 if (mExtractEditText == null) {
2331 return;
2332 }
2333 if (mExtractEditText.hasVerticalScrollBar()) {
2334 setCandidatesViewShown(false);
2335 }
2336 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002337
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002338 /**
2339 * This is called when the user has performed a cursor movement in the
2340 * extracted text view, when it is running in fullscreen mode. The default
2341 * implementation hides the candidates view when a vertical movement
2342 * happens, but only if the extracted text editor has a vertical scroll bar
2343 * because its text doesn't fit.
2344 * Re-implement this to provide whatever behavior you want.
2345 * @param dx The amount of cursor movement in the x dimension.
2346 * @param dy The amount of cursor movement in the y dimension.
2347 */
2348 public void onExtractedCursorMovement(int dx, int dy) {
2349 if (mExtractEditText == null || dy == 0) {
2350 return;
2351 }
2352 if (mExtractEditText.hasVerticalScrollBar()) {
2353 setCandidatesViewShown(false);
2354 }
2355 }
2356
2357 /**
2358 * This is called when the user has selected a context menu item from the
2359 * extracted text view, when running in fullscreen mode. The default
2360 * implementation sends this action to the current InputConnection's
2361 * {@link InputConnection#performContextMenuAction(int)}, for it
2362 * to be processed in underlying "real" editor. Re-implement this to
2363 * provide whatever behavior you want.
2364 */
2365 public boolean onExtractTextContextMenuItem(int id) {
2366 InputConnection ic = getCurrentInputConnection();
2367 if (ic != null) {
2368 ic.performContextMenuAction(id);
2369 }
2370 return true;
2371 }
2372
2373 /**
2374 * Return text that can be used as a button label for the given
2375 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
2376 * if there is no action requested. Note that there is no guarantee that
2377 * the returned text will be relatively short, so you probably do not
2378 * want to use it as text on a soft keyboard key label.
2379 *
2380 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
2381 *
2382 * @return Returns a label to use, or null if there is no action.
2383 */
2384 public CharSequence getTextForImeAction(int imeOptions) {
2385 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2386 case EditorInfo.IME_ACTION_NONE:
2387 return null;
2388 case EditorInfo.IME_ACTION_GO:
2389 return getText(com.android.internal.R.string.ime_action_go);
2390 case EditorInfo.IME_ACTION_SEARCH:
2391 return getText(com.android.internal.R.string.ime_action_search);
2392 case EditorInfo.IME_ACTION_SEND:
2393 return getText(com.android.internal.R.string.ime_action_send);
2394 case EditorInfo.IME_ACTION_NEXT:
2395 return getText(com.android.internal.R.string.ime_action_next);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002396 case EditorInfo.IME_ACTION_DONE:
2397 return getText(com.android.internal.R.string.ime_action_done);
Dianne Hackborndea3ef72010-10-28 14:24:22 -07002398 case EditorInfo.IME_ACTION_PREVIOUS:
2399 return getText(com.android.internal.R.string.ime_action_previous);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002400 default:
2401 return getText(com.android.internal.R.string.ime_action_default);
2402 }
2403 }
2404
2405 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07002406 * Called when the fullscreen-mode extracting editor info has changed,
2407 * to determine whether the extracting (extract text and candidates) portion
2408 * of the UI should be shown. The standard implementation hides or shows
2409 * the extract area depending on whether it makes sense for the
2410 * current editor. In particular, a {@link InputType#TYPE_NULL}
2411 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
2412 * turn off the extract area since there is no text to be shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002413 */
The Android Open Source Project10592532009-03-18 17:39:46 -07002414 public void onUpdateExtractingVisibility(EditorInfo ei) {
2415 if (ei.inputType == InputType.TYPE_NULL ||
2416 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
2417 // No reason to show extract UI!
2418 setExtractViewShown(false);
2419 return;
2420 }
2421
2422 setExtractViewShown(true);
2423 }
2424
2425 /**
2426 * Called when the fullscreen-mode extracting editor info has changed,
2427 * to update the state of its UI such as the action buttons shown.
2428 * You do not need to deal with this if you are using the standard
2429 * full screen extract UI. If replacing it, you will need to re-implement
2430 * this to put the appropriate action button in your own UI and handle it,
2431 * and perform any other changes.
2432 *
2433 * <p>The standard implementation turns on or off its accessory area
2434 * depending on whether there is an action button, and hides or shows
2435 * the entire extract area depending on whether it makes sense for the
2436 * current editor. In particular, a {@link InputType#TYPE_NULL} or
2437 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
2438 * extract area since there is no text to be shown.
2439 */
2440 public void onUpdateExtractingViews(EditorInfo ei) {
2441 if (!isExtractViewShown()) {
2442 return;
2443 }
2444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 if (mExtractAccessories == null) {
2446 return;
2447 }
2448 final boolean hasAction = ei.actionLabel != null || (
2449 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
The Android Open Source Project10592532009-03-18 17:39:46 -07002450 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
2451 ei.inputType != InputType.TYPE_NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002452 if (hasAction) {
2453 mExtractAccessories.setVisibility(View.VISIBLE);
Steve Kondik59eb6912009-09-07 22:53:34 -04002454 if (mExtractAction != null) {
2455 if (ei.actionLabel != null) {
2456 mExtractAction.setText(ei.actionLabel);
2457 } else {
2458 mExtractAction.setText(getTextForImeAction(ei.imeOptions));
2459 }
2460 mExtractAction.setOnClickListener(mActionClickListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002462 } else {
2463 mExtractAccessories.setVisibility(View.GONE);
Steve Kondik59eb6912009-09-07 22:53:34 -04002464 if (mExtractAction != null) {
2465 mExtractAction.setOnClickListener(null);
2466 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002467 }
2468 }
2469
2470 /**
2471 * This is called when, while currently displayed in extract mode, the
2472 * current input target changes. The default implementation will
2473 * auto-hide the IME if the new target is not a full editor, since this
Ken Wakasaf76a50c2012-03-09 19:56:35 +09002474 * can be a confusing experience for the user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002475 */
2476 public void onExtractingInputChanged(EditorInfo ei) {
2477 if (ei.inputType == InputType.TYPE_NULL) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002478 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002479 }
2480 }
2481
2482 void startExtractingText(boolean inputChanged) {
2483 final ExtractEditText eet = mExtractEditText;
2484 if (eet != null && getCurrentInputStarted()
2485 && isFullscreenMode()) {
2486 mExtractedToken++;
2487 ExtractedTextRequest req = new ExtractedTextRequest();
2488 req.token = mExtractedToken;
2489 req.flags = InputConnection.GET_TEXT_WITH_STYLES;
2490 req.hintMaxLines = 10;
2491 req.hintMaxChars = 10000;
Amith Yamasaniba4d93f2009-08-19 18:27:56 -07002492 InputConnection ic = getCurrentInputConnection();
2493 mExtractedText = ic == null? null
2494 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
Amith Yamasania8b00c82010-03-05 15:41:31 -08002495 if (mExtractedText == null || ic == null) {
2496 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
2497 + mExtractedText + ", input connection = " + ic);
2498 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002499 final EditorInfo ei = getCurrentInputEditorInfo();
2500
2501 try {
2502 eet.startInternalChanges();
The Android Open Source Project10592532009-03-18 17:39:46 -07002503 onUpdateExtractingVisibility(ei);
2504 onUpdateExtractingViews(ei);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002505 int inputType = ei.inputType;
2506 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
2507 == EditorInfo.TYPE_CLASS_TEXT) {
2508 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
2509 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2510 }
2511 }
2512 eet.setInputType(inputType);
2513 eet.setHint(ei.hintText);
2514 if (mExtractedText != null) {
2515 eet.setEnabled(true);
2516 eet.setExtractedText(mExtractedText);
2517 } else {
2518 eet.setEnabled(false);
2519 eet.setText("");
2520 }
2521 } finally {
2522 eet.finishInternalChanges();
2523 }
2524
2525 if (inputChanged) {
2526 onExtractingInputChanged(ei);
2527 }
2528 }
2529 }
satokab751aa2010-09-14 19:17:36 +09002530
2531 // TODO: Handle the subtype change event
2532 /**
2533 * Called when the subtype was changed.
2534 * @param newSubtype the subtype which is being changed to.
2535 */
2536 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
2537 if (DEBUG) {
2538 int nameResId = newSubtype.getNameResId();
satok9ef02832010-11-04 21:17:48 +09002539 String mode = newSubtype.getMode();
satokab751aa2010-09-14 19:17:36 +09002540 String output = "changeInputMethodSubtype:"
2541 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
satok9ef02832010-11-04 21:17:48 +09002542 + mode + ","
satokab751aa2010-09-14 19:17:36 +09002543 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
2544 Log.v(TAG, "--- " + output);
2545 }
2546 }
2547
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002548 /**
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09002549 * @return The recommended height of the input method window.
2550 * An IME author can get the last input method's height as the recommended height
2551 * by calling this in
2552 * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}.
2553 * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME
2554 * switching by using this value as a visible inset height. It's efficient for the smooth
2555 * transition between different IMEs. However, note that this may return 0 (or possibly
2556 * unexpectedly low height). You should thus avoid relying on the return value of this method
2557 * all the time. Please make sure to use a reasonable height for the IME.
2558 */
2559 public int getInputMethodWindowRecommendedHeight() {
2560 return mImm.getInputMethodWindowVisibleHeight();
2561 }
2562
2563 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002564 * Performs a dump of the InputMethodService's internal state. Override
2565 * to add your own information to the dump.
2566 */
2567 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2568 final Printer p = new PrintWriterPrinter(fout);
2569 p.println("Input method service state for " + this + ":");
2570 p.println(" mWindowCreated=" + mWindowCreated
The Android Open Source Project10592532009-03-18 17:39:46 -07002571 + " mWindowAdded=" + mWindowAdded);
2572 p.println(" mWindowVisible=" + mWindowVisible
2573 + " mWindowWasVisible=" + mWindowWasVisible
2574 + " mInShowWindow=" + mInShowWindow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002575 p.println(" Configuration=" + getResources().getConfiguration());
2576 p.println(" mToken=" + mToken);
2577 p.println(" mInputBinding=" + mInputBinding);
2578 p.println(" mInputConnection=" + mInputConnection);
2579 p.println(" mStartedInputConnection=" + mStartedInputConnection);
2580 p.println(" mInputStarted=" + mInputStarted
2581 + " mInputViewStarted=" + mInputViewStarted
2582 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
2583
2584 if (mInputEditorInfo != null) {
2585 p.println(" mInputEditorInfo:");
2586 mInputEditorInfo.dump(p, " ");
2587 } else {
2588 p.println(" mInputEditorInfo: null");
2589 }
2590
2591 p.println(" mShowInputRequested=" + mShowInputRequested
2592 + " mLastShowInputRequested=" + mLastShowInputRequested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002593 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
2594 p.println(" mCandidatesVisibility=" + mCandidatesVisibility
2595 + " mFullscreenApplied=" + mFullscreenApplied
The Android Open Source Project10592532009-03-18 17:39:46 -07002596 + " mIsFullscreen=" + mIsFullscreen
2597 + " mExtractViewHidden=" + mExtractViewHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002598
2599 if (mExtractedText != null) {
2600 p.println(" mExtractedText:");
2601 p.println(" text=" + mExtractedText.text.length() + " chars"
2602 + " startOffset=" + mExtractedText.startOffset);
2603 p.println(" selectionStart=" + mExtractedText.selectionStart
2604 + " selectionEnd=" + mExtractedText.selectionEnd
2605 + " flags=0x" + Integer.toHexString(mExtractedText.flags));
2606 } else {
2607 p.println(" mExtractedText: null");
2608 }
2609 p.println(" mExtractedToken=" + mExtractedToken);
2610 p.println(" mIsInputViewShown=" + mIsInputViewShown
2611 + " mStatusIcon=" + mStatusIcon);
2612 p.println("Last computed insets:");
2613 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
2614 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
Jeff Brownfbf09772011-01-16 14:06:57 -08002615 + " touchableInsets=" + mTmpInsets.touchableInsets
2616 + " touchableRegion=" + mTmpInsets.touchableRegion);
Yohei Yukawa2977eb72015-05-27 18:54:18 -07002617 p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
Yohei Yukawa7b739a82015-12-21 13:30:44 -08002618 p.println(" mSettingsObserver=" + mSettingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002619 }
2620}