blob: 6b79a8ac438a1896cda10b1c4a5bf1a115520619 [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;
298
299 boolean mShowInputForced;
300
301 boolean mFullscreenApplied;
302 boolean mIsFullscreen;
303 View mExtractView;
The Android Open Source Project10592532009-03-18 17:39:46 -0700304 boolean mExtractViewHidden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 ExtractEditText mExtractEditText;
306 ViewGroup mExtractAccessories;
307 Button mExtractAction;
308 ExtractedText mExtractedText;
309 int mExtractedToken;
310
311 View mInputView;
312 boolean mIsInputViewShown;
313
314 int mStatusIcon;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800315 int mBackDisposition;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316
Yohei Yukawa2977eb72015-05-27 18:54:18 -0700317 /**
318 * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
319 * have not shown our own window yet. In this situation, the previous inset continues to be
320 * shown as an empty region until it is explicitly updated. Basically we can trigger the update
321 * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}.
322 */
323 boolean mShouldClearInsetOfPreviousIme;
324
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 final Insets mTmpInsets = new Insets();
326 final int[] mTmpLocation = new int[2];
satokab751aa2010-09-14 19:17:36 +0900327
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
329 new ViewTreeObserver.OnComputeInternalInsetsListener() {
330 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700331 if (isExtractViewShown()) {
332 // In true fullscreen mode, we just say the window isn't covering
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 // any content so we don't impact whatever is behind.
334 View decor = getWindow().getWindow().getDecorView();
335 info.contentInsets.top = info.visibleInsets.top
336 = decor.getHeight();
Jeff Brownfbf09772011-01-16 14:06:57 -0800337 info.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
339 } else {
340 onComputeInsets(mTmpInsets);
341 info.contentInsets.top = mTmpInsets.contentTopInsets;
342 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -0800343 info.touchableRegion.set(mTmpInsets.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 info.setTouchableInsets(mTmpInsets.touchableInsets);
345 }
346 }
347 };
348
349 final View.OnClickListener mActionClickListener = new View.OnClickListener() {
350 public void onClick(View v) {
351 final EditorInfo ei = getCurrentInputEditorInfo();
352 final InputConnection ic = getCurrentInputConnection();
353 if (ei != null && ic != null) {
354 if (ei.actionId != 0) {
355 ic.performEditorAction(ei.actionId);
356 } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION)
357 != EditorInfo.IME_ACTION_NONE) {
358 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
359 }
360 }
361 }
362 };
363
364 /**
365 * Concrete implementation of
366 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
367 * all of the standard behavior for an input method.
368 */
369 public class InputMethodImpl extends AbstractInputMethodImpl {
370 /**
371 * Take care of attaching the given window token provided by the system.
372 */
373 public void attachToken(IBinder token) {
374 if (mToken == null) {
375 mToken = token;
376 mWindow.setToken(token);
377 }
378 }
379
380 /**
381 * Handle a new input binding, calling
382 * {@link InputMethodService#onBindInput InputMethodService.onBindInput()}
383 * when done.
384 */
385 public void bindInput(InputBinding binding) {
386 mInputBinding = binding;
387 mInputConnection = binding.getConnection();
388 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
389 + " ic=" + mInputConnection);
390 InputConnection ic = getCurrentInputConnection();
391 if (ic != null) ic.reportFullscreenMode(mIsFullscreen);
392 initialize();
393 onBindInput();
394 }
395
396 /**
397 * Clear the current input binding.
398 */
399 public void unbindInput() {
400 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
401 + " ic=" + mInputConnection);
402 onUnbindInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 mInputBinding = null;
404 mInputConnection = null;
405 }
406
407 public void startInput(InputConnection ic, EditorInfo attribute) {
408 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
409 doStartInput(ic, attribute, false);
410 }
411
412 public void restartInput(InputConnection ic, EditorInfo attribute) {
413 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
414 doStartInput(ic, attribute, true);
415 }
416
417 /**
418 * Handle a request by the system to hide the soft input area.
419 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800420 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 if (DEBUG) Log.v(TAG, "hideSoftInput()");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800422 boolean wasVis = isInputViewShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 mShowInputFlags = 0;
424 mShowInputRequested = false;
425 mShowInputForced = false;
satok2f913d92012-05-10 01:48:03 +0900426 doHideWindow();
Yohei Yukawa2977eb72015-05-27 18:54:18 -0700427 clearInsetOfPreviousIme();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800428 if (resultReceiver != null) {
429 resultReceiver.send(wasVis != isInputViewShown()
430 ? InputMethodManager.RESULT_HIDDEN
431 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
432 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
433 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 }
435
436 /**
437 * Handle a request by the system to show the soft input area.
438 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800439 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700440 if (DEBUG) Log.v(TAG, "showSoftInput()");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800441 boolean wasVis = isInputViewShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 mShowInputFlags = 0;
443 if (onShowInputRequested(flags, false)) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700444 try {
445 showWindow(true);
446 } catch (BadTokenException e) {
Yohei Yukawa6fcbb562015-09-14 16:48:15 -0700447 // We have ignored BadTokenException here since Jelly Bean MR-2 (API Level 18).
448 // We could ignore BadTokenException in InputMethodService#showWindow() instead,
449 // but it may break assumptions for those who override #showWindow() that we can
450 // detect errors in #showWindow() by checking BadTokenException.
451 // TODO: Investigate its feasibility. Update JavaDoc of #showWindow() of
452 // whether it's OK to override #showWindow() or not.
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700453 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -0700455 clearInsetOfPreviousIme();
satok865b9772011-01-21 02:45:06 +0900456 // If user uses hard keyboard, IME button should always be shown.
jungheang.lee217fd292013-02-26 16:53:22 +0900457 boolean showing = isInputViewShown();
Joe Onorato857fd9b2011-01-27 15:08:35 -0800458 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
459 mBackDisposition);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800460 if (resultReceiver != null) {
461 resultReceiver.send(wasVis != isInputViewShown()
462 ? InputMethodManager.RESULT_SHOWN
463 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
464 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
465 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 }
satokab751aa2010-09-14 19:17:36 +0900467
468 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
469 onCurrentInputMethodSubtypeChanged(subtype);
470 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 }
satokab751aa2010-09-14 19:17:36 +0900472
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 /**
474 * Concrete implementation of
475 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
476 * all of the standard behavior for an input method session.
477 */
478 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
479 public void finishInput() {
480 if (!isEnabled()) {
481 return;
482 }
483 if (DEBUG) Log.v(TAG, "finishInput() in " + this);
484 doFinishInput();
485 }
486
487 /**
488 * Call {@link InputMethodService#onDisplayCompletions
489 * InputMethodService.onDisplayCompletions()}.
490 */
491 public void displayCompletions(CompletionInfo[] completions) {
492 if (!isEnabled()) {
493 return;
494 }
495 mCurCompletions = completions;
496 onDisplayCompletions(completions);
497 }
498
499 /**
500 * Call {@link InputMethodService#onUpdateExtractedText
501 * InputMethodService.onUpdateExtractedText()}.
502 */
503 public void updateExtractedText(int token, ExtractedText text) {
504 if (!isEnabled()) {
505 return;
506 }
507 onUpdateExtractedText(token, text);
508 }
509
510 /**
511 * Call {@link InputMethodService#onUpdateSelection
512 * InputMethodService.onUpdateSelection()}.
513 */
514 public void updateSelection(int oldSelStart, int oldSelEnd,
515 int newSelStart, int newSelEnd,
516 int candidatesStart, int candidatesEnd) {
517 if (!isEnabled()) {
518 return;
519 }
520 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
521 newSelStart, newSelEnd, candidatesStart, candidatesEnd);
522 }
satok863fcd62011-06-21 17:38:02 +0900523
524 @Override
525 public void viewClicked(boolean focusChanged) {
526 if (!isEnabled()) {
527 return;
528 }
529 InputMethodService.this.onViewClicked(focusChanged);
530 }
531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 /**
533 * Call {@link InputMethodService#onUpdateCursor
534 * InputMethodService.onUpdateCursor()}.
535 */
536 public void updateCursor(Rect newCursor) {
537 if (!isEnabled()) {
538 return;
539 }
540 InputMethodService.this.onUpdateCursor(newCursor);
541 }
542
543 /**
544 * Call {@link InputMethodService#onAppPrivateCommand
545 * InputMethodService.onAppPrivateCommand()}.
546 */
547 public void appPrivateCommand(String action, Bundle data) {
548 if (!isEnabled()) {
549 return;
550 }
551 InputMethodService.this.onAppPrivateCommand(action, data);
552 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800553
554 /**
555 *
556 */
557 public void toggleSoftInput(int showFlags, int hideFlags) {
558 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
559 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900560
561 /**
562 * Call {@link InputMethodService#onUpdateCursorAnchorInfo
563 * InputMethodService.onUpdateCursorAnchorInfo()}.
564 */
565 public void updateCursorAnchorInfo(CursorAnchorInfo info) {
566 if (!isEnabled()) {
567 return;
568 }
569 InputMethodService.this.onUpdateCursorAnchorInfo(info);
570 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
572
573 /**
574 * Information about where interesting parts of the input method UI appear.
575 */
576 public static final class Insets {
577 /**
578 * This is the top part of the UI that is the main content. It is
579 * used to determine the basic space needed, to resize/pan the
580 * application behind. It is assumed that this inset does not
581 * change very much, since any change will cause a full resize/pan
582 * of the application behind. This value is relative to the top edge
583 * of the input method window.
584 */
585 public int contentTopInsets;
586
587 /**
588 * This is the top part of the UI that is visibly covering the
589 * application behind it. This provides finer-grained control over
590 * visibility, allowing you to change it relatively frequently (such
591 * as hiding or showing candidates) without disrupting the underlying
592 * UI too much. For example, this will never resize the application
593 * UI, will only pan if needed to make the current focus visible, and
594 * will not aggressively move the pan position when this changes unless
595 * needed to make the focus visible. This value is relative to the top edge
596 * of the input method window.
597 */
598 public int visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -0800599
600 /**
601 * This is the region of the UI that is touchable. It is used when
602 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
603 * The region should be specified relative to the origin of the window frame.
604 */
605 public final Region touchableRegion = new Region();
606
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 /**
608 * Option for {@link #touchableInsets}: the entire window frame
609 * can be touched.
610 */
611 public static final int TOUCHABLE_INSETS_FRAME
612 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
613
614 /**
615 * Option for {@link #touchableInsets}: the area inside of
616 * the content insets can be touched.
617 */
618 public static final int TOUCHABLE_INSETS_CONTENT
619 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
620
621 /**
622 * Option for {@link #touchableInsets}: the area inside of
623 * the visible insets can be touched.
624 */
625 public static final int TOUCHABLE_INSETS_VISIBLE
626 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -0800627
628 /**
629 * Option for {@link #touchableInsets}: the region specified by
630 * {@link #touchableRegion} can be touched.
631 */
632 public static final int TOUCHABLE_INSETS_REGION
633 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
634
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 /**
636 * Determine which area of the window is touchable by the user. May
637 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
Jeff Brownfbf09772011-01-16 14:06:57 -0800638 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
639 * or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 */
641 public int touchableInsets;
642 }
satok865b9772011-01-21 02:45:06 +0900643
The Android Open Source Project10592532009-03-18 17:39:46 -0700644 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -0800645 * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
646 *
647 * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
648 * Basically this functionality still needs to be considered as implementation details.</p>
649 */
650 @MainThread
651 private static final class SettingsObserver extends ContentObserver {
652 @Retention(RetentionPolicy.SOURCE)
653 @IntDef({
654 ShowImeWithHardKeyboardType.UNKNOWN,
655 ShowImeWithHardKeyboardType.FALSE,
656 ShowImeWithHardKeyboardType.TRUE,
657 })
658 private @interface ShowImeWithHardKeyboardType {
659 int UNKNOWN = 0;
660 int FALSE = 1;
661 int TRUE = 2;
662 }
663 @ShowImeWithHardKeyboardType
664 private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
665
666 private final InputMethodService mService;
667
668 private SettingsObserver(InputMethodService service) {
669 super(new Handler(service.getMainLooper()));
670 mService = service;
671 }
672
673 /**
674 * A factory method that internally enforces two-phase initialization to make sure that the
675 * object reference will not be escaped until the object is properly constructed.
676 *
677 * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread. Hence
678 * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
679 *
680 * @param service {@link InputMethodService} that needs to receive the callback.
681 * @return {@link SettingsObserver} that is already registered to
682 * {@link android.content.ContentResolver}. The caller must call
683 * {@link SettingsObserver#unregister()}.
684 */
685 public static SettingsObserver createAndRegister(InputMethodService service) {
686 final SettingsObserver observer = new SettingsObserver(service);
687 // The observer is properly constructed. Let's start accepting the event.
688 service.getContentResolver().registerContentObserver(
689 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
690 false, observer);
691 return observer;
692 }
693
694 void unregister() {
695 mService.getContentResolver().unregisterContentObserver(this);
696 }
697
698 private boolean shouldShowImeWithHardKeyboard() {
699 // Lazily initialize as needed.
700 if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
701 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
702 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
703 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
704 }
705 switch (mShowImeWithHardKeyboard) {
706 case ShowImeWithHardKeyboardType.TRUE:
707 return true;
708 case ShowImeWithHardKeyboardType.FALSE:
709 return false;
710 default:
711 Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
712 return false;
713 }
714 }
715
716 @Override
717 public void onChange(boolean selfChange, Uri uri) {
718 final Uri showImeWithHardKeyboardUri =
719 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
720 if (showImeWithHardKeyboardUri.equals(uri)) {
721 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
722 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
723 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
724 mService.updateInputViewShown();
725 }
726 }
727
728 @Override
729 public String toString() {
730 return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}";
731 }
732 }
733 private SettingsObserver mSettingsObserver;
734
735 /**
The Android Open Source Project10592532009-03-18 17:39:46 -0700736 * You can call this to customize the theme used by your IME's window.
737 * This theme should typically be one that derives from
738 * {@link android.R.style#Theme_InputMethod}, which is the default theme
739 * you will get. This must be set before {@link #onCreate}, so you
740 * will typically call it in your constructor with the resource ID
741 * of your custom theme.
742 */
satokab751aa2010-09-14 19:17:36 +0900743 @Override
The Android Open Source Project10592532009-03-18 17:39:46 -0700744 public void setTheme(int theme) {
745 if (mWindow != null) {
746 throw new IllegalStateException("Must be called before onCreate()");
747 }
748 mTheme = theme;
749 }
satok865b9772011-01-21 02:45:06 +0900750
Dianne Hackborn836531b2012-08-01 19:00:38 -0700751 /**
752 * You can call this to try to enable hardware accelerated drawing for
753 * your IME. This must be set before {@link #onCreate}, so you
754 * will typically call it in your constructor. It is not always possible
Alan Viverettee07b5952014-08-13 19:13:54 -0700755 * to use hardware accelerated drawing in an IME (for example on low-end
Dianne Hackborn836531b2012-08-01 19:00:38 -0700756 * devices that do not have the resources to support this), so the call
757 * returns true if it succeeds otherwise false if you will need to draw
758 * in software. You must be able to handle either case.
Alan Viverettee07b5952014-08-13 19:13:54 -0700759 *
760 * @deprecated Starting in API 21, hardware acceleration is always enabled
761 * on capable devices.
Dianne Hackborn836531b2012-08-01 19:00:38 -0700762 */
763 public boolean enableHardwareAcceleration() {
764 if (mWindow != null) {
765 throw new IllegalStateException("Must be called before onCreate()");
766 }
Jeff Brown98365d72012-08-19 20:30:52 -0700767 if (ActivityManager.isHighEndGfx()) {
Dianne Hackborn836531b2012-08-01 19:00:38 -0700768 mHardwareAccelerated = true;
769 return true;
770 }
771 return false;
772 }
773
Alan Viverette5effd7e2014-05-05 12:25:33 -0700774 @Override public void onCreate() {
775 mTheme = Resources.selectSystemTheme(mTheme,
776 getApplicationInfo().targetSdkVersion,
777 android.R.style.Theme_InputMethod,
778 android.R.style.Theme_Holo_InputMethod,
779 android.R.style.Theme_DeviceDefault_InputMethod,
780 android.R.style.Theme_DeviceDefault_InputMethod);
The Android Open Source Project10592532009-03-18 17:39:46 -0700781 super.setTheme(mTheme);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 super.onCreate();
783 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
Yohei Yukawa7b739a82015-12-21 13:30:44 -0800784 mSettingsObserver = SettingsObserver.createAndRegister(this);
Yohei Yukawa2977eb72015-05-27 18:54:18 -0700785 // If the previous IME has occupied non-empty inset in the screen, we need to decide whether
786 // we continue to use the same size of the inset or update it
787 mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 mInflater = (LayoutInflater)getSystemService(
789 Context.LAYOUT_INFLATER_SERVICE);
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700790 mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
Dianne Hackborne30e02f2014-05-27 18:24:45 -0700791 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
Dianne Hackborn836531b2012-08-01 19:00:38 -0700792 if (mHardwareAccelerated) {
793 mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
794 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 initViews();
Romain Guy980a9382010-01-08 15:06:28 -0800796 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 }
satokab751aa2010-09-14 19:17:36 +0900798
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 /**
800 * This is a hook that subclasses can use to perform initialization of
801 * their interface. It is called for you prior to any of your UI objects
802 * being created, both after the service is first created and after a
803 * configuration change happens.
804 */
805 public void onInitializeInterface() {
Gilles Debunne34703b62011-09-08 11:16:25 -0700806 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 }
satokab751aa2010-09-14 19:17:36 +0900808
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 void initialize() {
810 if (!mInitialized) {
811 mInitialized = true;
812 onInitializeInterface();
813 }
814 }
satokab751aa2010-09-14 19:17:36 +0900815
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 void initViews() {
817 mInitialized = false;
818 mWindowCreated = false;
819 mShowInputRequested = false;
820 mShowInputForced = false;
821
The Android Open Source Project10592532009-03-18 17:39:46 -0700822 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 mRootView = mInflater.inflate(
824 com.android.internal.R.layout.input_method, null);
John Spurlockc68d5772013-10-08 11:47:58 -0400825 mRootView.setSystemUiVisibility(
826 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 mWindow.setContentView(mRootView);
Seonggoo Kang72745ff2014-12-24 13:55:50 +0900828 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
Jeff Sharkey6e2bee72012-10-01 13:39:08 -0700830 if (Settings.Global.getInt(getContentResolver(),
831 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 mWindow.getWindow().setWindowAnimations(
833 com.android.internal.R.style.Animation_InputMethodFancy);
834 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700835 mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
836 mExtractViewHidden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
838 mExtractView = null;
839 mExtractEditText = null;
840 mExtractAccessories = null;
841 mExtractAction = null;
842 mFullscreenApplied = false;
843
844 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
845 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
846 mInputView = null;
847 mIsInputViewShown = false;
848
849 mExtractFrame.setVisibility(View.GONE);
850 mCandidatesVisibility = getCandidatesHiddenVisibility();
851 mCandidatesFrame.setVisibility(mCandidatesVisibility);
852 mInputFrame.setVisibility(View.GONE);
853 }
satokab751aa2010-09-14 19:17:36 +0900854
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 @Override public void onDestroy() {
856 super.onDestroy();
857 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
858 mInsetsComputer);
Satoshi Kataokac56191f2013-05-30 13:14:47 +0900859 doFinishInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 if (mWindowAdded) {
satokc0c87652011-09-12 12:08:05 +0900861 // Disable exit animation for the current IME window
862 // to avoid the race condition between the exit and enter animations
863 // when the current IME is being switched to another one.
864 mWindow.getWindow().setWindowAnimations(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 mWindow.dismiss();
866 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -0800867 if (mSettingsObserver != null) {
868 mSettingsObserver.unregister();
869 mSettingsObserver = null;
870 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 }
satokf17db9f2011-09-14 18:55:58 +0900872
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 /**
874 * Take care of handling configuration changes. Subclasses of
875 * InputMethodService generally don't need to deal directly with
876 * this on their own; the standard implementation here takes care of
877 * regenerating the input method UI as a result of the configuration
878 * change, so you can rely on your {@link #onCreateInputView} and
879 * other methods being called as appropriate due to a configuration change.
880 *
881 * <p>When a configuration change does happen,
882 * {@link #onInitializeInterface()} is guaranteed to be called the next
883 * time prior to any of the other input or UI creation callbacks. The
884 * following will be called immediately depending if appropriate for current
885 * state: {@link #onStartInput} if input is active, and
886 * {@link #onCreateInputView} and {@link #onStartInputView} and related
887 * appropriate functions if the UI is displayed.
888 */
889 @Override public void onConfigurationChanged(Configuration newConfig) {
890 super.onConfigurationChanged(newConfig);
891
892 boolean visible = mWindowVisible;
893 int showFlags = mShowInputFlags;
894 boolean showingInput = mShowInputRequested;
895 CompletionInfo[] completions = mCurCompletions;
896 initViews();
897 mInputViewStarted = false;
898 mCandidatesViewStarted = false;
899 if (mInputStarted) {
900 doStartInput(getCurrentInputConnection(),
901 getCurrentInputEditorInfo(), true);
902 }
903 if (visible) {
904 if (showingInput) {
905 // If we were last showing the soft keyboard, try to do so again.
906 if (onShowInputRequested(showFlags, true)) {
907 showWindow(true);
908 if (completions != null) {
909 mCurCompletions = completions;
910 onDisplayCompletions(completions);
911 }
912 } else {
satok2f913d92012-05-10 01:48:03 +0900913 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
915 } else if (mCandidatesVisibility == View.VISIBLE) {
916 // If the candidates are currently visible, make sure the
917 // window is shown for them.
918 showWindow(false);
919 } else {
920 // Otherwise hide the window.
satok2f913d92012-05-10 01:48:03 +0900921 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 }
satok865b9772011-01-21 02:45:06 +0900923 // If user uses hard keyboard, IME button should always be shown.
Joe Onorato857fd9b2011-01-27 15:08:35 -0800924 boolean showing = onEvaluateInputViewShown();
925 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
926 mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 }
928 }
929
930 /**
931 * Implement to return our standard {@link InputMethodImpl}. Subclasses
932 * can override to provide their own customized version.
933 */
satokab751aa2010-09-14 19:17:36 +0900934 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 public AbstractInputMethodImpl onCreateInputMethodInterface() {
936 return new InputMethodImpl();
937 }
938
939 /**
940 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
941 * can override to provide their own customized version.
942 */
satokab751aa2010-09-14 19:17:36 +0900943 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
945 return new InputMethodSessionImpl();
946 }
947
948 public LayoutInflater getLayoutInflater() {
949 return mInflater;
950 }
951
952 public Dialog getWindow() {
953 return mWindow;
954 }
955
Joe Onorato857fd9b2011-01-27 15:08:35 -0800956 public void setBackDisposition(int disposition) {
957 mBackDisposition = disposition;
958 }
959
960 public int getBackDisposition() {
961 return mBackDisposition;
962 }
963
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 /**
965 * Return the maximum width, in pixels, available the input method.
966 * Input methods are positioned at the bottom of the screen and, unless
967 * running in fullscreen, will generally want to be as short as possible
968 * so should compute their height based on their contents. However, they
969 * can stretch as much as needed horizontally. The function returns to
970 * you the maximum amount of space available horizontally, which you can
971 * use if needed for UI placement.
972 *
973 * <p>In many cases this is not needed, you can just rely on the normal
974 * view layout mechanisms to position your views within the full horizontal
975 * space given to the input method.
976 *
977 * <p>Note that this value can change dynamically, in particular when the
978 * screen orientation changes.
979 */
980 public int getMaxWidth() {
981 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
982 return wm.getDefaultDisplay().getWidth();
983 }
984
985 /**
986 * Return the currently active InputBinding for the input method, or
987 * null if there is none.
988 */
989 public InputBinding getCurrentInputBinding() {
990 return mInputBinding;
991 }
992
993 /**
994 * Retrieve the currently active InputConnection that is bound to
995 * the input method, or null if there is none.
996 */
997 public InputConnection getCurrentInputConnection() {
998 InputConnection ic = mStartedInputConnection;
999 if (ic != null) {
1000 return ic;
1001 }
1002 return mInputConnection;
1003 }
1004
1005 public boolean getCurrentInputStarted() {
1006 return mInputStarted;
1007 }
1008
1009 public EditorInfo getCurrentInputEditorInfo() {
1010 return mInputEditorInfo;
1011 }
1012
1013 /**
1014 * Re-evaluate whether the input method should be running in fullscreen
1015 * mode, and update its UI if this has changed since the last time it
1016 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to
1017 * determine whether it should currently run in fullscreen mode. You
1018 * can use {@link #isFullscreenMode()} to determine if the input method
1019 * is currently running in fullscreen mode.
1020 */
1021 public void updateFullscreenMode() {
1022 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
1023 boolean changed = mLastShowInputRequested != mShowInputRequested;
1024 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
1025 changed = true;
1026 mIsFullscreen = isFullscreen;
1027 InputConnection ic = getCurrentInputConnection();
1028 if (ic != null) ic.reportFullscreenMode(isFullscreen);
1029 mFullscreenApplied = true;
1030 initialize();
The Android Open Source Project10592532009-03-18 17:39:46 -07001031 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1032 mFullscreenArea.getLayoutParams();
1033 if (isFullscreen) {
1034 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
1035 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
1036 lp.height = 0;
1037 lp.weight = 1;
1038 } else {
1039 mFullscreenArea.setBackgroundDrawable(null);
1040 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
1041 lp.weight = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001043 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
1044 mFullscreenArea, lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 if (isFullscreen) {
1046 if (mExtractView == null) {
1047 View v = onCreateExtractTextView();
1048 if (v != null) {
1049 setExtractView(v);
1050 }
1051 }
1052 startExtractingText(false);
1053 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001054 updateExtractFrameVisibility();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 }
1056
1057 if (changed) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001058 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 mLastShowInputRequested = mShowInputRequested;
1060 }
1061 }
1062
1063 /**
1064 * Update the given window's parameters for the given mode. This is called
1065 * when the window is first displayed and each time the fullscreen or
1066 * candidates only mode changes.
1067 *
1068 * <p>The default implementation makes the layout for the window
Romain Guy980a9382010-01-08 15:06:28 -08001069 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
1070 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 *
1072 * @param win The input method's window.
1073 * @param isFullscreen If true, the window is running in fullscreen mode
1074 * and intended to cover the entire application display.
1075 * @param isCandidatesOnly If true, the window is only showing the
1076 * candidates view and none of the rest of its UI. This is mutually
1077 * exclusive with fullscreen mode.
1078 */
1079 public void onConfigureWindow(Window win, boolean isFullscreen,
1080 boolean isCandidatesOnly) {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001081 final int currentHeight = mWindow.getWindow().getAttributes().height;
1082 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
1083 if (mIsInputViewShown && currentHeight != newHeight) {
1084 Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: "
1085 + currentHeight + " -> " + newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001087 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 }
1089
1090 /**
1091 * Return whether the input method is <em>currently</em> running in
1092 * fullscreen mode. This is the mode that was last determined and
1093 * applied by {@link #updateFullscreenMode()}.
1094 */
1095 public boolean isFullscreenMode() {
1096 return mIsFullscreen;
1097 }
1098
1099 /**
1100 * Override this to control when the input method should run in
1101 * fullscreen mode. The default implementation runs in fullsceen only
1102 * when the screen is in landscape mode. If you change what
1103 * this returns, you will need to call {@link #updateFullscreenMode()}
1104 * yourself whenever the returned value may have changed to have it
1105 * re-evaluated and applied.
1106 */
1107 public boolean onEvaluateFullscreenMode() {
1108 Configuration config = getResources().getConfiguration();
Leon Scroggins2edd6822010-01-12 11:36:13 -05001109 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
1110 return false;
1111 }
1112 if (mInputEditorInfo != null
1113 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
1114 return false;
1115 }
1116 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117 }
Gilles Debunne34703b62011-09-08 11:16:25 -07001118
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001120 * Controls the visibility of the extracted text area. This only applies
1121 * when the input method is in fullscreen mode, and thus showing extracted
1122 * text. When false, the extracted text will not be shown, allowing some
1123 * of the application to be seen behind. This is normally set for you
1124 * by {@link #onUpdateExtractingVisibility}. This controls the visibility
1125 * of both the extracted text and candidate view; the latter since it is
1126 * not useful if there is no text to see.
1127 */
1128 public void setExtractViewShown(boolean shown) {
1129 if (mExtractViewHidden == shown) {
1130 mExtractViewHidden = !shown;
1131 updateExtractFrameVisibility();
1132 }
1133 }
1134
1135 /**
1136 * Return whether the fullscreen extract view is shown. This will only
1137 * return true if {@link #isFullscreenMode()} returns true, and in that
1138 * case its value depends on the last call to
1139 * {@link #setExtractViewShown(boolean)}. This effectively lets you
1140 * determine if the application window is entirely covered (when this
1141 * returns true) or if some part of it may be shown (if this returns
1142 * false, though if {@link #isFullscreenMode()} returns true in that case
1143 * then it is probably only a sliver of the application).
1144 */
1145 public boolean isExtractViewShown() {
1146 return mIsFullscreen && !mExtractViewHidden;
1147 }
1148
1149 void updateExtractFrameVisibility() {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001150 final int vis;
The Android Open Source Project10592532009-03-18 17:39:46 -07001151 if (isFullscreenMode()) {
1152 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001153 // "vis" should be applied for the extract frame as well in the fullscreen mode.
1154 mExtractFrame.setVisibility(vis);
The Android Open Source Project10592532009-03-18 17:39:46 -07001155 } else {
1156 vis = View.VISIBLE;
1157 mExtractFrame.setVisibility(View.GONE);
1158 }
1159 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
1160 if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
1161 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1162 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1163 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1164 0);
1165 if (animRes != 0) {
1166 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1167 this, animRes));
1168 }
1169 }
1170 mFullscreenArea.setVisibility(vis);
1171 }
1172
1173 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 * Compute the interesting insets into your UI. The default implementation
1175 * uses the top of the candidates frame for the visible insets, and the
1176 * top of the input frame for the content insets. The default touchable
1177 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1178 *
The Android Open Source Project10592532009-03-18 17:39:46 -07001179 * <p>Note that this method is not called when
1180 * {@link #isExtractViewShown} returns true, since
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 * in that case the application is left as-is behind the input method and
1182 * not impacted by anything in its UI.
1183 *
1184 * @param outInsets Fill in with the current UI insets.
1185 */
1186 public void onComputeInsets(Insets outInsets) {
1187 int[] loc = mTmpLocation;
1188 if (mInputFrame.getVisibility() == View.VISIBLE) {
1189 mInputFrame.getLocationInWindow(loc);
1190 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001191 View decor = getWindow().getWindow().getDecorView();
1192 loc[1] = decor.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001194 if (isFullscreenMode()) {
1195 // In fullscreen mode, we never resize the underlying window.
1196 View decor = getWindow().getWindow().getDecorView();
1197 outInsets.contentTopInsets = decor.getHeight();
1198 } else {
1199 outInsets.contentTopInsets = loc[1];
1200 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1202 mCandidatesFrame.getLocationInWindow(loc);
1203 }
1204 outInsets.visibleTopInsets = loc[1];
1205 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001206 outInsets.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
1208
1209 /**
1210 * Re-evaluate whether the soft input area should currently be shown, and
1211 * update its UI if this has changed since the last time it
1212 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to
1213 * determine whether the input view should currently be shown. You
1214 * can use {@link #isInputViewShown()} to determine if the input view
1215 * is currently shown.
1216 */
1217 public void updateInputViewShown() {
1218 boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
1219 if (mIsInputViewShown != isShown && mWindowVisible) {
1220 mIsInputViewShown = isShown;
1221 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1222 if (mInputView == null) {
1223 initialize();
1224 View v = onCreateInputView();
1225 if (v != null) {
1226 setInputView(v);
1227 }
1228 }
1229 }
1230 }
1231
1232 /**
1233 * Returns true if we have been asked to show our input view.
1234 */
1235 public boolean isShowInputRequested() {
1236 return mShowInputRequested;
1237 }
1238
1239 /**
1240 * Return whether the soft input view is <em>currently</em> shown to the
1241 * user. This is the state that was last determined and
1242 * applied by {@link #updateInputViewShown()}.
1243 */
1244 public boolean isInputViewShown() {
1245 return mIsInputViewShown && mWindowVisible;
1246 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001249 * Override this to control when the soft input area should be shown to the user. The default
1250 * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
1251 * unless the user shows an intention to use software keyboard. If you change what this
1252 * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
1253 * value may have changed to have it re-evaluated and applied.
1254 *
1255 * <p>When you override this method, it is recommended to call
1256 * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
1257 * returned.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258 */
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001259 @CallSuper
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260 public boolean onEvaluateInputViewShown() {
Yohei Yukawacf8403b2016-01-12 11:54:58 -08001261 if (mSettingsObserver == null) {
1262 Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
1263 return false;
1264 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001265 if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
1266 return true;
1267 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 Configuration config = getResources().getConfiguration();
1269 return config.keyboard == Configuration.KEYBOARD_NOKEYS
Ken Wakasa8710e762011-01-30 11:02:09 +09001270 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 /**
1274 * Controls the visibility of the candidates display area. By default
1275 * it is hidden.
1276 */
1277 public void setCandidatesViewShown(boolean shown) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001278 updateCandidatesVisibility(shown);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 if (!mShowInputRequested && mWindowVisible != shown) {
1280 // If we are being asked to show the candidates view while the app
1281 // has not asked for the input view to be shown, then we need
1282 // to update whether the window is shown.
1283 if (shown) {
1284 showWindow(false);
1285 } else {
satok2f913d92012-05-10 01:48:03 +09001286 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 }
1288 }
1289 }
1290
The Android Open Source Project10592532009-03-18 17:39:46 -07001291 void updateCandidatesVisibility(boolean shown) {
1292 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1293 if (mCandidatesVisibility != vis) {
1294 mCandidatesFrame.setVisibility(vis);
1295 mCandidatesVisibility = vis;
1296 }
1297 }
1298
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 /**
1300 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1301 * or {@link View#GONE View.GONE}) of the candidates view when it is not
The Android Open Source Project10592532009-03-18 17:39:46 -07001302 * shown. The default implementation returns GONE when
1303 * {@link #isExtractViewShown} returns true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 * otherwise VISIBLE. Be careful if you change this to return GONE in
1305 * other situations -- if showing or hiding the candidates view causes
1306 * your window to resize, this can cause temporary drawing artifacts as
1307 * the resize takes place.
1308 */
1309 public int getCandidatesHiddenVisibility() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001310 return isExtractViewShown() ? View.GONE : View.INVISIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 }
1312
Tor Norbye7b9c9122013-05-30 16:48:33 -07001313 public void showStatusIcon(@DrawableRes int iconResId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 mStatusIcon = iconResId;
1315 mImm.showStatusIcon(mToken, getPackageName(), iconResId);
1316 }
1317
1318 public void hideStatusIcon() {
1319 mStatusIcon = 0;
1320 mImm.hideStatusIcon(mToken);
1321 }
1322
1323 /**
1324 * Force switch to a new input method, as identified by <var>id</var>. This
1325 * input method will be destroyed, and the requested one started on the
1326 * current input field.
1327 *
1328 * @param id Unique identifier of the new input method ot start.
1329 */
1330 public void switchInputMethod(String id) {
1331 mImm.setInputMethod(mToken, id);
1332 }
1333
1334 public void setExtractView(View view) {
1335 mExtractFrame.removeAllViews();
1336 mExtractFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001337 ViewGroup.LayoutParams.MATCH_PARENT,
1338 ViewGroup.LayoutParams.MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 mExtractView = view;
1340 if (view != null) {
1341 mExtractEditText = (ExtractEditText)view.findViewById(
1342 com.android.internal.R.id.inputExtractEditText);
1343 mExtractEditText.setIME(this);
1344 mExtractAction = (Button)view.findViewById(
1345 com.android.internal.R.id.inputExtractAction);
1346 if (mExtractAction != null) {
1347 mExtractAccessories = (ViewGroup)view.findViewById(
1348 com.android.internal.R.id.inputExtractAccessories);
1349 }
1350 startExtractingText(false);
1351 } else {
1352 mExtractEditText = null;
1353 mExtractAccessories = null;
1354 mExtractAction = null;
1355 }
1356 }
1357
1358 /**
1359 * Replaces the current candidates view with a new one. You only need to
1360 * call this when dynamically changing the view; normally, you should
1361 * implement {@link #onCreateCandidatesView()} and create your view when
1362 * first needed by the input method.
1363 */
1364 public void setCandidatesView(View view) {
1365 mCandidatesFrame.removeAllViews();
1366 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001367 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 ViewGroup.LayoutParams.WRAP_CONTENT));
1369 }
1370
1371 /**
1372 * Replaces the current input view with a new one. You only need to
1373 * call this when dynamically changing the view; normally, you should
1374 * implement {@link #onCreateInputView()} and create your view when
1375 * first needed by the input method.
1376 */
1377 public void setInputView(View view) {
1378 mInputFrame.removeAllViews();
1379 mInputFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001380 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 ViewGroup.LayoutParams.WRAP_CONTENT));
1382 mInputView = view;
1383 }
1384
1385 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 * Called by the framework to create the layout for showing extacted text.
1387 * Only called when in fullscreen mode. The returned view hierarchy must
1388 * have an {@link ExtractEditText} whose ID is
1389 * {@link android.R.id#inputExtractEditText}.
1390 */
1391 public View onCreateExtractTextView() {
1392 return mInflater.inflate(
1393 com.android.internal.R.layout.input_method_extract_view, null);
1394 }
1395
1396 /**
1397 * Create and return the view hierarchy used to show candidates. This will
1398 * be called once, when the candidates are first displayed. You can return
1399 * null to have no candidates view; the default implementation returns null.
1400 *
1401 * <p>To control when the candidates view is displayed, use
1402 * {@link #setCandidatesViewShown(boolean)}.
1403 * To change the candidates view after the first one is created by this
1404 * function, use {@link #setCandidatesView(View)}.
1405 */
1406 public View onCreateCandidatesView() {
1407 return null;
1408 }
1409
1410 /**
1411 * Create and return the view hierarchy used for the input area (such as
1412 * a soft keyboard). This will be called once, when the input area is
1413 * first displayed. You can return null to have no input area; the default
1414 * implementation returns null.
1415 *
1416 * <p>To control when the input view is displayed, implement
1417 * {@link #onEvaluateInputViewShown()}.
1418 * To change the input view after the first one is created by this
1419 * function, use {@link #setInputView(View)}.
1420 */
1421 public View onCreateInputView() {
1422 return null;
1423 }
1424
1425 /**
1426 * Called when the input view is being shown and input has started on
1427 * a new editor. This will always be called after {@link #onStartInput},
1428 * allowing you to do your general setup there and just view-specific
1429 * setup here. You are guaranteed that {@link #onCreateInputView()} will
1430 * have been called some time before this function is called.
1431 *
1432 * @param info Description of the type of text being edited.
1433 * @param restarting Set to true if we are restarting input on the
1434 * same text field as before.
1435 */
1436 public void onStartInputView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001437 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 }
1439
1440 /**
1441 * Called when the input view is being hidden from the user. This will
1442 * be called either prior to hiding the window, or prior to switching to
1443 * another target for editing.
1444 *
1445 * <p>The default
1446 * implementation uses the InputConnection to clear any active composing
1447 * text; you can override this (not calling the base class implementation)
1448 * to perform whatever behavior you would like.
1449 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001450 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 * called immediately after.
1452 */
1453 public void onFinishInputView(boolean finishingInput) {
1454 if (!finishingInput) {
1455 InputConnection ic = getCurrentInputConnection();
1456 if (ic != null) {
1457 ic.finishComposingText();
1458 }
1459 }
1460 }
1461
1462 /**
1463 * Called when only the candidates view has been shown for showing
1464 * processing as the user enters text through a hard keyboard.
1465 * This will always be called after {@link #onStartInput},
1466 * allowing you to do your general setup there and just view-specific
1467 * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
1468 * will have been called some time before this function is called.
1469 *
1470 * <p>Note that this will <em>not</em> be called when the input method
1471 * is running in full editing mode, and thus receiving
1472 * {@link #onStartInputView} to initiate that operation. This is only
1473 * for the case when candidates are being shown while the input method
1474 * editor is hidden but wants to show its candidates UI as text is
1475 * entered through some other mechanism.
1476 *
1477 * @param info Description of the type of text being edited.
1478 * @param restarting Set to true if we are restarting input on the
1479 * same text field as before.
1480 */
1481 public void onStartCandidatesView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001482 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 }
1484
1485 /**
1486 * Called when the candidates view is being hidden from the user. This will
1487 * be called either prior to hiding the window, or prior to switching to
1488 * another target for editing.
1489 *
1490 * <p>The default
1491 * implementation uses the InputConnection to clear any active composing
1492 * text; you can override this (not calling the base class implementation)
1493 * to perform whatever behavior you would like.
1494 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001495 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 * called immediately after.
1497 */
1498 public void onFinishCandidatesView(boolean finishingInput) {
1499 if (!finishingInput) {
1500 InputConnection ic = getCurrentInputConnection();
1501 if (ic != null) {
1502 ic.finishComposingText();
1503 }
1504 }
1505 }
1506
1507 /**
1508 * The system has decided that it may be time to show your input method.
1509 * This is called due to a corresponding call to your
The Android Open Source Project4df24232009-03-05 14:34:35 -08001510 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 * method. The default implementation uses
1512 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
1513 * and the current configuration to decide whether the input view should
1514 * be shown at this point.
1515 *
1516 * @param flags Provides additional information about the show request,
The Android Open Source Project4df24232009-03-05 14:34:35 -08001517 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 * @param configChange This is true if we are re-showing due to a
1519 * configuration change.
1520 * @return Returns true to indicate that the window should be shown.
1521 */
1522 public boolean onShowInputRequested(int flags, boolean configChange) {
1523 if (!onEvaluateInputViewShown()) {
1524 return false;
1525 }
1526 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
1527 if (!configChange && onEvaluateFullscreenMode()) {
1528 // Don't show if this is not explicitly requested by the user and
1529 // the input method is fullscreen. That would be too disruptive.
1530 // However, we skip this change for a config change, since if
1531 // the IME is already shown we do want to go into fullscreen
1532 // mode at this point.
1533 return false;
1534 }
1535 Configuration config = getResources().getConfiguration();
1536 if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
1537 // And if the device has a hard keyboard, even if it is
1538 // currently hidden, don't show the input method implicitly.
1539 // These kinds of devices don't need it that much.
1540 return false;
1541 }
1542 }
1543 if ((flags&InputMethod.SHOW_FORCED) != 0) {
1544 mShowInputForced = true;
1545 }
1546 return true;
1547 }
1548
1549 public void showWindow(boolean showInput) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07001550 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 + " mShowInputRequested=" + mShowInputRequested
1552 + " mWindowAdded=" + mWindowAdded
1553 + " mWindowCreated=" + mWindowCreated
1554 + " mWindowVisible=" + mWindowVisible
1555 + " mInputStarted=" + mInputStarted);
The Android Open Source Project10592532009-03-18 17:39:46 -07001556
1557 if (mInShowWindow) {
1558 Log.w(TAG, "Re-entrance in to showWindow");
1559 return;
1560 }
1561
1562 try {
1563 mWindowWasVisible = mWindowVisible;
1564 mInShowWindow = true;
1565 showWindowInner(showInput);
Yohei Yukawa6fcbb562015-09-14 16:48:15 -07001566 } catch (BadTokenException e) {
1567 // BadTokenException is a normal consequence in certain situations, e.g., swapping IMEs
1568 // while there is a DO_SHOW_SOFT_INPUT message in the IIMethodWrapper queue.
1569 if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
1570 mWindowVisible = false;
1571 mWindowAdded = false;
1572 // Rethrow the exception to preserve the existing behavior. Some IMEs may have directly
1573 // called this method and relied on this exception for some clean-up tasks.
1574 // TODO: Give developers a clear guideline of whether it's OK to call this method or
1575 // InputMethodManager#showSoftInputFromInputMethod() should always be used instead.
1576 throw e;
The Android Open Source Project10592532009-03-18 17:39:46 -07001577 } finally {
Yohei Yukawa6fcbb562015-09-14 16:48:15 -07001578 // TODO: Is it OK to set true when we get BadTokenException?
The Android Open Source Project10592532009-03-18 17:39:46 -07001579 mWindowWasVisible = true;
1580 mInShowWindow = false;
1581 }
1582 }
satok06487a52010-10-29 11:37:18 +09001583
The Android Open Source Project10592532009-03-18 17:39:46 -07001584 void showWindowInner(boolean showInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001585 boolean doShowInput = false;
Seigo Nonaka98d88022015-04-15 18:31:32 +09001586 final int previousImeWindowStatus =
1587 (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588 mWindowVisible = true;
Yohei Yukawaac8bdd22015-09-11 18:17:11 -07001589 if (!mShowInputRequested && mInputStarted && showInput) {
1590 doShowInput = true;
1591 mShowInputRequested = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001592 }
satok06487a52010-10-29 11:37:18 +09001593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 if (DEBUG) Log.v(TAG, "showWindow: updating UI");
1595 initialize();
1596 updateFullscreenMode();
1597 updateInputViewShown();
1598
1599 if (!mWindowAdded || !mWindowCreated) {
1600 mWindowAdded = true;
1601 mWindowCreated = true;
1602 initialize();
1603 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
1604 View v = onCreateCandidatesView();
1605 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
1606 if (v != null) {
1607 setCandidatesView(v);
1608 }
1609 }
1610 if (mShowInputRequested) {
1611 if (!mInputViewStarted) {
1612 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1613 mInputViewStarted = true;
1614 onStartInputView(mInputEditorInfo, false);
1615 }
1616 } else if (!mCandidatesViewStarted) {
1617 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1618 mCandidatesViewStarted = true;
1619 onStartCandidatesView(mInputEditorInfo, false);
1620 }
1621
1622 if (doShowInput) {
1623 startExtractingText(false);
1624 }
satok06487a52010-10-29 11:37:18 +09001625
Seigo Nonaka98d88022015-04-15 18:31:32 +09001626 final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
1627 if (previousImeWindowStatus != nextImeWindowStatus) {
1628 mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
1629 }
1630 if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 if (DEBUG) Log.v(TAG, "showWindow: showing!");
1632 onWindowShown();
1633 mWindow.show();
Yohei Yukawa2977eb72015-05-27 18:54:18 -07001634 // Put here rather than in onWindowShown() in case people forget to call
1635 // super.onWindowShown().
1636 mShouldClearInsetOfPreviousIme = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 }
1638 }
satok06487a52010-10-29 11:37:18 +09001639
satokf17db9f2011-09-14 18:55:58 +09001640 private void finishViews() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 if (mInputViewStarted) {
1642 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1643 onFinishInputView(false);
1644 } else if (mCandidatesViewStarted) {
1645 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1646 onFinishCandidatesView(false);
1647 }
1648 mInputViewStarted = false;
1649 mCandidatesViewStarted = false;
satokf17db9f2011-09-14 18:55:58 +09001650 }
1651
satok2f913d92012-05-10 01:48:03 +09001652 private void doHideWindow() {
1653 mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
1654 hideWindow();
1655 }
1656
satokf17db9f2011-09-14 18:55:58 +09001657 public void hideWindow() {
1658 finishViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 if (mWindowVisible) {
1660 mWindow.hide();
1661 mWindowVisible = false;
1662 onWindowHidden();
The Android Open Source Project10592532009-03-18 17:39:46 -07001663 mWindowWasVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 }
Seigo Nonaka93c47ea2015-07-14 15:05:04 +09001665 updateFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666 }
satok06487a52010-10-29 11:37:18 +09001667
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 /**
1669 * Called when the input method window has been shown to the user, after
1670 * previously not being visible. This is done after all of the UI setup
1671 * for the window has occurred (creating its views etc).
1672 */
1673 public void onWindowShown() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001674 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001675 }
1676
1677 /**
1678 * Called when the input method window has been hidden from the user,
1679 * after previously being visible.
1680 */
1681 public void onWindowHidden() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001682 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001683 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -07001684
1685 /**
1686 * Reset the inset occupied the previous IME when and only when
1687 * {@link #mShouldClearInsetOfPreviousIme} is {@code true}.
1688 */
1689 private void clearInsetOfPreviousIme() {
1690 if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() "
1691 + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
1692 if (!mShouldClearInsetOfPreviousIme || mWindow == null) return;
Seigo Nonakae9372162015-06-04 17:46:27 +09001693 try {
1694 // We do not call onWindowShown() and onWindowHidden() so as not to make the IME author
1695 // confused.
1696 // TODO: Find out a better way which has less side-effect.
1697 mWindow.show();
1698 mWindow.hide();
1699 } catch (WindowManager.BadTokenException e) {
1700 if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme: BadTokenException: IME is done.");
1701 mWindowVisible = false;
1702 mWindowAdded = false;
1703 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -07001704 mShouldClearInsetOfPreviousIme = false;
1705 }
1706
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 /**
1708 * Called when a new client has bound to the input method. This
1709 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
1710 * and {@link #onFinishInput()} calls as the user navigates through its
1711 * UI. Upon this call you know that {@link #getCurrentInputBinding}
1712 * and {@link #getCurrentInputConnection} return valid objects.
1713 */
1714 public void onBindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001715 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 }
1717
1718 /**
1719 * Called when the previous bound client is no longer associated
1720 * with the input method. After returning {@link #getCurrentInputBinding}
1721 * and {@link #getCurrentInputConnection} will no longer return
1722 * valid objects.
1723 */
1724 public void onUnbindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001725 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 }
1727
1728 /**
1729 * Called to inform the input method that text input has started in an
1730 * editor. You should use this callback to initialize the state of your
1731 * input to match the state of the editor given to it.
1732 *
1733 * @param attribute The attributes of the editor that input is starting
1734 * in.
1735 * @param restarting Set to true if input is restarting in the same
1736 * editor such as because the application has changed the text in
1737 * the editor. Otherwise will be false, indicating this is a new
1738 * session with the editor.
1739 */
1740 public void onStartInput(EditorInfo attribute, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001741 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742 }
1743
1744 void doFinishInput() {
1745 if (mInputViewStarted) {
1746 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1747 onFinishInputView(true);
1748 } else if (mCandidatesViewStarted) {
1749 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1750 onFinishCandidatesView(true);
1751 }
1752 mInputViewStarted = false;
1753 mCandidatesViewStarted = false;
1754 if (mInputStarted) {
1755 if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
1756 onFinishInput();
1757 }
1758 mInputStarted = false;
1759 mStartedInputConnection = null;
1760 mCurCompletions = null;
1761 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001762
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
1764 if (!restarting) {
1765 doFinishInput();
1766 }
1767 mInputStarted = true;
1768 mStartedInputConnection = ic;
1769 mInputEditorInfo = attribute;
1770 initialize();
1771 if (DEBUG) Log.v(TAG, "CALL: onStartInput");
1772 onStartInput(attribute, restarting);
1773 if (mWindowVisible) {
1774 if (mShowInputRequested) {
1775 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1776 mInputViewStarted = true;
1777 onStartInputView(mInputEditorInfo, restarting);
1778 startExtractingText(true);
1779 } else if (mCandidatesVisibility == View.VISIBLE) {
1780 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1781 mCandidatesViewStarted = true;
1782 onStartCandidatesView(mInputEditorInfo, restarting);
1783 }
1784 }
1785 }
1786
1787 /**
1788 * Called to inform the input method that text input has finished in
1789 * the last editor. At this point there may be a call to
1790 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
1791 * new editor, or the input method may be left idle. This method is
1792 * <em>not</em> called when input restarts in the same editor.
1793 *
1794 * <p>The default
1795 * implementation uses the InputConnection to clear any active composing
1796 * text; you can override this (not calling the base class implementation)
1797 * to perform whatever behavior you would like.
1798 */
1799 public void onFinishInput() {
1800 InputConnection ic = getCurrentInputConnection();
1801 if (ic != null) {
1802 ic.finishComposingText();
1803 }
1804 }
1805
1806 /**
1807 * Called when the application has reported auto-completion candidates that
1808 * it would like to have the input method displayed. Typically these are
1809 * only used when an input method is running in full-screen mode, since
1810 * otherwise the user can see and interact with the pop-up window of
1811 * completions shown by the application.
1812 *
1813 * <p>The default implementation here does nothing.
1814 */
1815 public void onDisplayCompletions(CompletionInfo[] completions) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001816 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 }
1818
1819 /**
1820 * Called when the application has reported new extracted text to be shown
1821 * due to changes in its current text state. The default implementation
1822 * here places the new text in the extract edit text, when the input
1823 * method is running in fullscreen mode.
1824 */
1825 public void onUpdateExtractedText(int token, ExtractedText text) {
1826 if (mExtractedToken != token) {
1827 return;
1828 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001829 if (text != null) {
1830 if (mExtractEditText != null) {
1831 mExtractedText = text;
1832 mExtractEditText.setExtractedText(text);
1833 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001834 }
1835 }
1836
1837 /**
1838 * Called when the application has reported a new selection region of
1839 * the text. This is called whether or not the input method has requested
1840 * extracted text updates, although if so it will not receive this call
1841 * if the extracted text has changed as well.
Jean Chalardc743cb92013-09-12 16:28:45 +09001842 *
1843 * <p>Be careful about changing the text in reaction to this call with
1844 * methods such as setComposingText, commitText or
1845 * deleteSurroundingText. If the cursor moves as a result, this method
1846 * will be called again, which may result in an infinite loop.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001847 *
1848 * <p>The default implementation takes care of updating the cursor in
1849 * the extract text, if it is being shown.
1850 */
1851 public void onUpdateSelection(int oldSelStart, int oldSelEnd,
1852 int newSelStart, int newSelEnd,
1853 int candidatesStart, int candidatesEnd) {
1854 final ExtractEditText eet = mExtractEditText;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001855 if (eet != null && isFullscreenMode() && mExtractedText != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001856 final int off = mExtractedText.startOffset;
1857 eet.startInternalChanges();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001858 newSelStart -= off;
1859 newSelEnd -= off;
1860 final int len = eet.getText().length();
1861 if (newSelStart < 0) newSelStart = 0;
1862 else if (newSelStart > len) newSelStart = len;
1863 if (newSelEnd < 0) newSelEnd = 0;
1864 else if (newSelEnd > len) newSelEnd = len;
1865 eet.setSelection(newSelStart, newSelEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 eet.finishInternalChanges();
1867 }
1868 }
1869
1870 /**
satok863fcd62011-06-21 17:38:02 +09001871 * Called when the user tapped or clicked a text view.
1872 * IMEs can't rely on this method being called because this was not part of the original IME
1873 * protocol, so applications with custom text editing written before this method appeared will
1874 * not call to inform the IME of this interaction.
1875 * @param focusChanged true if the user changed the focused view by this click.
1876 */
1877 public void onViewClicked(boolean focusChanged) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001878 // Intentionally empty
satok863fcd62011-06-21 17:38:02 +09001879 }
1880
1881 /**
Yohei Yukawaa277db22014-08-21 18:38:44 -07001882 * Called when the application has reported a new location of its text
1883 * cursor. This is only called if explicitly requested by the input method.
1884 * The default implementation does nothing.
1885 * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 */
Yohei Yukawaa277db22014-08-21 18:38:44 -07001887 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 public void onUpdateCursor(Rect newCursor) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001889 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 }
1891
1892 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +09001893 * Called when the application has reported a new location of its text insertion point and
1894 * characters in the composition string. This is only called if explicitly requested by the
1895 * input method. The default implementation does nothing.
1896 * @param cursorAnchorInfo The positional information of the text insertion point and the
1897 * composition string.
1898 */
1899 public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
1900 // Intentionally empty
1901 }
1902
1903 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 * Close this input method's soft input area, removing it from the display.
1905 * The input method will continue running, but the user can no longer use
1906 * it to generate input by touching the screen.
1907 * @param flags Provides additional operating flags. Currently may be
1908 * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
1909 * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
1910 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001911 public void requestHideSelf(int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001912 mImm.hideSoftInputFromInputMethod(mToken, flags);
1913 }
1914
1915 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -08001916 * Show the input method. This is a call back to the
1917 * IMF to handle showing the input method.
The Android Open Source Project4df24232009-03-05 14:34:35 -08001918 * @param flags Provides additional operating flags. Currently may be
1919 * 0 or have the {@link InputMethodManager#SHOW_FORCED
1920 * InputMethodManager.} bit set.
1921 */
1922 private void requestShowSelf(int flags) {
1923 mImm.showSoftInputFromInputMethod(mToken, flags);
1924 }
Andrei Stingaceanu1036c742015-06-11 14:34:50 +00001925
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001926 private boolean handleBack(boolean doIt) {
1927 if (mShowInputRequested) {
1928 // If the soft input area is shown, back closes it and we
1929 // consume the back key.
1930 if (doIt) requestHideSelf(0);
1931 return true;
1932 } else if (mWindowVisible) {
1933 if (mCandidatesVisibility == View.VISIBLE) {
1934 // If we are showing candidates even if no input area, then
1935 // hide them.
1936 if (doIt) setCandidatesViewShown(false);
1937 } else {
1938 // If we have the window visible for some other reason --
1939 // most likely to show candidates -- then just get rid
1940 // of it. This really shouldn't happen, but just in case...
satok2f913d92012-05-10 01:48:03 +09001941 if (doIt) doHideWindow();
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001942 }
1943 return true;
1944 }
1945 return false;
1946 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07001947
1948 /**
1949 * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise
1950 * {@code null} is returned.
1951 */
1952 private ExtractEditText getExtractEditTextIfVisible() {
1953 if (!isExtractViewShown() || !isInputViewShown()) {
1954 return null;
1955 }
1956 return mExtractEditText;
1957 }
1958
The Android Open Source Project4df24232009-03-05 14:34:35 -08001959 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001960 * Override this to intercept key down events before they are processed by the
Quddus Chongee71b1f2012-04-12 11:49:37 -07001961 * application. If you return true, the application will not
1962 * process the event itself. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 * will occur as if the IME had not seen the event at all.
1964 *
1965 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001966 * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
1967 * possibly hide it when the key goes up (if not canceled or long pressed). In
1968 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 * events to move the cursor in the extracted text view, not allowing
1970 * them to perform navigation in the underlying application.
1971 */
1972 public boolean onKeyDown(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001973 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07001974 final ExtractEditText eet = getExtractEditTextIfVisible();
1975 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
1976 return true;
1977 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001978 if (handleBack(false)) {
1979 event.startTracking();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001981 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001982 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001983 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001984 return doMovementKey(keyCode, event, MOVEMENT_DOWN);
1985 }
1986
1987 /**
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001988 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
1989 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
1990 * the event).
1991 */
1992 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1993 return false;
1994 }
1995
1996 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001997 * Override this to intercept special key multiple events before they are
1998 * processed by the
1999 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002000 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002001 * will occur as if the IME had not seen the event at all.
2002 *
2003 * <p>The default implementation always returns false, except when
2004 * in fullscreen mode, where it will consume DPAD movement
2005 * events to move the cursor in the extracted text view, not allowing
2006 * them to perform navigation in the underlying application.
2007 */
2008 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
2009 return doMovementKey(keyCode, event, count);
2010 }
2011
2012 /**
2013 * Override this to intercept key up events before they are processed by the
2014 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002015 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016 * will occur as if the IME had not seen the event at all.
2017 *
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002018 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
2019 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
2020 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 * events to move the cursor in the extracted text view, not allowing
2022 * them to perform navigation in the underlying application.
2023 */
2024 public boolean onKeyUp(int keyCode, KeyEvent event) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002025 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
2026 final ExtractEditText eet = getExtractEditTextIfVisible();
2027 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2028 return true;
2029 }
2030 if (event.isTracking() && !event.isCanceled()) {
2031 return handleBack(true);
2032 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002033 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002034 return doMovementKey(keyCode, event, MOVEMENT_UP);
2035 }
2036
Victoria Leaseb38070c2012-08-24 13:46:02 -07002037 /**
2038 * Override this to intercept trackball motion events before they are
2039 * processed by the application.
2040 * If you return true, the application will not itself process the event.
2041 * If you return false, the normal application processing will occur as if
2042 * the IME had not seen the event at all.
2043 */
satokab751aa2010-09-14 19:17:36 +09002044 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002045 public boolean onTrackballEvent(MotionEvent event) {
Victoria Leaseb38070c2012-08-24 13:46:02 -07002046 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
2047 return false;
2048 }
2049
2050 /**
2051 * Override this to intercept generic motion events before they are
2052 * processed by the application.
2053 * If you return true, the application will not itself process the event.
2054 * If you return false, the normal application processing will occur as if
2055 * the IME had not seen the event at all.
2056 */
2057 @Override
2058 public boolean onGenericMotionEvent(MotionEvent event) {
2059 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002060 return false;
2061 }
2062
2063 public void onAppPrivateCommand(String action, Bundle data) {
2064 }
2065
The Android Open Source Project4df24232009-03-05 14:34:35 -08002066 /**
2067 * Handle a request by the system to toggle the soft input area.
2068 */
2069 private void onToggleSoftInput(int showFlags, int hideFlags) {
2070 if (DEBUG) Log.v(TAG, "toggleSoftInput()");
2071 if (isInputViewShown()) {
2072 requestHideSelf(hideFlags);
2073 } else {
2074 requestShowSelf(showFlags);
2075 }
2076 }
2077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002078 static final int MOVEMENT_DOWN = -1;
2079 static final int MOVEMENT_UP = -2;
2080
2081 void reportExtractedMovement(int keyCode, int count) {
2082 int dx = 0, dy = 0;
2083 switch (keyCode) {
2084 case KeyEvent.KEYCODE_DPAD_LEFT:
2085 dx = -count;
2086 break;
2087 case KeyEvent.KEYCODE_DPAD_RIGHT:
2088 dx = count;
2089 break;
2090 case KeyEvent.KEYCODE_DPAD_UP:
2091 dy = -count;
2092 break;
2093 case KeyEvent.KEYCODE_DPAD_DOWN:
2094 dy = count;
2095 break;
2096 }
satokab751aa2010-09-14 19:17:36 +09002097 onExtractedCursorMovement(dx, dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002098 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002099
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002100 boolean doMovementKey(int keyCode, KeyEvent event, int count) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002101 final ExtractEditText eet = getExtractEditTextIfVisible();
2102 if (eet != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002103 // If we are in fullscreen mode, the cursor will move around
2104 // the extract edit text, but should NOT cause focus to move
2105 // to other fields.
2106 MovementMethod movement = eet.getMovementMethod();
2107 Layout layout = eet.getLayout();
2108 if (movement != null && layout != null) {
2109 // We want our own movement method to handle the key, so the
2110 // cursor will properly move in our own word wrapping.
2111 if (count == MOVEMENT_DOWN) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002112 if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002113 reportExtractedMovement(keyCode, 1);
2114 return true;
2115 }
2116 } else if (count == MOVEMENT_UP) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002117 if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002118 return true;
2119 }
2120 } else {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002121 if (movement.onKeyOther(eet, eet.getText(), event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002122 reportExtractedMovement(keyCode, count);
2123 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07002124 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002125 if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002126 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002127 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002128 while (--count > 0) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002129 movement.onKeyDown(eet, eet.getText(), keyCode, down);
2130 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002131 }
2132 reportExtractedMovement(keyCode, count);
2133 }
2134 }
2135 }
2136 }
2137 // Regardless of whether the movement method handled the key,
2138 // we never allow DPAD navigation to the application.
2139 switch (keyCode) {
2140 case KeyEvent.KEYCODE_DPAD_LEFT:
2141 case KeyEvent.KEYCODE_DPAD_RIGHT:
2142 case KeyEvent.KEYCODE_DPAD_UP:
2143 case KeyEvent.KEYCODE_DPAD_DOWN:
2144 return true;
2145 }
2146 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002148 return false;
2149 }
2150
2151 /**
2152 * Send the given key event code (as defined by {@link KeyEvent}) to the
2153 * current input connection is a key down + key up event pair. The sent
2154 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
2155 * set, so that the recipient can identify them as coming from a software
2156 * input method, and
2157 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
2158 * that they don't impact the current touch mode of the UI.
2159 *
Jean Chalard405bc512012-05-29 19:12:34 +09002160 * <p>Note that it's discouraged to send such key events in normal operation;
2161 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
2162 * text fields, or for non-rich input methods. A reasonably capable software
2163 * input method should use the
2164 * {@link android.view.inputmethod.InputConnection#commitText} family of methods
2165 * to send text to an application, rather than sending key events.</p>
2166 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002167 * @param keyEventCode The raw key code to send, as defined by
2168 * {@link KeyEvent}.
2169 */
2170 public void sendDownUpKeyEvents(int keyEventCode) {
2171 InputConnection ic = getCurrentInputConnection();
2172 if (ic == null) return;
2173 long eventTime = SystemClock.uptimeMillis();
2174 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002175 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002176 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
Tadashi G. Takaokacb95cd62012-10-26 17:20:59 +09002177 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002178 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002179 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2180 }
2181
2182 /**
2183 * Ask the input target to execute its default action via
2184 * {@link InputConnection#performEditorAction
2185 * InputConnection.performEditorAction()}.
2186 *
2187 * @param fromEnterKey If true, this will be executed as if the user had
2188 * pressed an enter key on the keyboard, that is it will <em>not</em>
2189 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
2190 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
2191 * sent regardless of how the editor has set that flag.
2192 *
2193 * @return Returns a boolean indicating whether an action has been sent.
2194 * If false, either the editor did not specify a default action or it
2195 * does not want an action from the enter key. If true, the action was
2196 * sent (or there was no input connection at all).
2197 */
2198 public boolean sendDefaultEditorAction(boolean fromEnterKey) {
2199 EditorInfo ei = getCurrentInputEditorInfo();
2200 if (ei != null &&
2201 (!fromEnterKey || (ei.imeOptions &
2202 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
2203 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
2204 EditorInfo.IME_ACTION_NONE) {
2205 // If the enter key was pressed, and the editor has a default
2206 // action associated with pressing enter, then send it that
2207 // explicit action instead of the key event.
2208 InputConnection ic = getCurrentInputConnection();
2209 if (ic != null) {
2210 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
2211 }
2212 return true;
2213 }
2214
2215 return false;
2216 }
2217
2218 /**
2219 * Send the given UTF-16 character to the current input connection. Most
2220 * characters will be delivered simply by calling
2221 * {@link InputConnection#commitText InputConnection.commitText()} with
2222 * the character; some, however, may be handled different. In particular,
2223 * the enter character ('\n') will either be delivered as an action code
Jean Chalard405bc512012-05-29 19:12:34 +09002224 * or a raw key event, as appropriate. Consider this as a convenience
2225 * method for IMEs that do not have a full implementation of actions; a
2226 * fully complying IME will decide of the right action for each event and
2227 * will likely never call this method except maybe to handle events coming
2228 * from an actual hardware keyboard.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002229 *
2230 * @param charCode The UTF-16 character code to send.
2231 */
2232 public void sendKeyChar(char charCode) {
2233 switch (charCode) {
2234 case '\n': // Apps may be listening to an enter key to perform an action
2235 if (!sendDefaultEditorAction(true)) {
2236 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2237 }
2238 break;
2239 default:
2240 // Make sure that digits go through any text watcher on the client side.
2241 if (charCode >= '0' && charCode <= '9') {
2242 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2243 } else {
2244 InputConnection ic = getCurrentInputConnection();
2245 if (ic != null) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002246 ic.commitText(String.valueOf(charCode), 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002247 }
2248 }
2249 break;
2250 }
2251 }
2252
2253 /**
2254 * This is called when the user has moved the cursor in the extracted
2255 * text view, when running in fullsreen mode. The default implementation
2256 * performs the corresponding selection change on the underlying text
2257 * editor.
2258 */
2259 public void onExtractedSelectionChanged(int start, int end) {
2260 InputConnection conn = getCurrentInputConnection();
2261 if (conn != null) {
2262 conn.setSelection(start, end);
2263 }
2264 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002265
2266 /**
2267 * @hide
2268 */
2269 public void onExtractedDeleteText(int start, int end) {
2270 InputConnection conn = getCurrentInputConnection();
2271 if (conn != null) {
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002272 conn.finishComposingText();
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002273 conn.setSelection(start, start);
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002274 conn.deleteSurroundingText(0, end - start);
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002275 }
2276 }
2277
2278 /**
2279 * @hide
2280 */
2281 public void onExtractedReplaceText(int start, int end, CharSequence text) {
2282 InputConnection conn = getCurrentInputConnection();
2283 if (conn != null) {
2284 conn.setComposingRegion(start, end);
2285 conn.commitText(text, 1);
2286 }
2287 }
2288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002289 /**
Gilles Debunnee300be92011-12-06 10:15:56 -08002290 * @hide
2291 */
2292 public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2293 InputConnection conn = getCurrentInputConnection();
2294 if (conn != null) {
2295 if (!conn.setSelection(start, end)) return;
2296 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2297 if (text instanceof Spannable) {
2298 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2299 conn.setComposingRegion(start, end);
2300 conn.commitText(text, 1);
2301 }
2302 }
2303 }
2304
2305 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002306 * This is called when the user has clicked on the extracted text view,
2307 * when running in fullscreen mode. The default implementation hides
2308 * the candidates view when this happens, but only if the extracted text
2309 * editor has a vertical scroll bar because its text doesn't fit.
2310 * Re-implement this to provide whatever behavior you want.
2311 */
2312 public void onExtractedTextClicked() {
2313 if (mExtractEditText == null) {
2314 return;
2315 }
2316 if (mExtractEditText.hasVerticalScrollBar()) {
2317 setCandidatesViewShown(false);
2318 }
2319 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002320
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002321 /**
2322 * This is called when the user has performed a cursor movement in the
2323 * extracted text view, when it is running in fullscreen mode. The default
2324 * implementation hides the candidates view when a vertical movement
2325 * happens, but only if the extracted text editor has a vertical scroll bar
2326 * because its text doesn't fit.
2327 * Re-implement this to provide whatever behavior you want.
2328 * @param dx The amount of cursor movement in the x dimension.
2329 * @param dy The amount of cursor movement in the y dimension.
2330 */
2331 public void onExtractedCursorMovement(int dx, int dy) {
2332 if (mExtractEditText == null || dy == 0) {
2333 return;
2334 }
2335 if (mExtractEditText.hasVerticalScrollBar()) {
2336 setCandidatesViewShown(false);
2337 }
2338 }
2339
2340 /**
2341 * This is called when the user has selected a context menu item from the
2342 * extracted text view, when running in fullscreen mode. The default
2343 * implementation sends this action to the current InputConnection's
2344 * {@link InputConnection#performContextMenuAction(int)}, for it
2345 * to be processed in underlying "real" editor. Re-implement this to
2346 * provide whatever behavior you want.
2347 */
2348 public boolean onExtractTextContextMenuItem(int id) {
2349 InputConnection ic = getCurrentInputConnection();
2350 if (ic != null) {
2351 ic.performContextMenuAction(id);
2352 }
2353 return true;
2354 }
2355
2356 /**
2357 * Return text that can be used as a button label for the given
2358 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
2359 * if there is no action requested. Note that there is no guarantee that
2360 * the returned text will be relatively short, so you probably do not
2361 * want to use it as text on a soft keyboard key label.
2362 *
2363 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
2364 *
2365 * @return Returns a label to use, or null if there is no action.
2366 */
2367 public CharSequence getTextForImeAction(int imeOptions) {
2368 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2369 case EditorInfo.IME_ACTION_NONE:
2370 return null;
2371 case EditorInfo.IME_ACTION_GO:
2372 return getText(com.android.internal.R.string.ime_action_go);
2373 case EditorInfo.IME_ACTION_SEARCH:
2374 return getText(com.android.internal.R.string.ime_action_search);
2375 case EditorInfo.IME_ACTION_SEND:
2376 return getText(com.android.internal.R.string.ime_action_send);
2377 case EditorInfo.IME_ACTION_NEXT:
2378 return getText(com.android.internal.R.string.ime_action_next);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002379 case EditorInfo.IME_ACTION_DONE:
2380 return getText(com.android.internal.R.string.ime_action_done);
Dianne Hackborndea3ef72010-10-28 14:24:22 -07002381 case EditorInfo.IME_ACTION_PREVIOUS:
2382 return getText(com.android.internal.R.string.ime_action_previous);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002383 default:
2384 return getText(com.android.internal.R.string.ime_action_default);
2385 }
2386 }
2387
2388 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07002389 * Called when the fullscreen-mode extracting editor info has changed,
2390 * to determine whether the extracting (extract text and candidates) portion
2391 * of the UI should be shown. The standard implementation hides or shows
2392 * the extract area depending on whether it makes sense for the
2393 * current editor. In particular, a {@link InputType#TYPE_NULL}
2394 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
2395 * turn off the extract area since there is no text to be shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002396 */
The Android Open Source Project10592532009-03-18 17:39:46 -07002397 public void onUpdateExtractingVisibility(EditorInfo ei) {
2398 if (ei.inputType == InputType.TYPE_NULL ||
2399 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
2400 // No reason to show extract UI!
2401 setExtractViewShown(false);
2402 return;
2403 }
2404
2405 setExtractViewShown(true);
2406 }
2407
2408 /**
2409 * Called when the fullscreen-mode extracting editor info has changed,
2410 * to update the state of its UI such as the action buttons shown.
2411 * You do not need to deal with this if you are using the standard
2412 * full screen extract UI. If replacing it, you will need to re-implement
2413 * this to put the appropriate action button in your own UI and handle it,
2414 * and perform any other changes.
2415 *
2416 * <p>The standard implementation turns on or off its accessory area
2417 * depending on whether there is an action button, and hides or shows
2418 * the entire extract area depending on whether it makes sense for the
2419 * current editor. In particular, a {@link InputType#TYPE_NULL} or
2420 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
2421 * extract area since there is no text to be shown.
2422 */
2423 public void onUpdateExtractingViews(EditorInfo ei) {
2424 if (!isExtractViewShown()) {
2425 return;
2426 }
2427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002428 if (mExtractAccessories == null) {
2429 return;
2430 }
2431 final boolean hasAction = ei.actionLabel != null || (
2432 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
The Android Open Source Project10592532009-03-18 17:39:46 -07002433 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
2434 ei.inputType != InputType.TYPE_NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002435 if (hasAction) {
2436 mExtractAccessories.setVisibility(View.VISIBLE);
Steve Kondik59eb6912009-09-07 22:53:34 -04002437 if (mExtractAction != null) {
2438 if (ei.actionLabel != null) {
2439 mExtractAction.setText(ei.actionLabel);
2440 } else {
2441 mExtractAction.setText(getTextForImeAction(ei.imeOptions));
2442 }
2443 mExtractAction.setOnClickListener(mActionClickListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 } else {
2446 mExtractAccessories.setVisibility(View.GONE);
Steve Kondik59eb6912009-09-07 22:53:34 -04002447 if (mExtractAction != null) {
2448 mExtractAction.setOnClickListener(null);
2449 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002450 }
2451 }
2452
2453 /**
2454 * This is called when, while currently displayed in extract mode, the
2455 * current input target changes. The default implementation will
2456 * auto-hide the IME if the new target is not a full editor, since this
Ken Wakasaf76a50c2012-03-09 19:56:35 +09002457 * can be a confusing experience for the user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002458 */
2459 public void onExtractingInputChanged(EditorInfo ei) {
2460 if (ei.inputType == InputType.TYPE_NULL) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002461 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002462 }
2463 }
2464
2465 void startExtractingText(boolean inputChanged) {
2466 final ExtractEditText eet = mExtractEditText;
2467 if (eet != null && getCurrentInputStarted()
2468 && isFullscreenMode()) {
2469 mExtractedToken++;
2470 ExtractedTextRequest req = new ExtractedTextRequest();
2471 req.token = mExtractedToken;
2472 req.flags = InputConnection.GET_TEXT_WITH_STYLES;
2473 req.hintMaxLines = 10;
2474 req.hintMaxChars = 10000;
Amith Yamasaniba4d93f2009-08-19 18:27:56 -07002475 InputConnection ic = getCurrentInputConnection();
2476 mExtractedText = ic == null? null
2477 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
Amith Yamasania8b00c82010-03-05 15:41:31 -08002478 if (mExtractedText == null || ic == null) {
2479 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
2480 + mExtractedText + ", input connection = " + ic);
2481 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002482 final EditorInfo ei = getCurrentInputEditorInfo();
2483
2484 try {
2485 eet.startInternalChanges();
The Android Open Source Project10592532009-03-18 17:39:46 -07002486 onUpdateExtractingVisibility(ei);
2487 onUpdateExtractingViews(ei);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002488 int inputType = ei.inputType;
2489 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
2490 == EditorInfo.TYPE_CLASS_TEXT) {
2491 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
2492 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2493 }
2494 }
2495 eet.setInputType(inputType);
2496 eet.setHint(ei.hintText);
2497 if (mExtractedText != null) {
2498 eet.setEnabled(true);
2499 eet.setExtractedText(mExtractedText);
2500 } else {
2501 eet.setEnabled(false);
2502 eet.setText("");
2503 }
2504 } finally {
2505 eet.finishInternalChanges();
2506 }
2507
2508 if (inputChanged) {
2509 onExtractingInputChanged(ei);
2510 }
2511 }
2512 }
satokab751aa2010-09-14 19:17:36 +09002513
2514 // TODO: Handle the subtype change event
2515 /**
2516 * Called when the subtype was changed.
2517 * @param newSubtype the subtype which is being changed to.
2518 */
2519 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
2520 if (DEBUG) {
2521 int nameResId = newSubtype.getNameResId();
satok9ef02832010-11-04 21:17:48 +09002522 String mode = newSubtype.getMode();
satokab751aa2010-09-14 19:17:36 +09002523 String output = "changeInputMethodSubtype:"
2524 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
satok9ef02832010-11-04 21:17:48 +09002525 + mode + ","
satokab751aa2010-09-14 19:17:36 +09002526 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
2527 Log.v(TAG, "--- " + output);
2528 }
2529 }
2530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002531 /**
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09002532 * @return The recommended height of the input method window.
2533 * An IME author can get the last input method's height as the recommended height
2534 * by calling this in
2535 * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}.
2536 * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME
2537 * switching by using this value as a visible inset height. It's efficient for the smooth
2538 * transition between different IMEs. However, note that this may return 0 (or possibly
2539 * unexpectedly low height). You should thus avoid relying on the return value of this method
2540 * all the time. Please make sure to use a reasonable height for the IME.
2541 */
2542 public int getInputMethodWindowRecommendedHeight() {
2543 return mImm.getInputMethodWindowVisibleHeight();
2544 }
2545
2546 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002547 * Performs a dump of the InputMethodService's internal state. Override
2548 * to add your own information to the dump.
2549 */
2550 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2551 final Printer p = new PrintWriterPrinter(fout);
2552 p.println("Input method service state for " + this + ":");
2553 p.println(" mWindowCreated=" + mWindowCreated
The Android Open Source Project10592532009-03-18 17:39:46 -07002554 + " mWindowAdded=" + mWindowAdded);
2555 p.println(" mWindowVisible=" + mWindowVisible
2556 + " mWindowWasVisible=" + mWindowWasVisible
2557 + " mInShowWindow=" + mInShowWindow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002558 p.println(" Configuration=" + getResources().getConfiguration());
2559 p.println(" mToken=" + mToken);
2560 p.println(" mInputBinding=" + mInputBinding);
2561 p.println(" mInputConnection=" + mInputConnection);
2562 p.println(" mStartedInputConnection=" + mStartedInputConnection);
2563 p.println(" mInputStarted=" + mInputStarted
2564 + " mInputViewStarted=" + mInputViewStarted
2565 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
2566
2567 if (mInputEditorInfo != null) {
2568 p.println(" mInputEditorInfo:");
2569 mInputEditorInfo.dump(p, " ");
2570 } else {
2571 p.println(" mInputEditorInfo: null");
2572 }
2573
2574 p.println(" mShowInputRequested=" + mShowInputRequested
2575 + " mLastShowInputRequested=" + mLastShowInputRequested
2576 + " mShowInputForced=" + mShowInputForced
2577 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
2578 p.println(" mCandidatesVisibility=" + mCandidatesVisibility
2579 + " mFullscreenApplied=" + mFullscreenApplied
The Android Open Source Project10592532009-03-18 17:39:46 -07002580 + " mIsFullscreen=" + mIsFullscreen
2581 + " mExtractViewHidden=" + mExtractViewHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002582
2583 if (mExtractedText != null) {
2584 p.println(" mExtractedText:");
2585 p.println(" text=" + mExtractedText.text.length() + " chars"
2586 + " startOffset=" + mExtractedText.startOffset);
2587 p.println(" selectionStart=" + mExtractedText.selectionStart
2588 + " selectionEnd=" + mExtractedText.selectionEnd
2589 + " flags=0x" + Integer.toHexString(mExtractedText.flags));
2590 } else {
2591 p.println(" mExtractedText: null");
2592 }
2593 p.println(" mExtractedToken=" + mExtractedToken);
2594 p.println(" mIsInputViewShown=" + mIsInputViewShown
2595 + " mStatusIcon=" + mStatusIcon);
2596 p.println("Last computed insets:");
2597 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
2598 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
Jeff Brownfbf09772011-01-16 14:06:57 -08002599 + " touchableInsets=" + mTmpInsets.touchableInsets
2600 + " touchableRegion=" + mTmpInsets.touchableRegion);
Yohei Yukawa2977eb72015-05-27 18:54:18 -07002601 p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
Yohei Yukawa7b739a82015-12-21 13:30:44 -08002602 p.println(" mSettingsObserver=" + mSettingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002603 }
2604}