blob: 5647bf90d2fbb628e42991bd16e5c18c98f90bd6 [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;
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070021import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
Jorim Jaggi0da8fd12020-01-10 01:23:21 +010022import static android.view.WindowInsets.Type.navigationBars;
Tiger Huangc8364e32020-03-10 21:20:58 +080023import static android.view.WindowInsets.Type.statusBars;
Yohei Yukawa8f162c62018-01-10 13:18:09 -080024import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
Yohei Yukawa386f50e2018-03-14 13:03:42 -070026import static java.lang.annotation.RetentionPolicy.SOURCE;
27
Yohei Yukawac07fd4c2018-09-11 11:37:13 -070028import android.annotation.AnyThread;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080029import android.annotation.CallSuper;
Tor Norbye7b9c9122013-05-30 16:48:33 -070030import android.annotation.DrawableRes;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080031import android.annotation.IntDef;
32import android.annotation.MainThread;
Yohei Yukawa25e08132016-06-22 16:31:41 -070033import android.annotation.NonNull;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -080034import android.annotation.Nullable;
Dianne Hackborn836531b2012-08-01 19:00:38 -070035import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.app.Dialog;
Artur Satayev26958002019-12-10 17:47:52 +000037import android.compat.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.content.Context;
JianYang Liu7eec3162020-01-23 17:09:26 -080039import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.content.res.Configuration;
Dianne Hackbornd922ae02011-01-14 11:43:24 -080041import android.content.res.Resources;
The Android Open Source Project10592532009-03-18 17:39:46 -070042import android.content.res.TypedArray;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080043import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.graphics.Rect;
Jeff Brownfbf09772011-01-16 14:06:57 -080045import android.graphics.Region;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080046import android.net.Uri;
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -080047import android.os.Binder;
Mathew Inwood31755f92018-12-20 13:53:36 +000048import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Bundle;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080050import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.IBinder;
The Android Open Source Project4df24232009-03-05 14:34:35 -080052import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.SystemClock;
54import android.provider.Settings;
55import android.text.InputType;
56import android.text.Layout;
57import android.text.Spannable;
58import android.text.method.MovementMethod;
59import android.util.Log;
60import android.util.PrintWriterPrinter;
61import android.util.Printer;
Dianne Hackborne30e02f2014-05-27 18:24:45 -070062import android.view.Gravity;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080063import android.view.KeyCharacterMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.view.KeyEvent;
65import android.view.LayoutInflater;
66import android.view.MotionEvent;
67import android.view.View;
68import android.view.ViewGroup;
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070069import android.view.ViewRootImpl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.view.ViewTreeObserver;
71import android.view.Window;
Tiger Huang4a7835f2019-11-06 00:07:56 +080072import android.view.WindowInsets;
Tiger Huangc8364e32020-03-10 21:20:58 +080073import android.view.WindowInsets.Side;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import android.view.WindowManager;
The Android Open Source Project10592532009-03-18 17:39:46 -070075import android.view.animation.AnimationUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import android.view.inputmethod.CompletionInfo;
Yohei Yukawac2ddd602014-05-06 21:22:49 +090077import android.view.inputmethod.CursorAnchorInfo;
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080078import android.view.inputmethod.EditorInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079import android.view.inputmethod.ExtractedText;
80import android.view.inputmethod.ExtractedTextRequest;
Adam Hebc67f2e2019-11-13 14:34:56 -080081import android.view.inputmethod.InlineSuggestionsRequest;
82import android.view.inputmethod.InlineSuggestionsResponse;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083import android.view.inputmethod.InputBinding;
84import android.view.inputmethod.InputConnection;
Yohei Yukawa25e08132016-06-22 16:31:41 -070085import android.view.inputmethod.InputContentInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086import android.view.inputmethod.InputMethod;
87import android.view.inputmethod.InputMethodManager;
satokab751aa2010-09-14 19:17:36 +090088import android.view.inputmethod.InputMethodSubtype;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089import android.widget.FrameLayout;
Mark Renouf91eb2652016-04-11 16:03:26 -040090import android.widget.ImageButton;
The Android Open Source Project10592532009-03-18 17:39:46 -070091import android.widget.LinearLayout;
Mark Renouf91eb2652016-04-11 16:03:26 -040092import android.widget.TextView;
Charles Chen49f329c2020-02-13 16:41:32 +080093import android.window.WindowMetricsHelper;
The Android Open Source Project10592532009-03-18 17:39:46 -070094
Yohei Yukawac07fd4c2018-09-11 11:37:13 -070095import com.android.internal.annotations.GuardedBy;
Yohei Yukawac54c1172018-09-06 11:39:50 -070096import com.android.internal.inputmethod.IInputContentUriToken;
97import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -070098import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
Yohei Yukawad746a7e2018-09-18 18:55:02 -070099import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
Adam Hebc67f2e2019-11-13 14:34:56 -0800100import com.android.internal.view.IInlineSuggestionsRequestCallback;
Feng Cao36960ee2020-02-18 18:23:30 -0800101import com.android.internal.view.InlineSuggestionsRequestInfo;
Yohei Yukawac54c1172018-09-06 11:39:50 -0700102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103import java.io.FileDescriptor;
104import java.io.PrintWriter;
Yohei Yukawa7b739a82015-12-21 13:30:44 -0800105import java.lang.annotation.Retention;
106import java.lang.annotation.RetentionPolicy;
Vinit Nayak31595bc2019-09-30 15:04:19 -0700107import java.util.Collections;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
109/**
110 * InputMethodService provides a standard implementation of an InputMethod,
111 * which final implementations can derive from and customize. See the
112 * base class {@link AbstractInputMethodService} and the {@link InputMethod}
113 * interface for more information on the basics of writing input methods.
114 *
115 * <p>In addition to the normal Service lifecycle methods, this class
116 * introduces some new specific callbacks that most subclasses will want
117 * to make use of:</p>
118 * <ul>
119 * <li> {@link #onInitializeInterface()} for user-interface initialization,
120 * in particular to deal with configuration changes while the service is
121 * running.
122 * <li> {@link #onBindInput} to find out about switching to a new client.
123 * <li> {@link #onStartInput} to deal with an input session starting with
124 * the client.
125 * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
126 * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
127 * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
128 * starting within the input area of the IME.
129 * </ul>
130 *
131 * <p>An input method has significant discretion in how it goes about its
132 * work: the {@link android.inputmethodservice.InputMethodService} provides
133 * a basic framework for standard UI elements (input view, candidates view,
134 * and running in fullscreen mode), but it is up to a particular implementor
135 * to decide how to use them. For example, one input method could implement
136 * an input area with a keyboard, another could allow the user to draw text,
137 * while a third could have no input area (and thus not be visible to the
138 * user) but instead listen to audio and perform text to speech conversion.</p>
139 *
140 * <p>In the implementation provided here, all of these elements are placed
141 * together in a single window managed by the InputMethodService. It will
142 * execute callbacks as it needs information about them, and provides APIs for
143 * programmatic control over them. They layout of these elements is explicitly
144 * defined:</p>
145 *
146 * <ul>
147 * <li>The soft input view, if available, is placed at the bottom of the
148 * screen.
149 * <li>The candidates view, if currently shown, is placed above the soft
150 * input view.
151 * <li>If not running fullscreen, the application is moved or resized to be
152 * above these views; if running fullscreen, the window will completely cover
153 * the application and its top part will contain the extract text of what is
154 * currently being edited by the application.
155 * </ul>
156 *
157 *
158 * <a name="SoftInputView"></a>
159 * <h3>Soft Input View</h3>
160 *
161 * <p>Central to most input methods is the soft input view. This is where most
162 * user interaction occurs: pressing on soft keys, drawing characters, or
163 * however else your input method wants to generate text. Most implementations
164 * will simply have their own view doing all of this work, and return a new
165 * instance of it when {@link #onCreateInputView()} is called. At that point,
166 * as long as the input view is visible, you will see user interaction in
167 * that view and can call back on the InputMethodService to interact with the
168 * application as appropriate.</p>
169 *
170 * <p>There are some situations where you want to decide whether or not your
171 * soft input view should be shown to the user. This is done by implementing
172 * the {@link #onEvaluateInputViewShown()} to return true or false based on
173 * whether it should be shown in the current environment. If any of your
174 * state has changed that may impact this, call
175 * {@link #updateInputViewShown()} to have it re-evaluated. The default
176 * implementation always shows the input view unless there is a hard
177 * keyboard available, which is the appropriate behavior for most input
178 * methods.</p>
179 *
180 *
181 * <a name="CandidatesView"></a>
182 * <h3>Candidates View</h3>
183 *
184 * <p>Often while the user is generating raw text, an input method wants to
185 * provide them with a list of possible interpretations of that text that can
186 * be selected for use. This is accomplished with the candidates view, and
187 * like the soft input view you implement {@link #onCreateCandidatesView()}
188 * to instantiate your own view implementing your candidates UI.</p>
189 *
190 * <p>Management of the candidates view is a little different than the input
191 * view, because the candidates view tends to be more transient, being shown
192 * only when there are possible candidates for the current text being entered
193 * by the user. To control whether the candidates view is shown, you use
194 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate
195 * view tends to be shown and hidden a lot, it does not impact the application
196 * UI in the same way as the soft input view: it will never cause application
197 * windows to resize, only cause them to be panned if needed for the user to
198 * see the current focus.</p>
199 *
200 *
201 * <a name="FullscreenMode"></a>
202 * <h3>Fullscreen Mode</h3>
203 *
204 * <p>Sometimes your input method UI is too large to integrate with the
205 * application UI, so you just want to take over the screen. This is
206 * accomplished by switching to full-screen mode, causing the input method
207 * window to fill the entire screen and add its own "extracted text" editor
208 * showing the user the text that is being typed. Unlike the other UI elements,
209 * there is a standard implementation for the extract editor that you should
210 * not need to change. The editor is placed at the top of the IME, above the
211 * input and candidates views.</p>
212 *
213 * <p>Similar to the input view, you control whether the IME is running in
214 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
215 * to return true or false based on
216 * whether it should be fullscreen in the current environment. If any of your
217 * state has changed that may impact this, call
218 * {@link #updateFullscreenMode()} to have it re-evaluated. The default
219 * implementation selects fullscreen mode when the screen is in a landscape
220 * orientation, which is appropriate behavior for most input methods that have
221 * a significant input area.</p>
222 *
223 * <p>When in fullscreen mode, you have some special requirements because the
224 * user can not see the application UI. In particular, you should implement
225 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
226 * generated by your application, typically in your candidates view like you
227 * would normally show candidates.
228 *
229 *
230 * <a name="GeneratingText"></a>
231 * <h3>Generating Text</h3>
232 *
233 * <p>The key part of an IME is of course generating text for the application.
234 * This is done through calls to the
235 * {@link android.view.inputmethod.InputConnection} interface to the
236 * application, which can be retrieved from {@link #getCurrentInputConnection()}.
237 * This interface allows you to generate raw key events or, if the target
238 * supports it, directly edit in strings of candidates and committed text.</p>
239 *
240 * <p>Information about what the target is expected and supports can be found
241 * through the {@link android.view.inputmethod.EditorInfo} class, which is
242 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most
243 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
244 * EditorInfo.inputType}; in particular, if this is
245 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
246 * then the target does not support complex edits and you need to only deliver
247 * raw key events to it. An input method will also want to look at other
248 * values here, to for example detect password mode, auto complete text views,
249 * phone number entry, etc.</p>
250 *
251 * <p>When the user switches between input targets, you will receive calls to
252 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
253 * You can use these to reset and initialize your input state for the current
254 * target. For example, you will often want to clear any input state, and
255 * update a soft keyboard to be appropriate for the new inputType.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -0700256 *
257 * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
258 * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
259 * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 */
261public class InputMethodService extends AbstractInputMethodService {
262 static final String TAG = "InputMethodService";
263 static final boolean DEBUG = false;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800264
265 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700266 * Allows the system to optimize the back button affordance based on the presence of software
267 * keyboard.
268 *
269 * <p>For instance, on devices that have navigation bar and software-rendered back button, the
270 * system may use a different icon while {@link #isInputViewShown()} returns {@code true}, to
271 * indicate that the back button has "dismiss" affordance.</p>
272 *
273 * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
274 * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
275 * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
276 * not take this mode into account.</p>
277 *
278 * <p>For API level {@link android.os.Build.VERSION_CODES#O_MR1} and lower devices, this is the
279 * only mode you can safely specify without worrying about the compatibility.</p>
280 *
281 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800282 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700283 public static final int BACK_DISPOSITION_DEFAULT = 0;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800284
285 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700286 * Deprecated flag.
287 *
288 * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
289 *
290 * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
291 * handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
292 * {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
293 * of this mode had not been well defined. Most likely the end result would be the
294 * same as {@link #BACK_DISPOSITION_DEFAULT}. Either way it is not recommended to
295 * use this mode
296 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800297 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700298 @Deprecated
299 public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800300
301 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700302 * Deprecated flag.
303 *
304 * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
305 *
306 * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
307 * handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
308 * {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
309 * of this mode had not been well defined. In AOSP implementation running on devices
310 * that have navigation bar, specifying this flag could change the software back
311 * button to "Dismiss" icon no matter whether the software keyboard is shown or not,
312 * but there would be no easy way to restore the icon state even after IME lost the
313 * connection to the application. To avoid user confusions, do not specify this mode
314 * anyway
315 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800316 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700317 @Deprecated
318 public static final int BACK_DISPOSITION_WILL_DISMISS = 2;
319
320 /**
321 * Asks the system to not adjust the back button affordance even when the software keyboard is
322 * shown.
323 *
324 * <p>This mode is useful for UI modes where IME's main soft input window is used for some
325 * supplemental UI, such as floating candidate window for languages such as Chinese and
326 * Japanese, where users expect the back button is, or at least looks to be, handled by the
327 * target application rather than the UI shown by the IME even while {@link #isInputViewShown()}
328 * returns {@code true}.</p>
329 *
330 * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
331 * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
332 * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
333 * not take this mode into account.</p>
334 *
335 * @see #setBackDisposition(int)
336 */
337 public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
338
339 /**
340 * Enum flag to be used for {@link #setBackDisposition(int)}.
341 *
342 * @hide
343 */
344 @Retention(SOURCE)
345 @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
346 BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
347 prefix = "BACK_DISPOSITION_")
348 public @interface BackDispositionMode {}
Joe Onorato857fd9b2011-01-27 15:08:35 -0800349
350 /**
351 * @hide
352 * The IME is active. It may or may not be visible.
353 */
354 public static final int IME_ACTIVE = 0x1;
355
356 /**
357 * @hide
358 * The IME is visible.
359 */
360 public static final int IME_VISIBLE = 0x2;
361
Tarandeep Singheadb1392018-11-09 18:15:57 +0100362 /**
363 * @hide
364 * The IME is active and ready with views but set invisible.
365 * This flag cannot be combined with {@link #IME_VISIBLE}.
366 */
367 public static final int IME_INVISIBLE = 0x4;
368
Tarandeep Singh3fecef12018-01-22 14:33:33 -0800369 // Min and max values for back disposition.
370 private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700371 private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
Tarandeep Singh3fecef12018-01-22 14:33:33 -0800372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 InputMethodManager mImm;
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700374 private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
Yohei Yukawac54c1172018-09-06 11:39:50 -0700375
Mathew Inwood31755f92018-12-20 13:53:36 +0000376 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Dianne Hackbornd922ae02011-01-14 11:43:24 -0800377 int mTheme = 0;
The Android Open Source Project10592532009-03-18 17:39:46 -0700378
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 LayoutInflater mInflater;
The Android Open Source Project10592532009-03-18 17:39:46 -0700380 TypedArray mThemeAttrs;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100381 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 View mRootView;
383 SoftInputWindow mWindow;
384 boolean mInitialized;
Tarandeep Singheadb1392018-11-09 18:15:57 +0100385 boolean mViewsCreated;
386 // IME views visibility.
387 boolean mDecorViewVisible;
388 boolean mDecorViewWasVisible;
The Android Open Source Project10592532009-03-18 17:39:46 -0700389 boolean mInShowWindow;
Tarandeep Singheadb1392018-11-09 18:15:57 +0100390 // True if pre-rendering of IME views/window is supported.
391 boolean mCanPreRender;
392 // If IME is pre-rendered.
393 boolean mIsPreRendered;
394 // IME window visibility.
395 // Use (mDecorViewVisible && mWindowVisible) to check if IME is visible to the user.
396 boolean mWindowVisible;
397
The Android Open Source Project10592532009-03-18 17:39:46 -0700398 ViewGroup mFullscreenArea;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 FrameLayout mExtractFrame;
400 FrameLayout mCandidatesFrame;
401 FrameLayout mInputFrame;
402
403 IBinder mToken;
404
405 InputBinding mInputBinding;
406 InputConnection mInputConnection;
407 boolean mInputStarted;
408 boolean mInputViewStarted;
409 boolean mCandidatesViewStarted;
410 InputConnection mStartedInputConnection;
411 EditorInfo mInputEditorInfo;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 int mShowInputFlags;
414 boolean mShowInputRequested;
415 boolean mLastShowInputRequested;
416 int mCandidatesVisibility;
417 CompletionInfo[] mCurCompletions;
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 boolean mFullscreenApplied;
420 boolean mIsFullscreen;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100421 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 View mExtractView;
The Android Open Source Project10592532009-03-18 17:39:46 -0700423 boolean mExtractViewHidden;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100424 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 ExtractEditText mExtractEditText;
426 ViewGroup mExtractAccessories;
Mark Renouf91eb2652016-04-11 16:03:26 -0400427 View mExtractAction;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 ExtractedText mExtractedText;
429 int mExtractedToken;
430
431 View mInputView;
432 boolean mIsInputViewShown;
433
434 int mStatusIcon;
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700435
436 @BackDispositionMode
Joe Onorato857fd9b2011-01-27 15:08:35 -0800437 int mBackDisposition;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438
Yohei Yukawac07fd4c2018-09-11 11:37:13 -0700439 private Object mLock = new Object();
440 @GuardedBy("mLock")
441 private boolean mNotifyUserActionSent;
442
Mathew Inwood31755f92018-12-20 13:53:36 +0000443 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 final Insets mTmpInsets = new Insets();
445 final int[] mTmpLocation = new int[2];
satokab751aa2010-09-14 19:17:36 +0900446
Feng Cao97ec1c42020-03-25 12:20:42 -0700447 private InlineSuggestionSessionController mInlineSuggestionSessionController;
Adam Hebc67f2e2019-11-13 14:34:56 -0800448
JianYang Liu7eec3162020-01-23 17:09:26 -0800449 private boolean mAutomotiveHideNavBarForKeyboard;
450 private boolean mIsAutomotive;
451
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800452 /**
453 * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
454 * The original app window token is passed from client app window.
455 * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy
456 * token to identify this window.
457 * This dummy token is only valid for a single call to {@link InputMethodImpl#showSoftInput},
458 * after which it is set null until next call.
459 */
460 private IBinder mCurShowInputToken;
461
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800462 /**
463 * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#hideSoftInput}
464 * The original app window token is passed from client app window.
465 * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy
466 * token to identify this window.
467 * This dummy token is only valid for a single call to {@link InputMethodImpl#hideSoftInput},
468 * after which it is set {@code null} until next call.
469 */
470 private IBinder mCurHideInputToken;
471
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800472 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
Vinit Nayak31595bc2019-09-30 15:04:19 -0700473 onComputeInsets(mTmpInsets);
Tiger Huang618dbe022020-06-19 00:12:55 +0800474 if (!mViewsCreated) {
475 // The IME views are not ready, keep visible insets untouched.
476 mTmpInsets.visibleTopInsets = 0;
477 }
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800478 if (isExtractViewShown()) {
479 // In true fullscreen mode, we just say the window isn't covering
480 // any content so we don't impact whatever is behind.
481 View decor = getWindow().getWindow().getDecorView();
482 info.contentInsets.top = info.visibleInsets.top = decor.getHeight();
483 info.touchableRegion.setEmpty();
484 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
485 } else {
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800486 info.contentInsets.top = mTmpInsets.contentTopInsets;
487 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
488 info.touchableRegion.set(mTmpInsets.touchableRegion);
489 info.setTouchableInsets(mTmpInsets.touchableInsets);
490 }
Vinit Nayak31595bc2019-09-30 15:04:19 -0700491
492 if (mInputFrame != null) {
493 setImeExclusionRect(mTmpInsets.visibleTopInsets);
494 }
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800495 };
496
497 final View.OnClickListener mActionClickListener = v -> {
498 final EditorInfo ei = getCurrentInputEditorInfo();
499 final InputConnection ic = getCurrentInputConnection();
500 if (ei != null && ic != null) {
501 if (ei.actionId != 0) {
502 ic.performEditorAction(ei.actionId);
503 } else if ((ei.imeOptions & EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE) {
504 ic.performEditorAction(ei.imeOptions & EditorInfo.IME_MASK_ACTION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 }
506 }
507 };
508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 /**
510 * Concrete implementation of
511 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
512 * all of the standard behavior for an input method.
513 */
514 public class InputMethodImpl extends AbstractInputMethodImpl {
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800515
516 private boolean mSystemCallingShowSoftInput;
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800517 private boolean mSystemCallingHideSoftInput;
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800518
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700520 * {@inheritDoc}
Yohei Yukawac54c1172018-09-06 11:39:50 -0700521 * @hide
522 */
523 @MainThread
524 @Override
lumarke0af3942019-05-29 17:11:57 +0800525 public final void initializeInternal(@NonNull IBinder token, int displayId,
Yohei Yukawac54c1172018-09-06 11:39:50 -0700526 IInputMethodPrivilegedOperations privilegedOperations) {
lumarke0af3942019-05-29 17:11:57 +0800527 if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
528 Log.w(TAG, "The token has already registered, ignore this initialization.");
529 return;
530 }
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700531 mPrivOps.set(privilegedOperations);
Yohei Yukawad746a7e2018-09-18 18:55:02 -0700532 InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
lumark90120a82018-08-15 00:33:03 +0800533 updateInputMethodDisplay(displayId);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700534 attachToken(token);
535 }
536
537 /**
538 * {@inheritDoc}
Adam Hebc67f2e2019-11-13 14:34:56 -0800539 * @hide
540 */
541 @MainThread
542 @Override
Feng Cao36960ee2020-02-18 18:23:30 -0800543 public void onCreateInlineSuggestionsRequest(
544 @NonNull InlineSuggestionsRequestInfo requestInfo,
545 @NonNull IInlineSuggestionsRequestCallback cb) {
Joanne Chungd9a916f2020-01-02 19:03:16 +0800546 if (DEBUG) {
547 Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
548 }
Feng Cao97ec1c42020-03-25 12:20:42 -0700549 mInlineSuggestionSessionController.onMakeInlineSuggestionsRequest(requestInfo, cb);
Adam Hebc67f2e2019-11-13 14:34:56 -0800550 }
551
552 /**
553 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700555 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700556 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 public void attachToken(IBinder token) {
Yohei Yukawa674cc4b2018-09-06 10:47:15 -0700558 if (mToken != null) {
559 throw new IllegalStateException(
560 "attachToken() must be called at most once. token=" + token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 }
Yohei Yukawa674cc4b2018-09-06 10:47:15 -0700562 mToken = token;
563 mWindow.setToken(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 }
Tarandeep Singhd8d03a82017-11-28 13:35:32 -0800565
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700567 * {@inheritDoc}
lumark90120a82018-08-15 00:33:03 +0800568 * @hide
569 */
570 @MainThread
571 @Override
572 public void updateInputMethodDisplay(int displayId) {
573 // Update display for adding IME window to the right display.
Andrii Kuliane57f2dc2020-01-26 20:59:07 -0800574 // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
575 // for update resources & configuration correctly when show soft input
576 // in non-default display.
577 updateDisplay(displayId);
lumark90120a82018-08-15 00:33:03 +0800578 }
579
580 /**
581 * {@inheritDoc}
Yohei Yukawa16f04072017-10-18 20:19:43 -0700582 *
583 * <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700585 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700586 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 public void bindInput(InputBinding binding) {
588 mInputBinding = binding;
589 mInputConnection = binding.getConnection();
590 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
591 + " ic=" + mInputConnection);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700592 reportFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 initialize();
594 onBindInput();
595 }
596
597 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700598 * {@inheritDoc}
599 *
600 * <p>Calls {@link InputMethodService#onUnbindInput()} when done.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700602 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700603 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 public void unbindInput() {
605 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
606 + " ic=" + mInputConnection);
Tarandeep Singh0fe4f782020-03-05 13:37:53 -0800607 // Unbind input is per process per display.
608 // TODO(b/150902448): free-up IME surface when target is changing.
609 // e.g. DisplayContent#setInputMethodTarget()
610 removeImeSurface();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 onUnbindInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 mInputBinding = null;
613 mInputConnection = null;
614 }
615
Yohei Yukawa16f04072017-10-18 20:19:43 -0700616 /**
617 * {@inheritDoc}
618 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700619 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700620 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 public void startInput(InputConnection ic, EditorInfo attribute) {
622 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
623 doStartInput(ic, attribute, false);
624 }
625
Yohei Yukawa16f04072017-10-18 20:19:43 -0700626 /**
627 * {@inheritDoc}
628 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700629 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700630 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 public void restartInput(InputConnection ic, EditorInfo attribute) {
632 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
633 doStartInput(ic, attribute, true);
634 }
635
636 /**
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800637 * {@inheritDoc}
638 * @hide
639 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700640 @MainThread
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800641 @Override
Tarandeep Singheadb1392018-11-09 18:15:57 +0100642 public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800643 @NonNull EditorInfo editorInfo, boolean restarting,
Tarandeep Singheadb1392018-11-09 18:15:57 +0100644 @NonNull IBinder startInputToken, boolean shouldPreRenderIme) {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700645 mPrivOps.reportStartInput(startInputToken);
Tarandeep Singheadb1392018-11-09 18:15:57 +0100646 mCanPreRender = shouldPreRenderIme;
647 if (DEBUG) Log.v(TAG, "Will Pre-render IME: " + mCanPreRender);
648
649 if (restarting) {
650 restartInput(inputConnection, editorInfo);
651 } else {
652 startInput(inputConnection, editorInfo);
653 }
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800654 }
655
656 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700657 * {@inheritDoc}
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800658 * @hide
659 */
660 @MainThread
661 @Override
662 public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
663 IBinder hideInputToken) {
664 mSystemCallingHideSoftInput = true;
665 mCurHideInputToken = hideInputToken;
666 hideSoftInput(flags, resultReceiver);
667 mCurHideInputToken = null;
668 mSystemCallingHideSoftInput = false;
669 }
670
671 /**
672 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700674 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700675 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -0800676 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 if (DEBUG) Log.v(TAG, "hideSoftInput()");
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800678 if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
679 && !mSystemCallingHideSoftInput) {
680 Log.e(TAG, "IME shouldn't call hideSoftInput on itself."
681 + " Use requestHideSelf(int) itself");
682 return;
683 }
Tarandeep Singheadb1392018-11-09 18:15:57 +0100684 final boolean wasVisible = mIsPreRendered
685 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700686 applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */);
Tarandeep Singheadb1392018-11-09 18:15:57 +0100687 if (mIsPreRendered) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100688 if (DEBUG) {
689 Log.v(TAG, "Making IME window invisible");
690 }
691 setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
692 onPreRenderedWindowVisibilityChanged(false /* setVisible */);
693 } else {
694 mShowInputFlags = 0;
695 mShowInputRequested = false;
696 doHideWindow();
697 }
698 final boolean isVisible = mIsPreRendered
699 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
700 final boolean visibilityChanged = isVisible != wasVisible;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800701 if (resultReceiver != null) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100702 resultReceiver.send(visibilityChanged
The Android Open Source Project4df24232009-03-05 14:34:35 -0800703 ? InputMethodManager.RESULT_HIDDEN
Tarandeep Singheadb1392018-11-09 18:15:57 +0100704 : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
The Android Open Source Project4df24232009-03-05 14:34:35 -0800705 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
706 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 }
708
709 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700710 * {@inheritDoc}
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800711 * @hide
712 */
713 @MainThread
714 @Override
715 public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
716 IBinder showInputToken) {
717 mSystemCallingShowSoftInput = true;
718 mCurShowInputToken = showInputToken;
719 showSoftInput(flags, resultReceiver);
720 mCurShowInputToken = null;
721 mSystemCallingShowSoftInput = false;
722 }
723
724 /**
725 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700727 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700728 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -0800729 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700730 if (DEBUG) Log.v(TAG, "showSoftInput()");
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800731 // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
732 if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
733 && !mSystemCallingShowSoftInput) {
734 Log.e(TAG," IME shouldn't call showSoftInput on itself."
735 + " Use requestShowSelf(int) itself");
736 return;
737 }
Tarandeep Singheadb1392018-11-09 18:15:57 +0100738 final boolean wasVisible = mIsPreRendered
739 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700740 if (dispatchOnShowInputRequested(flags, false)) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100741 if (mIsPreRendered) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100742 if (DEBUG) {
743 Log.v(TAG, "Making IME window visible");
744 }
745 onPreRenderedWindowVisibilityChanged(true /* setVisible */);
746 } else {
747 showWindow(true);
748 }
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700749 applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 }
satok865b9772011-01-21 02:45:06 +0900751 // If user uses hard keyboard, IME button should always be shown.
Tarandeep Singheadb1392018-11-09 18:15:57 +0100752 setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
753 final boolean isVisible = mIsPreRendered
754 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
755 final boolean visibilityChanged = isVisible != wasVisible;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800756 if (resultReceiver != null) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100757 resultReceiver.send(visibilityChanged
The Android Open Source Project4df24232009-03-05 14:34:35 -0800758 ? InputMethodManager.RESULT_SHOWN
Tarandeep Singheadb1392018-11-09 18:15:57 +0100759 : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
The Android Open Source Project4df24232009-03-05 14:34:35 -0800760 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
761 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 }
satokab751aa2010-09-14 19:17:36 +0900763
Yohei Yukawa16f04072017-10-18 20:19:43 -0700764 /**
765 * {@inheritDoc}
766 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700767 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700768 @Override
satokab751aa2010-09-14 19:17:36 +0900769 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
Yohei Yukawac07fd4c2018-09-11 11:37:13 -0700770 dispatchOnCurrentInputMethodSubtypeChanged(subtype);
satokab751aa2010-09-14 19:17:36 +0900771 }
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800772
773 /**
774 * {@inheritDoc}
775 * @hide
776 */
777 @Override
778 public void setCurrentShowInputToken(IBinder showInputToken) {
779 mCurShowInputToken = showInputToken;
780 }
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800781
782 /**
783 * {@inheritDoc}
784 * @hide
785 */
786 @Override
787 public void setCurrentHideInputToken(IBinder hideInputToken) {
788 mCurHideInputToken = hideInputToken;
789 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 }
satokab751aa2010-09-14 19:17:36 +0900791
Adam Hebc67f2e2019-11-13 14:34:56 -0800792 /**
Adam He045c0202020-04-17 15:20:01 -0700793 * Called when Autofill is requesting an {@link InlineSuggestionsRequest} from the IME.
794 *
795 * <p>The Autofill Framework will first request the IME to create and send an
796 * {@link InlineSuggestionsRequest} back. Once Autofill Framework receives a valid request and
797 * also receives valid inline suggestions, they will be returned via
798 * {@link #onInlineSuggestionsResponse(InlineSuggestionsResponse)}.</p>
799 *
800 * <p>IME Lifecycle - The request will wait to be created after inputStarted</p>
801 *
802 * <p>If the IME wants to support displaying inline suggestions, they must set
803 * supportsInlineSuggestions in its XML and implement this method to return a valid
804 * {@link InlineSuggestionsRequest}.</p>
Adam Hebc67f2e2019-11-13 14:34:56 -0800805 *
Feng Cao36960ee2020-02-18 18:23:30 -0800806 * @param uiExtras the extras that contain the UI renderer related information
807 * @return an {@link InlineSuggestionsRequest} to be sent to Autofill.
Adam Hebc67f2e2019-11-13 14:34:56 -0800808 */
Feng Cao36960ee2020-02-18 18:23:30 -0800809 @Nullable
810 public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull Bundle uiExtras) {
Adam Hebc67f2e2019-11-13 14:34:56 -0800811 return null;
812 }
813
814 /**
815 * Called when Autofill responds back with {@link InlineSuggestionsResponse} containing
816 * inline suggestions.
817 *
818 * <p>Should be implemented by subclasses.</p>
819 *
820 * @param response {@link InlineSuggestionsResponse} passed back by Autofill.
821 * @return Whether the IME will use and render the inline suggestions.
822 */
823 public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
824 return false;
825 }
826
Adam He40116252020-02-04 14:55:39 -0800827 /**
828 * Returns the {@link IBinder} input token from the host view root.
829 */
830 @Nullable
831 private IBinder getHostInputToken() {
832 ViewRootImpl viewRoot = null;
833 if (mRootView != null) {
834 viewRoot = mRootView.getViewRootImpl();
835 }
836 return viewRoot == null ? null : viewRoot.getInputToken();
837 }
838
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800839 private void notifyImeHidden() {
Taran Singhaef529a2020-03-23 16:57:22 -0700840 requestHideSelf(0);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800841 }
842
Tarandeep Singh94c9a832020-02-03 14:55:30 -0800843 private void removeImeSurface() {
844 if (!mShowInputRequested && !mWindowVisible) {
845 // hiding a window removes its surface.
846 mWindow.hide();
847 }
848 }
849
Yohei Yukawac54c1172018-09-06 11:39:50 -0700850 private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700851 mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700852 }
853
Vinit Nayak31595bc2019-09-30 15:04:19 -0700854 /** Set region of the keyboard to be avoided from back gesture */
855 private void setImeExclusionRect(int visibleTopInsets) {
856 View inputFrameRootView = mInputFrame.getRootView();
857 Rect r = new Rect(0, visibleTopInsets, inputFrameRootView.getWidth(),
858 inputFrameRootView.getHeight());
859 inputFrameRootView.setSystemGestureExclusionRects(Collections.singletonList(r));
860 }
861
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 /**
863 * Concrete implementation of
864 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
865 * all of the standard behavior for an input method session.
866 */
867 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
868 public void finishInput() {
869 if (!isEnabled()) {
870 return;
871 }
872 if (DEBUG) Log.v(TAG, "finishInput() in " + this);
873 doFinishInput();
874 }
875
876 /**
877 * Call {@link InputMethodService#onDisplayCompletions
878 * InputMethodService.onDisplayCompletions()}.
879 */
880 public void displayCompletions(CompletionInfo[] completions) {
881 if (!isEnabled()) {
882 return;
883 }
884 mCurCompletions = completions;
885 onDisplayCompletions(completions);
886 }
887
888 /**
889 * Call {@link InputMethodService#onUpdateExtractedText
890 * InputMethodService.onUpdateExtractedText()}.
891 */
892 public void updateExtractedText(int token, ExtractedText text) {
893 if (!isEnabled()) {
894 return;
895 }
896 onUpdateExtractedText(token, text);
897 }
898
899 /**
900 * Call {@link InputMethodService#onUpdateSelection
901 * InputMethodService.onUpdateSelection()}.
902 */
903 public void updateSelection(int oldSelStart, int oldSelEnd,
904 int newSelStart, int newSelEnd,
905 int candidatesStart, int candidatesEnd) {
906 if (!isEnabled()) {
907 return;
908 }
909 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
910 newSelStart, newSelEnd, candidatesStart, candidatesEnd);
911 }
satok863fcd62011-06-21 17:38:02 +0900912
913 @Override
914 public void viewClicked(boolean focusChanged) {
915 if (!isEnabled()) {
916 return;
917 }
918 InputMethodService.this.onViewClicked(focusChanged);
919 }
920
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 /**
922 * Call {@link InputMethodService#onUpdateCursor
923 * InputMethodService.onUpdateCursor()}.
924 */
925 public void updateCursor(Rect newCursor) {
926 if (!isEnabled()) {
927 return;
928 }
929 InputMethodService.this.onUpdateCursor(newCursor);
930 }
931
932 /**
933 * Call {@link InputMethodService#onAppPrivateCommand
934 * InputMethodService.onAppPrivateCommand()}.
935 */
936 public void appPrivateCommand(String action, Bundle data) {
937 if (!isEnabled()) {
938 return;
939 }
940 InputMethodService.this.onAppPrivateCommand(action, data);
941 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800942
943 /**
944 *
945 */
946 public void toggleSoftInput(int showFlags, int hideFlags) {
947 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
948 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900949
950 /**
951 * Call {@link InputMethodService#onUpdateCursorAnchorInfo
952 * InputMethodService.onUpdateCursorAnchorInfo()}.
953 */
954 public void updateCursorAnchorInfo(CursorAnchorInfo info) {
955 if (!isEnabled()) {
956 return;
957 }
958 InputMethodService.this.onUpdateCursorAnchorInfo(info);
959 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800960
961 /**
962 * Notify IME that window is hidden.
963 * @hide
964 */
965 public final void notifyImeHidden() {
966 InputMethodService.this.notifyImeHidden();
967 }
Tarandeep Singh94c9a832020-02-03 14:55:30 -0800968
969 /**
970 * Notify IME that surface can be now removed.
971 * @hide
972 */
973 public final void removeImeSurface() {
974 InputMethodService.this.removeImeSurface();
975 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 }
977
978 /**
979 * Information about where interesting parts of the input method UI appear.
980 */
981 public static final class Insets {
982 /**
983 * This is the top part of the UI that is the main content. It is
984 * used to determine the basic space needed, to resize/pan the
985 * application behind. It is assumed that this inset does not
986 * change very much, since any change will cause a full resize/pan
987 * of the application behind. This value is relative to the top edge
988 * of the input method window.
989 */
990 public int contentTopInsets;
991
992 /**
993 * This is the top part of the UI that is visibly covering the
994 * application behind it. This provides finer-grained control over
995 * visibility, allowing you to change it relatively frequently (such
996 * as hiding or showing candidates) without disrupting the underlying
997 * UI too much. For example, this will never resize the application
998 * UI, will only pan if needed to make the current focus visible, and
999 * will not aggressively move the pan position when this changes unless
1000 * needed to make the focus visible. This value is relative to the top edge
1001 * of the input method window.
1002 */
1003 public int visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -08001004
1005 /**
1006 * This is the region of the UI that is touchable. It is used when
1007 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
1008 * The region should be specified relative to the origin of the window frame.
1009 */
1010 public final Region touchableRegion = new Region();
1011
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 /**
1013 * Option for {@link #touchableInsets}: the entire window frame
1014 * can be touched.
1015 */
1016 public static final int TOUCHABLE_INSETS_FRAME
1017 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
1018
1019 /**
1020 * Option for {@link #touchableInsets}: the area inside of
1021 * the content insets can be touched.
1022 */
1023 public static final int TOUCHABLE_INSETS_CONTENT
1024 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
1025
1026 /**
1027 * Option for {@link #touchableInsets}: the area inside of
1028 * the visible insets can be touched.
1029 */
1030 public static final int TOUCHABLE_INSETS_VISIBLE
1031 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001032
1033 /**
1034 * Option for {@link #touchableInsets}: the region specified by
1035 * {@link #touchableRegion} can be touched.
1036 */
1037 public static final int TOUCHABLE_INSETS_REGION
1038 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
1039
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 /**
1041 * Determine which area of the window is touchable by the user. May
1042 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
Jeff Brownfbf09772011-01-16 14:06:57 -08001043 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
1044 * or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 */
1046 public int touchableInsets;
1047 }
satok865b9772011-01-21 02:45:06 +09001048
The Android Open Source Project10592532009-03-18 17:39:46 -07001049 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001050 * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
1051 *
1052 * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
1053 * Basically this functionality still needs to be considered as implementation details.</p>
1054 */
1055 @MainThread
1056 private static final class SettingsObserver extends ContentObserver {
1057 @Retention(RetentionPolicy.SOURCE)
1058 @IntDef({
1059 ShowImeWithHardKeyboardType.UNKNOWN,
1060 ShowImeWithHardKeyboardType.FALSE,
1061 ShowImeWithHardKeyboardType.TRUE,
1062 })
1063 private @interface ShowImeWithHardKeyboardType {
1064 int UNKNOWN = 0;
1065 int FALSE = 1;
1066 int TRUE = 2;
1067 }
1068 @ShowImeWithHardKeyboardType
1069 private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
1070
1071 private final InputMethodService mService;
1072
1073 private SettingsObserver(InputMethodService service) {
1074 super(new Handler(service.getMainLooper()));
1075 mService = service;
1076 }
1077
1078 /**
1079 * A factory method that internally enforces two-phase initialization to make sure that the
1080 * object reference will not be escaped until the object is properly constructed.
1081 *
1082 * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread. Hence
1083 * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
1084 *
1085 * @param service {@link InputMethodService} that needs to receive the callback.
1086 * @return {@link SettingsObserver} that is already registered to
1087 * {@link android.content.ContentResolver}. The caller must call
1088 * {@link SettingsObserver#unregister()}.
1089 */
1090 public static SettingsObserver createAndRegister(InputMethodService service) {
1091 final SettingsObserver observer = new SettingsObserver(service);
1092 // The observer is properly constructed. Let's start accepting the event.
1093 service.getContentResolver().registerContentObserver(
1094 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
1095 false, observer);
1096 return observer;
1097 }
1098
1099 void unregister() {
1100 mService.getContentResolver().unregisterContentObserver(this);
1101 }
1102
Mathew Inwood1dd7d112018-07-31 14:53:29 +01001103 @UnsupportedAppUsage
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001104 private boolean shouldShowImeWithHardKeyboard() {
1105 // Lazily initialize as needed.
1106 if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
1107 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
1108 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
1109 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
1110 }
1111 switch (mShowImeWithHardKeyboard) {
1112 case ShowImeWithHardKeyboardType.TRUE:
1113 return true;
1114 case ShowImeWithHardKeyboardType.FALSE:
1115 return false;
1116 default:
1117 Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
1118 return false;
1119 }
1120 }
1121
1122 @Override
1123 public void onChange(boolean selfChange, Uri uri) {
1124 final Uri showImeWithHardKeyboardUri =
1125 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
1126 if (showImeWithHardKeyboardUri.equals(uri)) {
1127 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
1128 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
1129 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001130 // In Android M and prior, state change of
1131 // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered
1132 // #onConfigurationChanged(). For compatibility reasons, we reset the internal
1133 // state as if configuration was changed.
1134 mService.resetStateForNewConfiguration();
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001135 }
1136 }
1137
1138 @Override
1139 public String toString() {
1140 return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}";
1141 }
1142 }
Mathew Inwood1dd7d112018-07-31 14:53:29 +01001143 @UnsupportedAppUsage
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001144 private SettingsObserver mSettingsObserver;
1145
1146 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001147 * You can call this to customize the theme used by your IME's window.
1148 * This theme should typically be one that derives from
1149 * {@link android.R.style#Theme_InputMethod}, which is the default theme
1150 * you will get. This must be set before {@link #onCreate}, so you
1151 * will typically call it in your constructor with the resource ID
1152 * of your custom theme.
1153 */
satokab751aa2010-09-14 19:17:36 +09001154 @Override
The Android Open Source Project10592532009-03-18 17:39:46 -07001155 public void setTheme(int theme) {
1156 if (mWindow != null) {
1157 throw new IllegalStateException("Must be called before onCreate()");
1158 }
1159 mTheme = theme;
1160 }
satok865b9772011-01-21 02:45:06 +09001161
Dianne Hackborn836531b2012-08-01 19:00:38 -07001162 /**
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001163 * You can call this to try to enable accelerated drawing for your IME. This must be set before
1164 * {@link #onCreate()}, so you will typically call it in your constructor. It is not always
1165 * possible to use hardware accelerated drawing in an IME (for example on low-end devices that
1166 * do not have the resources to support this), so the call {@code true} if it succeeds otherwise
1167 * {@code false} if you will need to draw in software. You must be able to handle either case.
Alan Viverettee07b5952014-08-13 19:13:54 -07001168 *
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001169 * <p>In API 21 and later, system may automatically enable hardware accelerated drawing for your
1170 * IME on capable devices even if this method is not explicitly called. Make sure that your IME
1171 * is able to handle either case.</p>
1172 *
1173 * @return {@code true} if accelerated drawing is successfully enabled otherwise {@code false}.
1174 * On API 21 and later devices the return value is basically just a hint and your IME
1175 * does not need to change the behavior based on the it
1176 * @deprecated Starting in API 21, hardware acceleration is always enabled on capable devices
Dianne Hackborn836531b2012-08-01 19:00:38 -07001177 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001178 @Deprecated
Dianne Hackborn836531b2012-08-01 19:00:38 -07001179 public boolean enableHardwareAcceleration() {
1180 if (mWindow != null) {
1181 throw new IllegalStateException("Must be called before onCreate()");
1182 }
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001183 return ActivityManager.isHighEndGfx();
Dianne Hackborn836531b2012-08-01 19:00:38 -07001184 }
1185
Alan Viverette5effd7e2014-05-05 12:25:33 -07001186 @Override public void onCreate() {
1187 mTheme = Resources.selectSystemTheme(mTheme,
1188 getApplicationInfo().targetSdkVersion,
1189 android.R.style.Theme_InputMethod,
1190 android.R.style.Theme_Holo_InputMethod,
1191 android.R.style.Theme_DeviceDefault_InputMethod,
1192 android.R.style.Theme_DeviceDefault_InputMethod);
The Android Open Source Project10592532009-03-18 17:39:46 -07001193 super.setTheme(mTheme);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 super.onCreate();
1195 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001196 mSettingsObserver = SettingsObserver.createAndRegister(this);
JianYang Liu7eec3162020-01-23 17:09:26 -08001197
1198 mIsAutomotive = isAutomotive();
1199 mAutomotiveHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
1200 com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
1201
lumark90120a82018-08-15 00:33:03 +08001202 // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
1203 // for update resources & configuration correctly when show soft input
1204 // in non-default display.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001205 mInflater = (LayoutInflater)getSystemService(
1206 Context.LAYOUT_INFLATER_SERVICE);
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001207 mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
Dianne Hackborne30e02f2014-05-27 18:24:45 -07001208 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
Tiger Huangc8364e32020-03-10 21:20:58 +08001209 mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
1210 mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
Tiger Huang227c6652020-03-19 22:28:43 +08001211 mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
Jorim Jaggi0da8fd12020-01-10 01:23:21 +01001212
JianYang Liu7eec3162020-01-23 17:09:26 -08001213 // IME layout should always be inset by navigation bar, no matter its current visibility,
1214 // unless automotive requests it, since automotive may hide the navigation bar.
Tiger Huang4a7835f2019-11-06 00:07:56 +08001215 mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
1216 (v, insets) -> v.onApplyWindowInsets(
Jorim Jaggi0da8fd12020-01-10 01:23:21 +01001217 new WindowInsets.Builder(insets).setInsets(
Tiger Huang52724442020-01-20 21:38:42 +08001218 navigationBars(),
JianYang Liu7eec3162020-01-23 17:09:26 -08001219 mIsAutomotive && mAutomotiveHideNavBarForKeyboard
1220 ? android.graphics.Insets.NONE
1221 : insets.getInsetsIgnoringVisibility(navigationBars())
1222 )
Jorim Jaggi0da8fd12020-01-10 01:23:21 +01001223 .build()));
1224
Yohei Yukawa8f162c62018-01-10 13:18:09 -08001225 // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
1226 // by default (but IME developers can opt this out later if they want a new behavior).
1227 mWindow.getWindow().setFlags(
1228 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
1229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 initViews();
Romain Guy980a9382010-01-08 15:06:28 -08001231 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
Feng Cao97ec1c42020-03-25 12:20:42 -07001232
1233 mInlineSuggestionSessionController = new InlineSuggestionSessionController(
1234 this::onCreateInlineSuggestionsRequest, this::getHostInputToken,
1235 this::onInlineSuggestionsResponse);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 }
satokab751aa2010-09-14 19:17:36 +09001237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 /**
1239 * This is a hook that subclasses can use to perform initialization of
1240 * their interface. It is called for you prior to any of your UI objects
1241 * being created, both after the service is first created and after a
1242 * configuration change happens.
1243 */
1244 public void onInitializeInterface() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001245 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 }
satokab751aa2010-09-14 19:17:36 +09001247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 void initialize() {
1249 if (!mInitialized) {
1250 mInitialized = true;
1251 onInitializeInterface();
1252 }
1253 }
satokab751aa2010-09-14 19:17:36 +09001254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 void initViews() {
1256 mInitialized = false;
Tarandeep Singheadb1392018-11-09 18:15:57 +01001257 mViewsCreated = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258 mShowInputRequested = false;
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001259 mShowInputFlags = 0;
1260
The Android Open Source Project10592532009-03-18 17:39:46 -07001261 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 mRootView = mInflater.inflate(
1263 com.android.internal.R.layout.input_method, null);
1264 mWindow.setContentView(mRootView);
Seonggoo Kang72745ff2014-12-24 13:55:50 +09001265 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
Jeff Sharkey6e2bee72012-10-01 13:39:08 -07001267 if (Settings.Global.getInt(getContentResolver(),
1268 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 mWindow.getWindow().setWindowAnimations(
1270 com.android.internal.R.style.Animation_InputMethodFancy);
1271 }
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001272 mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
The Android Open Source Project10592532009-03-18 17:39:46 -07001273 mExtractViewHidden = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001274 mExtractFrame = mRootView.findViewById(android.R.id.extractArea);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 mExtractView = null;
1276 mExtractEditText = null;
1277 mExtractAccessories = null;
1278 mExtractAction = null;
1279 mFullscreenApplied = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001280
1281 mCandidatesFrame = mRootView.findViewById(android.R.id.candidatesArea);
1282 mInputFrame = mRootView.findViewById(android.R.id.inputArea);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 mInputView = null;
1284 mIsInputViewShown = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001285
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 mExtractFrame.setVisibility(View.GONE);
1287 mCandidatesVisibility = getCandidatesHiddenVisibility();
1288 mCandidatesFrame.setVisibility(mCandidatesVisibility);
1289 mInputFrame.setVisibility(View.GONE);
1290 }
satokab751aa2010-09-14 19:17:36 +09001291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 @Override public void onDestroy() {
1293 super.onDestroy();
1294 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
1295 mInsetsComputer);
Satoshi Kataokac56191f2013-05-30 13:14:47 +09001296 doFinishInput();
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07001297 mWindow.dismissForDestroyIfNecessary();
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001298 if (mSettingsObserver != null) {
1299 mSettingsObserver.unregister();
1300 mSettingsObserver = null;
1301 }
Yohei Yukawaeec552e2018-09-09 20:48:41 -07001302 if (mToken != null) {
1303 // This is completely optional, but allows us to show more explicit error messages
1304 // when IME developers are doing something unsupported.
Yohei Yukawad746a7e2018-09-18 18:55:02 -07001305 InputMethodPrivilegedOperationsRegistry.remove(mToken);
Yohei Yukawaeec552e2018-09-09 20:48:41 -07001306 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 }
satokf17db9f2011-09-14 18:55:58 +09001308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 /**
1310 * Take care of handling configuration changes. Subclasses of
1311 * InputMethodService generally don't need to deal directly with
1312 * this on their own; the standard implementation here takes care of
1313 * regenerating the input method UI as a result of the configuration
1314 * change, so you can rely on your {@link #onCreateInputView} and
1315 * other methods being called as appropriate due to a configuration change.
1316 *
1317 * <p>When a configuration change does happen,
1318 * {@link #onInitializeInterface()} is guaranteed to be called the next
1319 * time prior to any of the other input or UI creation callbacks. The
1320 * following will be called immediately depending if appropriate for current
1321 * state: {@link #onStartInput} if input is active, and
1322 * {@link #onCreateInputView} and {@link #onStartInputView} and related
1323 * appropriate functions if the UI is displayed.
1324 */
1325 @Override public void onConfigurationChanged(Configuration newConfig) {
1326 super.onConfigurationChanged(newConfig);
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001327 resetStateForNewConfiguration();
1328 }
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001329
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001330 private void resetStateForNewConfiguration() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01001331 boolean visible = mDecorViewVisible;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 int showFlags = mShowInputFlags;
1333 boolean showingInput = mShowInputRequested;
1334 CompletionInfo[] completions = mCurCompletions;
1335 initViews();
1336 mInputViewStarted = false;
1337 mCandidatesViewStarted = false;
1338 if (mInputStarted) {
1339 doStartInput(getCurrentInputConnection(),
1340 getCurrentInputEditorInfo(), true);
1341 }
1342 if (visible) {
1343 if (showingInput) {
1344 // If we were last showing the soft keyboard, try to do so again.
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001345 if (dispatchOnShowInputRequested(showFlags, true)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 showWindow(true);
1347 if (completions != null) {
1348 mCurCompletions = completions;
1349 onDisplayCompletions(completions);
1350 }
1351 } else {
satok2f913d92012-05-10 01:48:03 +09001352 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001353 }
1354 } else if (mCandidatesVisibility == View.VISIBLE) {
1355 // If the candidates are currently visible, make sure the
1356 // window is shown for them.
1357 showWindow(false);
1358 } else {
1359 // Otherwise hide the window.
satok2f913d92012-05-10 01:48:03 +09001360 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 }
satok865b9772011-01-21 02:45:06 +09001362 // If user uses hard keyboard, IME button should always be shown.
Joe Onorato857fd9b2011-01-27 15:08:35 -08001363 boolean showing = onEvaluateInputViewShown();
Yohei Yukawac54c1172018-09-06 11:39:50 -07001364 setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 }
1366 }
1367
1368 /**
1369 * Implement to return our standard {@link InputMethodImpl}. Subclasses
1370 * can override to provide their own customized version.
1371 */
satokab751aa2010-09-14 19:17:36 +09001372 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 public AbstractInputMethodImpl onCreateInputMethodInterface() {
1374 return new InputMethodImpl();
1375 }
1376
1377 /**
1378 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
1379 * can override to provide their own customized version.
1380 */
satokab751aa2010-09-14 19:17:36 +09001381 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
1383 return new InputMethodSessionImpl();
1384 }
1385
1386 public LayoutInflater getLayoutInflater() {
1387 return mInflater;
1388 }
1389
1390 public Dialog getWindow() {
1391 return mWindow;
1392 }
Yohei Yukawa386f50e2018-03-14 13:03:42 -07001393
1394 /**
1395 * Sets the disposition mode that indicates the expected affordance for the back button.
1396 *
1397 * <p>Keep in mind that specifying this flag does not change the the default behavior of
1398 * {@link #onKeyDown(int, KeyEvent)}. It is IME developers' responsibility for making sure that
1399 * their custom implementation of {@link #onKeyDown(int, KeyEvent)} is consistent with the mode
1400 * specified to this API.</p>
1401 *
1402 * @see #getBackDisposition()
1403 * @param disposition disposition mode to be set
1404 */
1405 public void setBackDisposition(@BackDispositionMode int disposition) {
Tarandeep Singh3fecef12018-01-22 14:33:33 -08001406 if (disposition == mBackDisposition) {
1407 return;
1408 }
1409 if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
1410 Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
1411 return;
1412 }
Joe Onorato857fd9b2011-01-27 15:08:35 -08001413 mBackDisposition = disposition;
Tarandeep Singheadb1392018-11-09 18:15:57 +01001414 setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
Joe Onorato857fd9b2011-01-27 15:08:35 -08001415 }
1416
Yohei Yukawa386f50e2018-03-14 13:03:42 -07001417 /**
1418 * Retrieves the current disposition mode that indicates the expected back button affordance.
1419 *
1420 * @see #setBackDisposition(int)
1421 * @return currently selected disposition mode
1422 */
1423 @BackDispositionMode
Joe Onorato857fd9b2011-01-27 15:08:35 -08001424 public int getBackDisposition() {
1425 return mBackDisposition;
1426 }
1427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 /**
1429 * Return the maximum width, in pixels, available the input method.
1430 * Input methods are positioned at the bottom of the screen and, unless
1431 * running in fullscreen, will generally want to be as short as possible
1432 * so should compute their height based on their contents. However, they
1433 * can stretch as much as needed horizontally. The function returns to
1434 * you the maximum amount of space available horizontally, which you can
1435 * use if needed for UI placement.
1436 *
1437 * <p>In many cases this is not needed, you can just rely on the normal
1438 * view layout mechanisms to position your views within the full horizontal
1439 * space given to the input method.
1440 *
1441 * <p>Note that this value can change dynamically, in particular when the
1442 * screen orientation changes.
1443 */
1444 public int getMaxWidth() {
Andrii Kuliane57f2dc2020-01-26 20:59:07 -08001445 final WindowManager windowManager = getSystemService(WindowManager.class);
Charles Chen49f329c2020-02-13 16:41:32 +08001446 return WindowMetricsHelper.getBoundsExcludingNavigationBarAndCutout(
1447 windowManager.getCurrentWindowMetrics()).width();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448 }
1449
1450 /**
1451 * Return the currently active InputBinding for the input method, or
1452 * null if there is none.
1453 */
1454 public InputBinding getCurrentInputBinding() {
1455 return mInputBinding;
1456 }
1457
1458 /**
1459 * Retrieve the currently active InputConnection that is bound to
1460 * the input method, or null if there is none.
1461 */
1462 public InputConnection getCurrentInputConnection() {
1463 InputConnection ic = mStartedInputConnection;
1464 if (ic != null) {
1465 return ic;
1466 }
1467 return mInputConnection;
1468 }
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001469
1470 /**
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001471 * Force switch to the last used input method and subtype. If the last input method didn't have
1472 * any subtypes, the framework will simply switch to the last input method with no subtype
1473 * specified.
1474 * @return true if the current input method and subtype was successfully switched to the last
1475 * used input method and subtype.
1476 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001477 public final boolean switchToPreviousInputMethod() {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001478 return mPrivOps.switchToPreviousInputMethod();
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001479 }
1480
1481 /**
1482 * Force switch to the next input method and subtype. If there is no IME enabled except
1483 * current IME and subtype, do nothing.
1484 * @param onlyCurrentIme if true, the framework will find the next subtype which
1485 * belongs to the current IME
1486 * @return true if the current input method and subtype was successfully switched to the next
1487 * input method and subtype.
1488 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001489 public final boolean switchToNextInputMethod(boolean onlyCurrentIme) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001490 return mPrivOps.switchToNextInputMethod(onlyCurrentIme);
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001491 }
1492
1493 /**
1494 * Returns true if the current IME needs to offer the users ways to switch to a next input
1495 * method (e.g. a globe key.).
1496 * When an IME sets supportsSwitchingToNextInputMethod and this method returns true,
1497 * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly.
1498 * <p> Note that the system determines the most appropriate next input method
1499 * and subtype in order to provide the consistent user experience in switching
1500 * between IMEs and subtypes.
1501 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001502 public final boolean shouldOfferSwitchingToNextInputMethod() {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001503 return mPrivOps.shouldOfferSwitchingToNextInputMethod();
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001504 }
1505
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 public boolean getCurrentInputStarted() {
1507 return mInputStarted;
1508 }
1509
1510 public EditorInfo getCurrentInputEditorInfo() {
1511 return mInputEditorInfo;
1512 }
Yohei Yukawac54c1172018-09-06 11:39:50 -07001513
1514 private void reportFullscreenMode() {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07001515 mPrivOps.reportFullscreenMode(mIsFullscreen);
Yohei Yukawac54c1172018-09-06 11:39:50 -07001516 }
1517
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 /**
1519 * Re-evaluate whether the input method should be running in fullscreen
1520 * mode, and update its UI if this has changed since the last time it
1521 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to
1522 * determine whether it should currently run in fullscreen mode. You
1523 * can use {@link #isFullscreenMode()} to determine if the input method
1524 * is currently running in fullscreen mode.
1525 */
1526 public void updateFullscreenMode() {
1527 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
1528 boolean changed = mLastShowInputRequested != mShowInputRequested;
1529 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
1530 changed = true;
1531 mIsFullscreen = isFullscreen;
Yohei Yukawac54c1172018-09-06 11:39:50 -07001532 reportFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001533 mFullscreenApplied = true;
1534 initialize();
The Android Open Source Project10592532009-03-18 17:39:46 -07001535 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1536 mFullscreenArea.getLayoutParams();
1537 if (isFullscreen) {
1538 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
1539 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
1540 lp.height = 0;
1541 lp.weight = 1;
1542 } else {
1543 mFullscreenArea.setBackgroundDrawable(null);
1544 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
1545 lp.weight = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001546 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001547 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
1548 mFullscreenArea, lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549 if (isFullscreen) {
1550 if (mExtractView == null) {
1551 View v = onCreateExtractTextView();
1552 if (v != null) {
1553 setExtractView(v);
1554 }
1555 }
1556 startExtractingText(false);
1557 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001558 updateExtractFrameVisibility();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001559 }
1560
1561 if (changed) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001562 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 mLastShowInputRequested = mShowInputRequested;
1564 }
1565 }
1566
1567 /**
1568 * Update the given window's parameters for the given mode. This is called
1569 * when the window is first displayed and each time the fullscreen or
1570 * candidates only mode changes.
1571 *
1572 * <p>The default implementation makes the layout for the window
Romain Guy980a9382010-01-08 15:06:28 -08001573 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
1574 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001575 *
1576 * @param win The input method's window.
1577 * @param isFullscreen If true, the window is running in fullscreen mode
1578 * and intended to cover the entire application display.
1579 * @param isCandidatesOnly If true, the window is only showing the
1580 * candidates view and none of the rest of its UI. This is mutually
1581 * exclusive with fullscreen mode.
1582 */
1583 public void onConfigureWindow(Window win, boolean isFullscreen,
1584 boolean isCandidatesOnly) {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001585 final int currentHeight = mWindow.getWindow().getAttributes().height;
1586 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
1587 if (mIsInputViewShown && currentHeight != newHeight) {
Yohei Yukawad0332832017-02-01 13:59:43 -08001588 if (DEBUG) {
1589 Log.w(TAG,"Window size has been changed. This may cause jankiness of resizing "
1590 + "window: " + currentHeight + " -> " + newHeight);
1591 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001592 }
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001593 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 }
1595
1596 /**
1597 * Return whether the input method is <em>currently</em> running in
1598 * fullscreen mode. This is the mode that was last determined and
1599 * applied by {@link #updateFullscreenMode()}.
1600 */
1601 public boolean isFullscreenMode() {
1602 return mIsFullscreen;
1603 }
1604
1605 /**
1606 * Override this to control when the input method should run in
1607 * fullscreen mode. The default implementation runs in fullsceen only
1608 * when the screen is in landscape mode. If you change what
1609 * this returns, you will need to call {@link #updateFullscreenMode()}
1610 * yourself whenever the returned value may have changed to have it
1611 * re-evaluated and applied.
1612 */
1613 public boolean onEvaluateFullscreenMode() {
1614 Configuration config = getResources().getConfiguration();
Leon Scroggins2edd6822010-01-12 11:36:13 -05001615 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
1616 return false;
1617 }
1618 if (mInputEditorInfo != null
1619 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
1620 return false;
1621 }
1622 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623 }
Gilles Debunne34703b62011-09-08 11:16:25 -07001624
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001625 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001626 * Controls the visibility of the extracted text area. This only applies
1627 * when the input method is in fullscreen mode, and thus showing extracted
1628 * text. When false, the extracted text will not be shown, allowing some
1629 * of the application to be seen behind. This is normally set for you
1630 * by {@link #onUpdateExtractingVisibility}. This controls the visibility
1631 * of both the extracted text and candidate view; the latter since it is
1632 * not useful if there is no text to see.
1633 */
1634 public void setExtractViewShown(boolean shown) {
1635 if (mExtractViewHidden == shown) {
1636 mExtractViewHidden = !shown;
1637 updateExtractFrameVisibility();
1638 }
1639 }
1640
1641 /**
1642 * Return whether the fullscreen extract view is shown. This will only
1643 * return true if {@link #isFullscreenMode()} returns true, and in that
1644 * case its value depends on the last call to
1645 * {@link #setExtractViewShown(boolean)}. This effectively lets you
1646 * determine if the application window is entirely covered (when this
1647 * returns true) or if some part of it may be shown (if this returns
1648 * false, though if {@link #isFullscreenMode()} returns true in that case
1649 * then it is probably only a sliver of the application).
1650 */
1651 public boolean isExtractViewShown() {
1652 return mIsFullscreen && !mExtractViewHidden;
1653 }
1654
1655 void updateExtractFrameVisibility() {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001656 final int vis;
The Android Open Source Project10592532009-03-18 17:39:46 -07001657 if (isFullscreenMode()) {
1658 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001659 // "vis" should be applied for the extract frame as well in the fullscreen mode.
1660 mExtractFrame.setVisibility(vis);
The Android Open Source Project10592532009-03-18 17:39:46 -07001661 } else {
1662 vis = View.VISIBLE;
1663 mExtractFrame.setVisibility(View.GONE);
1664 }
1665 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
Tarandeep Singheadb1392018-11-09 18:15:57 +01001666 if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001667 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1668 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1669 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1670 0);
1671 if (animRes != 0) {
1672 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1673 this, animRes));
1674 }
1675 }
1676 mFullscreenArea.setVisibility(vis);
1677 }
1678
1679 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001680 * Compute the interesting insets into your UI. The default implementation
1681 * uses the top of the candidates frame for the visible insets, and the
1682 * top of the input frame for the content insets. The default touchable
1683 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1684 *
The Android Open Source Project10592532009-03-18 17:39:46 -07001685 * <p>Note that this method is not called when
1686 * {@link #isExtractViewShown} returns true, since
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 * in that case the application is left as-is behind the input method and
1688 * not impacted by anything in its UI.
1689 *
1690 * @param outInsets Fill in with the current UI insets.
1691 */
1692 public void onComputeInsets(Insets outInsets) {
1693 int[] loc = mTmpLocation;
1694 if (mInputFrame.getVisibility() == View.VISIBLE) {
1695 mInputFrame.getLocationInWindow(loc);
1696 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001697 View decor = getWindow().getWindow().getDecorView();
1698 loc[1] = decor.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001700 if (isFullscreenMode()) {
1701 // In fullscreen mode, we never resize the underlying window.
1702 View decor = getWindow().getWindow().getDecorView();
1703 outInsets.contentTopInsets = decor.getHeight();
1704 } else {
1705 outInsets.contentTopInsets = loc[1];
1706 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1708 mCandidatesFrame.getLocationInWindow(loc);
1709 }
1710 outInsets.visibleTopInsets = loc[1];
1711 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001712 outInsets.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
1714
1715 /**
1716 * Re-evaluate whether the soft input area should currently be shown, and
1717 * update its UI if this has changed since the last time it
1718 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to
1719 * determine whether the input view should currently be shown. You
1720 * can use {@link #isInputViewShown()} to determine if the input view
1721 * is currently shown.
1722 */
1723 public void updateInputViewShown() {
1724 boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
Tarandeep Singheadb1392018-11-09 18:15:57 +01001725 if (mIsInputViewShown != isShown && mDecorViewVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 mIsInputViewShown = isShown;
1727 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1728 if (mInputView == null) {
1729 initialize();
1730 View v = onCreateInputView();
1731 if (v != null) {
1732 setInputView(v);
1733 }
1734 }
1735 }
1736 }
1737
1738 /**
1739 * Returns true if we have been asked to show our input view.
1740 */
1741 public boolean isShowInputRequested() {
1742 return mShowInputRequested;
1743 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01001744
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 /**
1746 * Return whether the soft input view is <em>currently</em> shown to the
1747 * user. This is the state that was last determined and
1748 * applied by {@link #updateInputViewShown()}.
1749 */
1750 public boolean isInputViewShown() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01001751 return mCanPreRender ? mWindowVisible : mIsInputViewShown && mDecorViewVisible;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001753
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001755 * Override this to control when the soft input area should be shown to the user. The default
1756 * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
1757 * unless the user shows an intention to use software keyboard. If you change what this
1758 * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
1759 * value may have changed to have it re-evaluated and applied.
1760 *
1761 * <p>When you override this method, it is recommended to call
1762 * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
1763 * returned.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 */
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001765 @CallSuper
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 public boolean onEvaluateInputViewShown() {
Yohei Yukawacf8403b2016-01-12 11:54:58 -08001767 if (mSettingsObserver == null) {
1768 Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
1769 return false;
1770 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001771 if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
1772 return true;
1773 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001774 Configuration config = getResources().getConfiguration();
1775 return config.keyboard == Configuration.KEYBOARD_NOKEYS
Ken Wakasa8710e762011-01-30 11:02:09 +09001776 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001777 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001778
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001779 /**
1780 * Controls the visibility of the candidates display area. By default
1781 * it is hidden.
1782 */
1783 public void setCandidatesViewShown(boolean shown) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001784 updateCandidatesVisibility(shown);
Tarandeep Singheadb1392018-11-09 18:15:57 +01001785 if (!mShowInputRequested && mDecorViewVisible != shown) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001786 // If we are being asked to show the candidates view while the app
1787 // has not asked for the input view to be shown, then we need
1788 // to update whether the window is shown.
1789 if (shown) {
1790 showWindow(false);
1791 } else {
satok2f913d92012-05-10 01:48:03 +09001792 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001793 }
1794 }
1795 }
1796
The Android Open Source Project10592532009-03-18 17:39:46 -07001797 void updateCandidatesVisibility(boolean shown) {
1798 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1799 if (mCandidatesVisibility != vis) {
1800 mCandidatesFrame.setVisibility(vis);
1801 mCandidatesVisibility = vis;
1802 }
1803 }
1804
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 /**
1806 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1807 * or {@link View#GONE View.GONE}) of the candidates view when it is not
The Android Open Source Project10592532009-03-18 17:39:46 -07001808 * shown. The default implementation returns GONE when
1809 * {@link #isExtractViewShown} returns true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 * otherwise VISIBLE. Be careful if you change this to return GONE in
1811 * other situations -- if showing or hiding the candidates view causes
1812 * your window to resize, this can cause temporary drawing artifacts as
1813 * the resize takes place.
1814 */
1815 public int getCandidatesHiddenVisibility() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001816 return isExtractViewShown() ? View.GONE : View.INVISIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001818
Tor Norbye7b9c9122013-05-30 16:48:33 -07001819 public void showStatusIcon(@DrawableRes int iconResId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 mStatusIcon = iconResId;
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001821 mPrivOps.updateStatusIcon(getPackageName(), iconResId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001823
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001824 public void hideStatusIcon() {
1825 mStatusIcon = 0;
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001826 mPrivOps.updateStatusIcon(null, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001827 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 /**
1830 * Force switch to a new input method, as identified by <var>id</var>. This
1831 * input method will be destroyed, and the requested one started on the
1832 * current input field.
1833 *
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001834 * @param id Unique identifier of the new input method to start.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001835 */
1836 public void switchInputMethod(String id) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001837 mPrivOps.setInputMethod(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001838 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001839
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001840 /**
1841 * Force switch to a new input method, as identified by {@code id}. This
1842 * input method will be destroyed, and the requested one started on the
1843 * current input field.
1844 *
1845 * @param id Unique identifier of the new input method to start.
1846 * @param subtype The new subtype of the new input method to be switched to.
1847 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001848 public final void switchInputMethod(String id, InputMethodSubtype subtype) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001849 mPrivOps.setInputMethodAndSubtype(id, subtype);
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001850 }
1851
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852 public void setExtractView(View view) {
1853 mExtractFrame.removeAllViews();
1854 mExtractFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001855 ViewGroup.LayoutParams.MATCH_PARENT,
1856 ViewGroup.LayoutParams.MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001857 mExtractView = view;
1858 if (view != null) {
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001859 mExtractEditText = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 com.android.internal.R.id.inputExtractEditText);
1861 mExtractEditText.setIME(this);
Mark Renouf91eb2652016-04-11 16:03:26 -04001862 mExtractAction = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863 com.android.internal.R.id.inputExtractAction);
1864 if (mExtractAction != null) {
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001865 mExtractAccessories = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 com.android.internal.R.id.inputExtractAccessories);
1867 }
1868 startExtractingText(false);
1869 } else {
1870 mExtractEditText = null;
1871 mExtractAccessories = null;
1872 mExtractAction = null;
1873 }
1874 }
1875
1876 /**
1877 * Replaces the current candidates view with a new one. You only need to
1878 * call this when dynamically changing the view; normally, you should
1879 * implement {@link #onCreateCandidatesView()} and create your view when
1880 * first needed by the input method.
1881 */
1882 public void setCandidatesView(View view) {
1883 mCandidatesFrame.removeAllViews();
1884 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001885 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 ViewGroup.LayoutParams.WRAP_CONTENT));
1887 }
1888
1889 /**
1890 * Replaces the current input view with a new one. You only need to
1891 * call this when dynamically changing the view; normally, you should
1892 * implement {@link #onCreateInputView()} and create your view when
1893 * first needed by the input method.
1894 */
1895 public void setInputView(View view) {
1896 mInputFrame.removeAllViews();
1897 mInputFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001898 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001899 ViewGroup.LayoutParams.WRAP_CONTENT));
1900 mInputView = view;
1901 }
1902
1903 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 * Called by the framework to create the layout for showing extacted text.
1905 * Only called when in fullscreen mode. The returned view hierarchy must
1906 * have an {@link ExtractEditText} whose ID is
1907 * {@link android.R.id#inputExtractEditText}.
1908 */
1909 public View onCreateExtractTextView() {
1910 return mInflater.inflate(
1911 com.android.internal.R.layout.input_method_extract_view, null);
1912 }
1913
1914 /**
1915 * Create and return the view hierarchy used to show candidates. This will
1916 * be called once, when the candidates are first displayed. You can return
1917 * null to have no candidates view; the default implementation returns null.
1918 *
1919 * <p>To control when the candidates view is displayed, use
1920 * {@link #setCandidatesViewShown(boolean)}.
1921 * To change the candidates view after the first one is created by this
1922 * function, use {@link #setCandidatesView(View)}.
1923 */
1924 public View onCreateCandidatesView() {
1925 return null;
1926 }
1927
1928 /**
1929 * Create and return the view hierarchy used for the input area (such as
1930 * a soft keyboard). This will be called once, when the input area is
1931 * first displayed. You can return null to have no input area; the default
1932 * implementation returns null.
1933 *
1934 * <p>To control when the input view is displayed, implement
1935 * {@link #onEvaluateInputViewShown()}.
1936 * To change the input view after the first one is created by this
1937 * function, use {@link #setInputView(View)}.
1938 */
1939 public View onCreateInputView() {
1940 return null;
1941 }
1942
1943 /**
1944 * Called when the input view is being shown and input has started on
1945 * a new editor. This will always be called after {@link #onStartInput},
1946 * allowing you to do your general setup there and just view-specific
1947 * setup here. You are guaranteed that {@link #onCreateInputView()} will
1948 * have been called some time before this function is called.
1949 *
1950 * @param info Description of the type of text being edited.
1951 * @param restarting Set to true if we are restarting input on the
1952 * same text field as before.
1953 */
1954 public void onStartInputView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001955 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001956 }
1957
1958 /**
1959 * Called when the input view is being hidden from the user. This will
1960 * be called either prior to hiding the window, or prior to switching to
1961 * another target for editing.
1962 *
1963 * <p>The default
1964 * implementation uses the InputConnection to clear any active composing
1965 * text; you can override this (not calling the base class implementation)
1966 * to perform whatever behavior you would like.
1967 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001968 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 * called immediately after.
1970 */
1971 public void onFinishInputView(boolean finishingInput) {
1972 if (!finishingInput) {
1973 InputConnection ic = getCurrentInputConnection();
1974 if (ic != null) {
1975 ic.finishComposingText();
1976 }
1977 }
1978 }
1979
1980 /**
1981 * Called when only the candidates view has been shown for showing
1982 * processing as the user enters text through a hard keyboard.
1983 * This will always be called after {@link #onStartInput},
1984 * allowing you to do your general setup there and just view-specific
1985 * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
1986 * will have been called some time before this function is called.
1987 *
1988 * <p>Note that this will <em>not</em> be called when the input method
1989 * is running in full editing mode, and thus receiving
1990 * {@link #onStartInputView} to initiate that operation. This is only
1991 * for the case when candidates are being shown while the input method
1992 * editor is hidden but wants to show its candidates UI as text is
1993 * entered through some other mechanism.
1994 *
1995 * @param info Description of the type of text being edited.
1996 * @param restarting Set to true if we are restarting input on the
1997 * same text field as before.
1998 */
1999 public void onStartCandidatesView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002000 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002001 }
2002
2003 /**
2004 * Called when the candidates view is being hidden from the user. This will
2005 * be called either prior to hiding the window, or prior to switching to
2006 * another target for editing.
2007 *
2008 * <p>The default
2009 * implementation uses the InputConnection to clear any active composing
2010 * text; you can override this (not calling the base class implementation)
2011 * to perform whatever behavior you would like.
2012 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08002013 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002014 * called immediately after.
2015 */
2016 public void onFinishCandidatesView(boolean finishingInput) {
2017 if (!finishingInput) {
2018 InputConnection ic = getCurrentInputConnection();
2019 if (ic != null) {
2020 ic.finishComposingText();
2021 }
2022 }
2023 }
2024
2025 /**
2026 * The system has decided that it may be time to show your input method.
2027 * This is called due to a corresponding call to your
The Android Open Source Project4df24232009-03-05 14:34:35 -08002028 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002029 * method. The default implementation uses
2030 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
2031 * and the current configuration to decide whether the input view should
2032 * be shown at this point.
2033 *
2034 * @param flags Provides additional information about the show request,
The Android Open Source Project4df24232009-03-05 14:34:35 -08002035 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002036 * @param configChange This is true if we are re-showing due to a
2037 * configuration change.
2038 * @return Returns true to indicate that the window should be shown.
2039 */
2040 public boolean onShowInputRequested(int flags, boolean configChange) {
2041 if (!onEvaluateInputViewShown()) {
2042 return false;
2043 }
2044 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
2045 if (!configChange && onEvaluateFullscreenMode()) {
2046 // Don't show if this is not explicitly requested by the user and
2047 // the input method is fullscreen. That would be too disruptive.
2048 // However, we skip this change for a config change, since if
2049 // the IME is already shown we do want to go into fullscreen
2050 // mode at this point.
2051 return false;
2052 }
Yohei Yukawad0d07972016-05-04 11:56:35 -07002053 if (!mSettingsObserver.shouldShowImeWithHardKeyboard() &&
2054 getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002055 // And if the device has a hard keyboard, even if it is
2056 // currently hidden, don't show the input method implicitly.
2057 // These kinds of devices don't need it that much.
2058 return false;
2059 }
2060 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002061 return true;
2062 }
Yohei Yukawaef5b4652016-04-03 22:50:11 -07002063
2064 /**
2065 * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal
2066 * states depending on its result. Since {@link #onShowInputRequested(int, boolean)} is
2067 * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
2068 * to have this method to ensure that those internal states are always updated no matter how
2069 * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
2070 * @param flags Provides additional information about the show request,
2071 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
2072 * @param configChange This is true if we are re-showing due to a
2073 * configuration change.
2074 * @return Returns true to indicate that the window should be shown.
2075 * @see #onShowInputRequested(int, boolean)
2076 */
2077 private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
2078 final boolean result = onShowInputRequested(flags, configChange);
Feng Cao97ec1c42020-03-25 12:20:42 -07002079 mInlineSuggestionSessionController.notifyOnShowInputRequested(result);
Yohei Yukawaef5b4652016-04-03 22:50:11 -07002080 if (result) {
2081 mShowInputFlags = flags;
2082 } else {
2083 mShowInputFlags = 0;
2084 }
2085 return result;
2086 }
2087
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002088 public void showWindow(boolean showInput) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07002089 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002090 + " mShowInputRequested=" + mShowInputRequested
Tarandeep Singheadb1392018-11-09 18:15:57 +01002091 + " mViewsCreated=" + mViewsCreated
2092 + " mDecorViewVisible=" + mDecorViewVisible
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 + " mWindowVisible=" + mWindowVisible
Yohei Yukawaef5b4652016-04-03 22:50:11 -07002094 + " mInputStarted=" + mInputStarted
2095 + " mShowInputFlags=" + mShowInputFlags);
2096
The Android Open Source Project10592532009-03-18 17:39:46 -07002097 if (mInShowWindow) {
2098 Log.w(TAG, "Re-entrance in to showWindow");
2099 return;
2100 }
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002101
Tarandeep Singheadb1392018-11-09 18:15:57 +01002102 mDecorViewWasVisible = mDecorViewVisible;
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002103 mInShowWindow = true;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002104 boolean isPreRenderedAndInvisible = mIsPreRendered && !mWindowVisible;
2105 final int previousImeWindowStatus =
2106 (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
2107 ? (isPreRenderedAndInvisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
2108 startViews(prepareWindow(showInput));
2109 final int nextImeWindowStatus = mapToImeWindowStatus();
2110 if (previousImeWindowStatus != nextImeWindowStatus) {
2111 setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
2112 }
2113
2114 // compute visibility
2115 onWindowShown();
2116 mIsPreRendered = mCanPreRender;
2117 if (mIsPreRendered) {
2118 onPreRenderedWindowVisibilityChanged(true /* setVisible */);
2119 } else {
2120 // Pre-rendering not supported.
2121 if (DEBUG) Log.d(TAG, "No pre-rendering supported");
2122 mWindowVisible = true;
2123 }
2124
2125 // request draw for the IME surface.
2126 // When IME is not pre-rendered, this will actually show the IME.
2127 if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
2128 if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
2129 mWindow.show();
2130 }
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002131 maybeNotifyPreRendered();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002132 mDecorViewWasVisible = true;
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002133 mInShowWindow = false;
The Android Open Source Project10592532009-03-18 17:39:46 -07002134 }
satok06487a52010-10-29 11:37:18 +09002135
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002136 /**
2137 * Notify {@link android.view.ImeInsetsSourceConsumer} if IME has been pre-rendered
2138 * for current EditorInfo, when pre-rendering is enabled.
2139 */
2140 private void maybeNotifyPreRendered() {
2141 if (!mCanPreRender || !mIsPreRendered) {
2142 return;
2143 }
2144 mPrivOps.reportPreRendered(getCurrentInputEditorInfo());
2145 }
2146
2147
Tarandeep Singheadb1392018-11-09 18:15:57 +01002148 private boolean prepareWindow(boolean showInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002149 boolean doShowInput = false;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002150 mDecorViewVisible = true;
Yohei Yukawaac8bdd22015-09-11 18:17:11 -07002151 if (!mShowInputRequested && mInputStarted && showInput) {
2152 doShowInput = true;
2153 mShowInputRequested = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002154 }
satok06487a52010-10-29 11:37:18 +09002155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002156 if (DEBUG) Log.v(TAG, "showWindow: updating UI");
2157 initialize();
2158 updateFullscreenMode();
2159 updateInputViewShown();
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002160
Tarandeep Singheadb1392018-11-09 18:15:57 +01002161 if (!mViewsCreated) {
2162 mViewsCreated = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002163 initialize();
2164 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
2165 View v = onCreateCandidatesView();
2166 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
2167 if (v != null) {
2168 setCandidatesView(v);
2169 }
2170 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002171 return doShowInput;
2172 }
2173
2174 private void startViews(boolean doShowInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002175 if (mShowInputRequested) {
2176 if (!mInputViewStarted) {
2177 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
2178 mInputViewStarted = true;
Feng Cao97ec1c42020-03-25 12:20:42 -07002179 mInlineSuggestionSessionController.notifyOnStartInputView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002180 onStartInputView(mInputEditorInfo, false);
2181 }
2182 } else if (!mCandidatesViewStarted) {
2183 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
2184 mCandidatesViewStarted = true;
2185 onStartCandidatesView(mInputEditorInfo, false);
2186 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002187 if (doShowInput) startExtractingText(false);
2188 }
satok06487a52010-10-29 11:37:18 +09002189
Tarandeep Singheadb1392018-11-09 18:15:57 +01002190 private void onPreRenderedWindowVisibilityChanged(boolean setVisible) {
2191 mWindowVisible = setVisible;
2192 mShowInputFlags = setVisible ? mShowInputFlags : 0;
2193 mShowInputRequested = setVisible;
2194 mDecorViewVisible = setVisible;
2195 if (setVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002196 onWindowShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002197 }
2198 }
satok06487a52010-10-29 11:37:18 +09002199
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002200 /**
2201 * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002202 * {@link ViewRootImpl.sNewInsetsMode} is enabled.
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002203 * @param setVisible {@code true} to make it visible, false to hide it.
2204 */
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002205 private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
2206 if (!isVisibilityAppliedUsingInsetsConsumer()) {
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002207 return;
2208 }
Tarandeep Singh4fe5b652020-02-20 17:20:19 -08002209 mPrivOps.applyImeVisibility(setVisible
2210 ? mCurShowInputToken : mCurHideInputToken, setVisible);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002211 }
2212
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002213 private boolean isVisibilityAppliedUsingInsetsConsumer() {
2214 return ViewRootImpl.sNewInsetsMode > NEW_INSETS_MODE_NONE;
2215 }
2216
Tarandeep Singheadb1392018-11-09 18:15:57 +01002217 private void finishViews(boolean finishingInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002218 if (mInputViewStarted) {
2219 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
Feng Cao97ec1c42020-03-25 12:20:42 -07002220 mInlineSuggestionSessionController.notifyOnFinishInputView();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002221 onFinishInputView(finishingInput);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002222 } else if (mCandidatesViewStarted) {
2223 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
Tarandeep Singheadb1392018-11-09 18:15:57 +01002224 onFinishCandidatesView(finishingInput);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002225 }
2226 mInputViewStarted = false;
2227 mCandidatesViewStarted = false;
satokf17db9f2011-09-14 18:55:58 +09002228 }
2229
satok2f913d92012-05-10 01:48:03 +09002230 private void doHideWindow() {
Yohei Yukawac54c1172018-09-06 11:39:50 -07002231 setImeWindowStatus(0, mBackDisposition);
satok2f913d92012-05-10 01:48:03 +09002232 hideWindow();
2233 }
2234
satokf17db9f2011-09-14 18:55:58 +09002235 public void hideWindow() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01002236 if (DEBUG) Log.v(TAG, "CALL: hideWindow");
2237 mIsPreRendered = false;
2238 mWindowVisible = false;
2239 finishViews(false /* finishingInput */);
2240 if (mDecorViewVisible) {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002241 // When insets API is enabled, it is responsible for client and server side
2242 // visibility of IME window.
Taran Singhe68d6f52020-03-16 11:53:05 -07002243 if (isVisibilityAppliedUsingInsetsConsumer()) {
Taran Singhf739e472020-03-20 13:08:10 -07002244 if (mInputView != null) {
2245 mInputView.dispatchWindowVisibilityChanged(View.GONE);
2246 }
Taran Singhe68d6f52020-03-16 11:53:05 -07002247 } else {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002248 mWindow.hide();
2249 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002250 mDecorViewVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002251 onWindowHidden();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002252 mDecorViewWasVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002253 }
Seigo Nonaka93c47ea2015-07-14 15:05:04 +09002254 updateFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002255 }
satok06487a52010-10-29 11:37:18 +09002256
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002257 /**
tiansiming [田思明]b9025932018-02-05 18:28:28 +08002258 * Called immediately before the input method window is shown to the user.
2259 * You could override this to prepare for the window to be shown
2260 * (update view structure etc).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002261 */
2262 public void onWindowShown() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002263 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002264 }
2265
2266 /**
2267 * Called when the input method window has been hidden from the user,
2268 * after previously being visible.
2269 */
2270 public void onWindowHidden() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002271 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002272 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -07002273
2274 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002275 * Called when a new client has bound to the input method. This
2276 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
2277 * and {@link #onFinishInput()} calls as the user navigates through its
2278 * UI. Upon this call you know that {@link #getCurrentInputBinding}
2279 * and {@link #getCurrentInputConnection} return valid objects.
2280 */
2281 public void onBindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002282 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002283 }
2284
2285 /**
2286 * Called when the previous bound client is no longer associated
2287 * with the input method. After returning {@link #getCurrentInputBinding}
2288 * and {@link #getCurrentInputConnection} will no longer return
2289 * valid objects.
2290 */
2291 public void onUnbindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002292 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002293 }
2294
2295 /**
2296 * Called to inform the input method that text input has started in an
2297 * editor. You should use this callback to initialize the state of your
2298 * input to match the state of the editor given to it.
2299 *
2300 * @param attribute The attributes of the editor that input is starting
2301 * in.
2302 * @param restarting Set to true if input is restarting in the same
2303 * editor such as because the application has changed the text in
2304 * the editor. Otherwise will be false, indicating this is a new
2305 * session with the editor.
2306 */
2307 public void onStartInput(EditorInfo attribute, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002308 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002309 }
2310
2311 void doFinishInput() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01002312 if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
2313 finishViews(true /* finishingInput */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002314 if (mInputStarted) {
Feng Cao97ec1c42020-03-25 12:20:42 -07002315 mInlineSuggestionSessionController.notifyOnFinishInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002316 if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
2317 onFinishInput();
2318 }
2319 mInputStarted = false;
2320 mStartedInputConnection = null;
2321 mCurCompletions = null;
2322 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08002323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002324 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
2325 if (!restarting) {
2326 doFinishInput();
2327 }
2328 mInputStarted = true;
2329 mStartedInputConnection = ic;
2330 mInputEditorInfo = attribute;
2331 initialize();
Feng Cao97ec1c42020-03-25 12:20:42 -07002332 mInlineSuggestionSessionController.notifyOnStartInput(
2333 attribute == null ? null : attribute.packageName,
2334 attribute == null ? null : attribute.autofillId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002335 if (DEBUG) Log.v(TAG, "CALL: onStartInput");
2336 onStartInput(attribute, restarting);
Tarandeep Singheadb1392018-11-09 18:15:57 +01002337 if (mDecorViewVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002338 if (mShowInputRequested) {
2339 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
2340 mInputViewStarted = true;
Feng Cao97ec1c42020-03-25 12:20:42 -07002341 mInlineSuggestionSessionController.notifyOnStartInputView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002342 onStartInputView(mInputEditorInfo, restarting);
2343 startExtractingText(true);
2344 } else if (mCandidatesVisibility == View.VISIBLE) {
2345 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
2346 mCandidatesViewStarted = true;
2347 onStartCandidatesView(mInputEditorInfo, restarting);
2348 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002349 } else if (mCanPreRender && mInputEditorInfo != null && mStartedInputConnection != null) {
2350 // Pre-render IME views and window when real EditorInfo is available.
2351 // pre-render IME window and keep it invisible.
2352 if (DEBUG) Log.v(TAG, "Pre-Render IME for " + mInputEditorInfo.fieldName);
2353 if (mInShowWindow) {
2354 Log.w(TAG, "Re-entrance in to showWindow");
2355 return;
2356 }
2357
2358 mDecorViewWasVisible = mDecorViewVisible;
2359 mInShowWindow = true;
2360 startViews(prepareWindow(true /* showInput */));
2361
2362 // compute visibility
2363 mIsPreRendered = true;
2364 onPreRenderedWindowVisibilityChanged(false /* setVisible */);
2365
2366 // request draw for the IME surface.
2367 // When IME is not pre-rendered, this will actually show the IME.
2368 if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
2369 mWindow.show();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002370 maybeNotifyPreRendered();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002371 mDecorViewWasVisible = true;
2372 mInShowWindow = false;
2373 } else {
2374 mIsPreRendered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002375 }
2376 }
2377
2378 /**
2379 * Called to inform the input method that text input has finished in
2380 * the last editor. At this point there may be a call to
2381 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
2382 * new editor, or the input method may be left idle. This method is
2383 * <em>not</em> called when input restarts in the same editor.
2384 *
2385 * <p>The default
2386 * implementation uses the InputConnection to clear any active composing
2387 * text; you can override this (not calling the base class implementation)
2388 * to perform whatever behavior you would like.
2389 */
2390 public void onFinishInput() {
2391 InputConnection ic = getCurrentInputConnection();
2392 if (ic != null) {
2393 ic.finishComposingText();
2394 }
2395 }
2396
2397 /**
2398 * Called when the application has reported auto-completion candidates that
2399 * it would like to have the input method displayed. Typically these are
2400 * only used when an input method is running in full-screen mode, since
2401 * otherwise the user can see and interact with the pop-up window of
2402 * completions shown by the application.
2403 *
2404 * <p>The default implementation here does nothing.
2405 */
2406 public void onDisplayCompletions(CompletionInfo[] completions) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002407 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002408 }
2409
2410 /**
2411 * Called when the application has reported new extracted text to be shown
2412 * due to changes in its current text state. The default implementation
2413 * here places the new text in the extract edit text, when the input
2414 * method is running in fullscreen mode.
2415 */
2416 public void onUpdateExtractedText(int token, ExtractedText text) {
2417 if (mExtractedToken != token) {
2418 return;
2419 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002420 if (text != null) {
2421 if (mExtractEditText != null) {
2422 mExtractedText = text;
2423 mExtractEditText.setExtractedText(text);
2424 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002425 }
2426 }
2427
2428 /**
2429 * Called when the application has reported a new selection region of
2430 * the text. This is called whether or not the input method has requested
2431 * extracted text updates, although if so it will not receive this call
2432 * if the extracted text has changed as well.
Jean Chalardc743cb92013-09-12 16:28:45 +09002433 *
2434 * <p>Be careful about changing the text in reaction to this call with
2435 * methods such as setComposingText, commitText or
2436 * deleteSurroundingText. If the cursor moves as a result, this method
2437 * will be called again, which may result in an infinite loop.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002438 *
2439 * <p>The default implementation takes care of updating the cursor in
2440 * the extract text, if it is being shown.
2441 */
2442 public void onUpdateSelection(int oldSelStart, int oldSelEnd,
2443 int newSelStart, int newSelEnd,
2444 int candidatesStart, int candidatesEnd) {
2445 final ExtractEditText eet = mExtractEditText;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07002446 if (eet != null && isFullscreenMode() && mExtractedText != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002447 final int off = mExtractedText.startOffset;
2448 eet.startInternalChanges();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07002449 newSelStart -= off;
2450 newSelEnd -= off;
2451 final int len = eet.getText().length();
2452 if (newSelStart < 0) newSelStart = 0;
2453 else if (newSelStart > len) newSelStart = len;
2454 if (newSelEnd < 0) newSelEnd = 0;
2455 else if (newSelEnd > len) newSelEnd = len;
2456 eet.setSelection(newSelStart, newSelEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002457 eet.finishInternalChanges();
2458 }
2459 }
2460
2461 /**
satok863fcd62011-06-21 17:38:02 +09002462 * Called when the user tapped or clicked a text view.
2463 * IMEs can't rely on this method being called because this was not part of the original IME
2464 * protocol, so applications with custom text editing written before this method appeared will
2465 * not call to inform the IME of this interaction.
2466 * @param focusChanged true if the user changed the focused view by this click.
Yohei Yukawa0eb8d162019-01-22 21:47:57 -08002467 * @see InputMethodManager#viewClicked(View)
2468 * @deprecated The method may not be called for composite {@link View} that works as a giant
2469 * "Canvas", which can host its own UI hierarchy and sub focus state.
2470 * {@link android.webkit.WebView} is a good example. Application / IME developers
2471 * should not rely on this method. If your goal is just being notified when an
2472 * on-going input is interrupted, simply monitor {@link #onFinishInput()}.
satok863fcd62011-06-21 17:38:02 +09002473 */
Yohei Yukawa0eb8d162019-01-22 21:47:57 -08002474 @Deprecated
satok863fcd62011-06-21 17:38:02 +09002475 public void onViewClicked(boolean focusChanged) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002476 // Intentionally empty
satok863fcd62011-06-21 17:38:02 +09002477 }
2478
2479 /**
Yohei Yukawaa277db22014-08-21 18:38:44 -07002480 * Called when the application has reported a new location of its text
2481 * cursor. This is only called if explicitly requested by the input method.
2482 * The default implementation does nothing.
Andrew Solovay5c05ded2018-10-02 14:14:42 -07002483 * @deprecated Use {@link #onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002484 */
Yohei Yukawaa277db22014-08-21 18:38:44 -07002485 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002486 public void onUpdateCursor(Rect newCursor) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002487 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002488 }
2489
2490 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +09002491 * Called when the application has reported a new location of its text insertion point and
2492 * characters in the composition string. This is only called if explicitly requested by the
2493 * input method. The default implementation does nothing.
2494 * @param cursorAnchorInfo The positional information of the text insertion point and the
2495 * composition string.
2496 */
2497 public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
2498 // Intentionally empty
2499 }
2500
2501 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002502 * Close this input method's soft input area, removing it from the display.
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002503 *
2504 * The input method will continue running, but the user can no longer use it to generate input
2505 * by touching the screen.
2506 *
2507 * @see InputMethodManager#HIDE_IMPLICIT_ONLY
2508 * @see InputMethodManager#HIDE_NOT_ALWAYS
2509 * @param flags Provides additional operating flags.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002510 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08002511 public void requestHideSelf(int flags) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07002512 mPrivOps.hideMySoftInput(flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002513 }
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002514
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002515 /**
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002516 * Show the input method's soft input area, so the user sees the input method window and can
2517 * interact with it.
2518 *
2519 * @see InputMethodManager#SHOW_IMPLICIT
2520 * @see InputMethodManager#SHOW_FORCED
2521 * @param flags Provides additional operating flags.
The Android Open Source Project4df24232009-03-05 14:34:35 -08002522 */
Tarandeep Singh45136992018-03-08 10:52:03 -08002523 public final void requestShowSelf(int flags) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07002524 mPrivOps.showMySoftInput(flags);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002525 }
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002526
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002527 private boolean handleBack(boolean doIt) {
2528 if (mShowInputRequested) {
2529 // If the soft input area is shown, back closes it and we
2530 // consume the back key.
2531 if (doIt) requestHideSelf(0);
2532 return true;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002533 } else if (mDecorViewVisible) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002534 if (mCandidatesVisibility == View.VISIBLE) {
2535 // If we are showing candidates even if no input area, then
2536 // hide them.
2537 if (doIt) setCandidatesViewShown(false);
2538 } else {
2539 // If we have the window visible for some other reason --
2540 // most likely to show candidates -- then just get rid
2541 // of it. This really shouldn't happen, but just in case...
satok2f913d92012-05-10 01:48:03 +09002542 if (doIt) doHideWindow();
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002543 }
2544 return true;
2545 }
2546 return false;
2547 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002548
2549 /**
Andrew Solovay5c05ded2018-10-02 14:14:42 -07002550 * @return {@link ExtractEditText} if it is considered to be visible and active. Otherwise
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002551 * {@code null} is returned.
2552 */
2553 private ExtractEditText getExtractEditTextIfVisible() {
2554 if (!isExtractViewShown() || !isInputViewShown()) {
2555 return null;
2556 }
2557 return mExtractEditText;
2558 }
2559
The Android Open Source Project4df24232009-03-05 14:34:35 -08002560 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -07002561 * Called back when a {@link KeyEvent} is forwarded from the target application.
2562 *
2563 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK} if the IME is
2564 * currently shown , to possibly hide it when the key goes up (if not canceled or long pressed).
2565 * In addition, in fullscreen mode only, it will consume DPAD movement events to move the cursor
2566 * in the extracted text view, not allowing them to perform navigation in the underlying
2567 * application.</p>
2568 *
2569 * <p>The default implementation does not take flags specified to
2570 * {@link #setBackDisposition(int)} into account, even on API version
2571 * {@link android.os.Build.VERSION_CODES#P} and later devices. IME developers are responsible
2572 * for making sure that their special handling for {@link KeyEvent#KEYCODE_BACK} are consistent
2573 * with the flag they specified to {@link #setBackDisposition(int)}.</p>
2574 *
2575 * @param keyCode The value in {@code event.getKeyCode()}
2576 * @param event Description of the key event
2577 *
2578 * @return {@code true} if the event is consumed by the IME and the application no longer needs
2579 * to consume it. Return {@code false} when the event should be handled as if the IME
2580 * had not seen the event at all.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002581 */
2582 public boolean onKeyDown(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002583 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002584 final ExtractEditText eet = getExtractEditTextIfVisible();
2585 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2586 return true;
2587 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002588 if (handleBack(false)) {
2589 event.startTracking();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002590 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002591 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002592 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002593 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002594 return doMovementKey(keyCode, event, MOVEMENT_DOWN);
2595 }
2596
2597 /**
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002598 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
2599 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
2600 * the event).
2601 */
2602 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
2603 return false;
2604 }
2605
2606 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002607 * Override this to intercept special key multiple events before they are
2608 * processed by the
2609 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002610 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002611 * will occur as if the IME had not seen the event at all.
2612 *
2613 * <p>The default implementation always returns false, except when
2614 * in fullscreen mode, where it will consume DPAD movement
2615 * events to move the cursor in the extracted text view, not allowing
2616 * them to perform navigation in the underlying application.
2617 */
2618 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
2619 return doMovementKey(keyCode, event, count);
2620 }
2621
2622 /**
2623 * Override this to intercept key up events before they are processed by the
2624 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002625 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002626 * will occur as if the IME had not seen the event at all.
2627 *
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002628 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
2629 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
2630 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002631 * events to move the cursor in the extracted text view, not allowing
2632 * them to perform navigation in the underlying application.
2633 */
2634 public boolean onKeyUp(int keyCode, KeyEvent event) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002635 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
2636 final ExtractEditText eet = getExtractEditTextIfVisible();
2637 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2638 return true;
2639 }
2640 if (event.isTracking() && !event.isCanceled()) {
2641 return handleBack(true);
2642 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002643 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002644 return doMovementKey(keyCode, event, MOVEMENT_UP);
2645 }
2646
Victoria Leaseb38070c2012-08-24 13:46:02 -07002647 /**
2648 * Override this to intercept trackball motion events before they are
2649 * processed by the application.
2650 * If you return true, the application will not itself process the event.
2651 * If you return false, the normal application processing will occur as if
2652 * the IME had not seen the event at all.
2653 */
satokab751aa2010-09-14 19:17:36 +09002654 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002655 public boolean onTrackballEvent(MotionEvent event) {
Victoria Leaseb38070c2012-08-24 13:46:02 -07002656 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
2657 return false;
2658 }
2659
2660 /**
2661 * Override this to intercept generic motion events before they are
2662 * processed by the application.
2663 * If you return true, the application will not itself process the event.
2664 * If you return false, the normal application processing will occur as if
2665 * the IME had not seen the event at all.
2666 */
2667 @Override
2668 public boolean onGenericMotionEvent(MotionEvent event) {
2669 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002670 return false;
2671 }
2672
2673 public void onAppPrivateCommand(String action, Bundle data) {
2674 }
2675
The Android Open Source Project4df24232009-03-05 14:34:35 -08002676 /**
2677 * Handle a request by the system to toggle the soft input area.
2678 */
2679 private void onToggleSoftInput(int showFlags, int hideFlags) {
2680 if (DEBUG) Log.v(TAG, "toggleSoftInput()");
2681 if (isInputViewShown()) {
2682 requestHideSelf(hideFlags);
2683 } else {
2684 requestShowSelf(showFlags);
2685 }
2686 }
2687
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002688 static final int MOVEMENT_DOWN = -1;
2689 static final int MOVEMENT_UP = -2;
2690
2691 void reportExtractedMovement(int keyCode, int count) {
2692 int dx = 0, dy = 0;
2693 switch (keyCode) {
2694 case KeyEvent.KEYCODE_DPAD_LEFT:
2695 dx = -count;
2696 break;
2697 case KeyEvent.KEYCODE_DPAD_RIGHT:
2698 dx = count;
2699 break;
2700 case KeyEvent.KEYCODE_DPAD_UP:
2701 dy = -count;
2702 break;
2703 case KeyEvent.KEYCODE_DPAD_DOWN:
2704 dy = count;
2705 break;
2706 }
satokab751aa2010-09-14 19:17:36 +09002707 onExtractedCursorMovement(dx, dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002708 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002709
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002710 boolean doMovementKey(int keyCode, KeyEvent event, int count) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002711 final ExtractEditText eet = getExtractEditTextIfVisible();
2712 if (eet != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002713 // If we are in fullscreen mode, the cursor will move around
2714 // the extract edit text, but should NOT cause focus to move
2715 // to other fields.
2716 MovementMethod movement = eet.getMovementMethod();
2717 Layout layout = eet.getLayout();
2718 if (movement != null && layout != null) {
2719 // We want our own movement method to handle the key, so the
2720 // cursor will properly move in our own word wrapping.
2721 if (count == MOVEMENT_DOWN) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002722 if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002723 reportExtractedMovement(keyCode, 1);
2724 return true;
2725 }
2726 } else if (count == MOVEMENT_UP) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002727 if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002728 return true;
2729 }
2730 } else {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002731 if (movement.onKeyOther(eet, eet.getText(), event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002732 reportExtractedMovement(keyCode, count);
2733 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07002734 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002735 if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002736 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002737 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002738 while (--count > 0) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002739 movement.onKeyDown(eet, eet.getText(), keyCode, down);
2740 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002741 }
2742 reportExtractedMovement(keyCode, count);
2743 }
2744 }
2745 }
2746 }
2747 // Regardless of whether the movement method handled the key,
2748 // we never allow DPAD navigation to the application.
2749 switch (keyCode) {
2750 case KeyEvent.KEYCODE_DPAD_LEFT:
2751 case KeyEvent.KEYCODE_DPAD_RIGHT:
2752 case KeyEvent.KEYCODE_DPAD_UP:
2753 case KeyEvent.KEYCODE_DPAD_DOWN:
2754 return true;
2755 }
2756 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002757
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002758 return false;
2759 }
2760
2761 /**
2762 * Send the given key event code (as defined by {@link KeyEvent}) to the
2763 * current input connection is a key down + key up event pair. The sent
2764 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
2765 * set, so that the recipient can identify them as coming from a software
2766 * input method, and
2767 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
2768 * that they don't impact the current touch mode of the UI.
2769 *
Jean Chalard405bc512012-05-29 19:12:34 +09002770 * <p>Note that it's discouraged to send such key events in normal operation;
2771 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
2772 * text fields, or for non-rich input methods. A reasonably capable software
2773 * input method should use the
2774 * {@link android.view.inputmethod.InputConnection#commitText} family of methods
2775 * to send text to an application, rather than sending key events.</p>
2776 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002777 * @param keyEventCode The raw key code to send, as defined by
2778 * {@link KeyEvent}.
2779 */
2780 public void sendDownUpKeyEvents(int keyEventCode) {
2781 InputConnection ic = getCurrentInputConnection();
2782 if (ic == null) return;
2783 long eventTime = SystemClock.uptimeMillis();
2784 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002785 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002786 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
Tadashi G. Takaokacb95cd62012-10-26 17:20:59 +09002787 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002788 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002789 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2790 }
2791
2792 /**
2793 * Ask the input target to execute its default action via
2794 * {@link InputConnection#performEditorAction
2795 * InputConnection.performEditorAction()}.
2796 *
2797 * @param fromEnterKey If true, this will be executed as if the user had
2798 * pressed an enter key on the keyboard, that is it will <em>not</em>
2799 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
2800 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
2801 * sent regardless of how the editor has set that flag.
2802 *
2803 * @return Returns a boolean indicating whether an action has been sent.
2804 * If false, either the editor did not specify a default action or it
2805 * does not want an action from the enter key. If true, the action was
2806 * sent (or there was no input connection at all).
2807 */
2808 public boolean sendDefaultEditorAction(boolean fromEnterKey) {
2809 EditorInfo ei = getCurrentInputEditorInfo();
2810 if (ei != null &&
2811 (!fromEnterKey || (ei.imeOptions &
2812 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
2813 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
2814 EditorInfo.IME_ACTION_NONE) {
2815 // If the enter key was pressed, and the editor has a default
2816 // action associated with pressing enter, then send it that
2817 // explicit action instead of the key event.
2818 InputConnection ic = getCurrentInputConnection();
2819 if (ic != null) {
2820 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
2821 }
2822 return true;
2823 }
2824
2825 return false;
2826 }
2827
2828 /**
2829 * Send the given UTF-16 character to the current input connection. Most
2830 * characters will be delivered simply by calling
2831 * {@link InputConnection#commitText InputConnection.commitText()} with
2832 * the character; some, however, may be handled different. In particular,
2833 * the enter character ('\n') will either be delivered as an action code
Jean Chalard405bc512012-05-29 19:12:34 +09002834 * or a raw key event, as appropriate. Consider this as a convenience
2835 * method for IMEs that do not have a full implementation of actions; a
2836 * fully complying IME will decide of the right action for each event and
2837 * will likely never call this method except maybe to handle events coming
2838 * from an actual hardware keyboard.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002839 *
2840 * @param charCode The UTF-16 character code to send.
2841 */
2842 public void sendKeyChar(char charCode) {
2843 switch (charCode) {
2844 case '\n': // Apps may be listening to an enter key to perform an action
2845 if (!sendDefaultEditorAction(true)) {
2846 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2847 }
2848 break;
2849 default:
2850 // Make sure that digits go through any text watcher on the client side.
2851 if (charCode >= '0' && charCode <= '9') {
2852 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2853 } else {
2854 InputConnection ic = getCurrentInputConnection();
2855 if (ic != null) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002856 ic.commitText(String.valueOf(charCode), 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002857 }
2858 }
2859 break;
2860 }
2861 }
2862
2863 /**
2864 * This is called when the user has moved the cursor in the extracted
2865 * text view, when running in fullsreen mode. The default implementation
2866 * performs the corresponding selection change on the underlying text
2867 * editor.
2868 */
2869 public void onExtractedSelectionChanged(int start, int end) {
2870 InputConnection conn = getCurrentInputConnection();
2871 if (conn != null) {
2872 conn.setSelection(start, end);
2873 }
2874 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002875
2876 /**
2877 * @hide
2878 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002879 @UnsupportedAppUsage
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002880 public void onExtractedDeleteText(int start, int end) {
2881 InputConnection conn = getCurrentInputConnection();
2882 if (conn != null) {
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002883 conn.finishComposingText();
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002884 conn.setSelection(start, start);
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002885 conn.deleteSurroundingText(0, end - start);
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002886 }
2887 }
2888
2889 /**
2890 * @hide
2891 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002892 @UnsupportedAppUsage
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002893 public void onExtractedReplaceText(int start, int end, CharSequence text) {
2894 InputConnection conn = getCurrentInputConnection();
2895 if (conn != null) {
2896 conn.setComposingRegion(start, end);
2897 conn.commitText(text, 1);
2898 }
2899 }
2900
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002901 /**
Gilles Debunnee300be92011-12-06 10:15:56 -08002902 * @hide
2903 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002904 @UnsupportedAppUsage
Gilles Debunnee300be92011-12-06 10:15:56 -08002905 public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2906 InputConnection conn = getCurrentInputConnection();
2907 if (conn != null) {
2908 if (!conn.setSelection(start, end)) return;
2909 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2910 if (text instanceof Spannable) {
2911 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2912 conn.setComposingRegion(start, end);
2913 conn.commitText(text, 1);
2914 }
2915 }
2916 }
2917
2918 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002919 * This is called when the user has clicked on the extracted text view,
2920 * when running in fullscreen mode. The default implementation hides
2921 * the candidates view when this happens, but only if the extracted text
2922 * editor has a vertical scroll bar because its text doesn't fit.
2923 * Re-implement this to provide whatever behavior you want.
2924 */
2925 public void onExtractedTextClicked() {
2926 if (mExtractEditText == null) {
2927 return;
2928 }
2929 if (mExtractEditText.hasVerticalScrollBar()) {
2930 setCandidatesViewShown(false);
2931 }
2932 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002933
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002934 /**
2935 * This is called when the user has performed a cursor movement in the
2936 * extracted text view, when it is running in fullscreen mode. The default
2937 * implementation hides the candidates view when a vertical movement
2938 * happens, but only if the extracted text editor has a vertical scroll bar
2939 * because its text doesn't fit.
2940 * Re-implement this to provide whatever behavior you want.
2941 * @param dx The amount of cursor movement in the x dimension.
2942 * @param dy The amount of cursor movement in the y dimension.
2943 */
2944 public void onExtractedCursorMovement(int dx, int dy) {
2945 if (mExtractEditText == null || dy == 0) {
2946 return;
2947 }
2948 if (mExtractEditText.hasVerticalScrollBar()) {
2949 setCandidatesViewShown(false);
2950 }
2951 }
2952
2953 /**
2954 * This is called when the user has selected a context menu item from the
2955 * extracted text view, when running in fullscreen mode. The default
2956 * implementation sends this action to the current InputConnection's
2957 * {@link InputConnection#performContextMenuAction(int)}, for it
2958 * to be processed in underlying "real" editor. Re-implement this to
2959 * provide whatever behavior you want.
2960 */
2961 public boolean onExtractTextContextMenuItem(int id) {
2962 InputConnection ic = getCurrentInputConnection();
2963 if (ic != null) {
2964 ic.performContextMenuAction(id);
2965 }
2966 return true;
2967 }
Elliot Waite54de7742017-01-11 15:30:35 -08002968
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002969 /**
2970 * Return text that can be used as a button label for the given
2971 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
2972 * if there is no action requested. Note that there is no guarantee that
2973 * the returned text will be relatively short, so you probably do not
2974 * want to use it as text on a soft keyboard key label.
Elliot Waite54de7742017-01-11 15:30:35 -08002975 *
2976 * @param imeOptions The value from {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
2977 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002978 * @return Returns a label to use, or null if there is no action.
2979 */
2980 public CharSequence getTextForImeAction(int imeOptions) {
2981 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2982 case EditorInfo.IME_ACTION_NONE:
2983 return null;
2984 case EditorInfo.IME_ACTION_GO:
2985 return getText(com.android.internal.R.string.ime_action_go);
2986 case EditorInfo.IME_ACTION_SEARCH:
2987 return getText(com.android.internal.R.string.ime_action_search);
2988 case EditorInfo.IME_ACTION_SEND:
2989 return getText(com.android.internal.R.string.ime_action_send);
2990 case EditorInfo.IME_ACTION_NEXT:
2991 return getText(com.android.internal.R.string.ime_action_next);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002992 case EditorInfo.IME_ACTION_DONE:
2993 return getText(com.android.internal.R.string.ime_action_done);
Dianne Hackborndea3ef72010-10-28 14:24:22 -07002994 case EditorInfo.IME_ACTION_PREVIOUS:
2995 return getText(com.android.internal.R.string.ime_action_previous);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002996 default:
2997 return getText(com.android.internal.R.string.ime_action_default);
2998 }
2999 }
Mark Renouf91eb2652016-04-11 16:03:26 -04003000
3001 /**
3002 * Return a drawable resource id that can be used as a button icon for the given
3003 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
3004 *
3005 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
3006 *
3007 * @return Returns a drawable resource id to use.
3008 */
3009 @DrawableRes
3010 private int getIconForImeAction(int imeOptions) {
3011 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
3012 case EditorInfo.IME_ACTION_GO:
3013 return com.android.internal.R.drawable.ic_input_extract_action_go;
3014 case EditorInfo.IME_ACTION_SEARCH:
3015 return com.android.internal.R.drawable.ic_input_extract_action_search;
3016 case EditorInfo.IME_ACTION_SEND:
3017 return com.android.internal.R.drawable.ic_input_extract_action_send;
3018 case EditorInfo.IME_ACTION_NEXT:
3019 return com.android.internal.R.drawable.ic_input_extract_action_next;
3020 case EditorInfo.IME_ACTION_DONE:
3021 return com.android.internal.R.drawable.ic_input_extract_action_done;
3022 case EditorInfo.IME_ACTION_PREVIOUS:
3023 return com.android.internal.R.drawable.ic_input_extract_action_previous;
3024 default:
3025 return com.android.internal.R.drawable.ic_input_extract_action_return;
3026 }
3027 }
3028
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003029 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07003030 * Called when the fullscreen-mode extracting editor info has changed,
3031 * to determine whether the extracting (extract text and candidates) portion
3032 * of the UI should be shown. The standard implementation hides or shows
3033 * the extract area depending on whether it makes sense for the
3034 * current editor. In particular, a {@link InputType#TYPE_NULL}
3035 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
3036 * turn off the extract area since there is no text to be shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003037 */
The Android Open Source Project10592532009-03-18 17:39:46 -07003038 public void onUpdateExtractingVisibility(EditorInfo ei) {
3039 if (ei.inputType == InputType.TYPE_NULL ||
3040 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
3041 // No reason to show extract UI!
3042 setExtractViewShown(false);
3043 return;
3044 }
3045
3046 setExtractViewShown(true);
3047 }
3048
3049 /**
3050 * Called when the fullscreen-mode extracting editor info has changed,
3051 * to update the state of its UI such as the action buttons shown.
3052 * You do not need to deal with this if you are using the standard
3053 * full screen extract UI. If replacing it, you will need to re-implement
3054 * this to put the appropriate action button in your own UI and handle it,
3055 * and perform any other changes.
3056 *
3057 * <p>The standard implementation turns on or off its accessory area
3058 * depending on whether there is an action button, and hides or shows
3059 * the entire extract area depending on whether it makes sense for the
3060 * current editor. In particular, a {@link InputType#TYPE_NULL} or
3061 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
3062 * extract area since there is no text to be shown.
3063 */
3064 public void onUpdateExtractingViews(EditorInfo ei) {
3065 if (!isExtractViewShown()) {
3066 return;
3067 }
3068
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003069 if (mExtractAccessories == null) {
3070 return;
3071 }
3072 final boolean hasAction = ei.actionLabel != null || (
3073 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
The Android Open Source Project10592532009-03-18 17:39:46 -07003074 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
3075 ei.inputType != InputType.TYPE_NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003076 if (hasAction) {
3077 mExtractAccessories.setVisibility(View.VISIBLE);
Steve Kondik59eb6912009-09-07 22:53:34 -04003078 if (mExtractAction != null) {
Mark Renouf91eb2652016-04-11 16:03:26 -04003079 if (mExtractAction instanceof ImageButton) {
3080 ((ImageButton) mExtractAction)
3081 .setImageResource(getIconForImeAction(ei.imeOptions));
3082 if (ei.actionLabel != null) {
3083 mExtractAction.setContentDescription(ei.actionLabel);
3084 } else {
3085 mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
3086 }
Steve Kondik59eb6912009-09-07 22:53:34 -04003087 } else {
Mark Renouf91eb2652016-04-11 16:03:26 -04003088 if (ei.actionLabel != null) {
3089 ((TextView) mExtractAction).setText(ei.actionLabel);
3090 } else {
3091 ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
3092 }
Steve Kondik59eb6912009-09-07 22:53:34 -04003093 }
3094 mExtractAction.setOnClickListener(mActionClickListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003095 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003096 } else {
3097 mExtractAccessories.setVisibility(View.GONE);
Steve Kondik59eb6912009-09-07 22:53:34 -04003098 if (mExtractAction != null) {
3099 mExtractAction.setOnClickListener(null);
3100 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003101 }
3102 }
3103
3104 /**
3105 * This is called when, while currently displayed in extract mode, the
3106 * current input target changes. The default implementation will
3107 * auto-hide the IME if the new target is not a full editor, since this
Ken Wakasaf76a50c2012-03-09 19:56:35 +09003108 * can be a confusing experience for the user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003109 */
3110 public void onExtractingInputChanged(EditorInfo ei) {
3111 if (ei.inputType == InputType.TYPE_NULL) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08003112 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003113 }
3114 }
3115
3116 void startExtractingText(boolean inputChanged) {
3117 final ExtractEditText eet = mExtractEditText;
3118 if (eet != null && getCurrentInputStarted()
3119 && isFullscreenMode()) {
3120 mExtractedToken++;
3121 ExtractedTextRequest req = new ExtractedTextRequest();
3122 req.token = mExtractedToken;
3123 req.flags = InputConnection.GET_TEXT_WITH_STYLES;
3124 req.hintMaxLines = 10;
3125 req.hintMaxChars = 10000;
Amith Yamasaniba4d93f2009-08-19 18:27:56 -07003126 InputConnection ic = getCurrentInputConnection();
3127 mExtractedText = ic == null? null
3128 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
Amith Yamasania8b00c82010-03-05 15:41:31 -08003129 if (mExtractedText == null || ic == null) {
3130 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
3131 + mExtractedText + ", input connection = " + ic);
3132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003133 final EditorInfo ei = getCurrentInputEditorInfo();
3134
3135 try {
3136 eet.startInternalChanges();
The Android Open Source Project10592532009-03-18 17:39:46 -07003137 onUpdateExtractingVisibility(ei);
3138 onUpdateExtractingViews(ei);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003139 int inputType = ei.inputType;
3140 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
3141 == EditorInfo.TYPE_CLASS_TEXT) {
3142 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
3143 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
3144 }
3145 }
3146 eet.setInputType(inputType);
3147 eet.setHint(ei.hintText);
3148 if (mExtractedText != null) {
3149 eet.setEnabled(true);
3150 eet.setExtractedText(mExtractedText);
3151 } else {
3152 eet.setEnabled(false);
3153 eet.setText("");
3154 }
3155 } finally {
3156 eet.finishInternalChanges();
3157 }
3158
3159 if (inputChanged) {
3160 onExtractingInputChanged(ei);
3161 }
3162 }
3163 }
satokab751aa2010-09-14 19:17:36 +09003164
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003165 private void dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
3166 synchronized (mLock) {
3167 mNotifyUserActionSent = false;
3168 }
3169 onCurrentInputMethodSubtypeChanged(newSubtype);
3170 }
3171
satokab751aa2010-09-14 19:17:36 +09003172 // TODO: Handle the subtype change event
3173 /**
3174 * Called when the subtype was changed.
3175 * @param newSubtype the subtype which is being changed to.
3176 */
3177 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
3178 if (DEBUG) {
3179 int nameResId = newSubtype.getNameResId();
satok9ef02832010-11-04 21:17:48 +09003180 String mode = newSubtype.getMode();
satokab751aa2010-09-14 19:17:36 +09003181 String output = "changeInputMethodSubtype:"
3182 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
satok9ef02832010-11-04 21:17:48 +09003183 + mode + ","
satokab751aa2010-09-14 19:17:36 +09003184 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
3185 Log.v(TAG, "--- " + output);
3186 }
3187 }
3188
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003189 /**
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003190 * Aimed to return the previous input method's {@link Insets#contentTopInsets}, but its actual
3191 * semantics has never been well defined.
3192 *
3193 * <p>Note that the previous document clearly mentioned that this method could return {@code 0}
3194 * at any time for whatever reason. Now this method is just always returning {@code 0}.</p>
3195 *
3196 * @return on Android {@link android.os.Build.VERSION_CODES#Q} and later devices this method
3197 * always returns {@code 0}
3198 * @deprecated the actual behavior of this method has never been well defined. You cannot use
3199 * this method in a reliable and predictable way
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003200 */
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003201 @Deprecated
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003202 public int getInputMethodWindowRecommendedHeight() {
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003203 Log.w(TAG, "getInputMethodWindowRecommendedHeight() is deprecated and now always returns 0."
3204 + " Do not use this method.");
3205 return 0;
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003206 }
3207
3208 /**
Yohei Yukawa25e08132016-06-22 16:31:41 -07003209 * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
3210 * permission to the content.
3211 *
Yohei Yukawa25e08132016-06-22 16:31:41 -07003212 * @param inputContentInfo Content to be temporarily exposed from the input method to the
3213 * application.
3214 * This cannot be {@code null}.
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003215 * @param inputConnection {@link InputConnection} with which
Yohei Yukawab2a0e052018-01-14 16:06:16 -08003216 * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} will be called.
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003217 * @hide
Yohei Yukawa25e08132016-06-22 16:31:41 -07003218 */
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003219 @Override
3220 public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
3221 @NonNull InputConnection inputConnection) {
3222 if (inputConnection == null) {
3223 return;
Yohei Yukawa25e08132016-06-22 16:31:41 -07003224 }
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003225 if (getCurrentInputConnection() != inputConnection) {
3226 return;
Yohei Yukawa25e08132016-06-22 16:31:41 -07003227 }
Yohei Yukawac54c1172018-09-06 11:39:50 -07003228 exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
3229 }
3230
3231 /**
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003232 * {@inheritDoc}
3233 * @hide
3234 */
3235 @AnyThread
3236 @Override
3237 public final void notifyUserActionIfNecessary() {
3238 synchronized (mLock) {
3239 if (mNotifyUserActionSent) {
3240 return;
3241 }
Yohei Yukawa9b60ba02019-01-21 00:06:27 -08003242 mPrivOps.notifyUserAction();
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003243 mNotifyUserActionSent = true;
3244 }
3245 }
3246
3247 /**
Yohei Yukawac54c1172018-09-06 11:39:50 -07003248 * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
3249 * permission to the content.
3250 *
3251 * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
3252 * InputConnection)} for details.</p>
3253 *
3254 * @param inputContentInfo Content to be temporarily exposed from the input method to the
3255 * application.
3256 * This cannot be {@code null}.
3257 * @param editorInfo The editor that receives {@link InputContentInfo}.
3258 */
3259 private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
3260 @NonNull EditorInfo editorInfo) {
Yohei Yukawac54c1172018-09-06 11:39:50 -07003261 final Uri contentUri = inputContentInfo.getContentUri();
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07003262 final IInputContentUriToken uriToken =
3263 mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
3264 if (uriToken == null) {
Yohei Yukawac54c1172018-09-06 11:39:50 -07003265 Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07003266 + " packageName=" + editorInfo.packageName);
Yohei Yukawac54c1172018-09-06 11:39:50 -07003267 return;
3268 }
3269 inputContentInfo.setUriToken(uriToken);
Yohei Yukawa25e08132016-06-22 16:31:41 -07003270 }
3271
Tarandeep Singheadb1392018-11-09 18:15:57 +01003272 private int mapToImeWindowStatus() {
3273 return IME_ACTIVE
3274 | (isInputViewShown()
3275 ? (mCanPreRender ? (mWindowVisible ? IME_VISIBLE : IME_INVISIBLE)
3276 : IME_VISIBLE) : 0);
Tarandeep Singh3fecef12018-01-22 14:33:33 -08003277 }
3278
JianYang Liu7eec3162020-01-23 17:09:26 -08003279 private boolean isAutomotive() {
3280 return getApplicationContext().getPackageManager().hasSystemFeature(
3281 PackageManager.FEATURE_AUTOMOTIVE);
3282 }
3283
Yohei Yukawa25e08132016-06-22 16:31:41 -07003284 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003285 * Performs a dump of the InputMethodService's internal state. Override
3286 * to add your own information to the dump.
3287 */
3288 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
3289 final Printer p = new PrintWriterPrinter(fout);
3290 p.println("Input method service state for " + this + ":");
Tarandeep Singheadb1392018-11-09 18:15:57 +01003291 p.println(" mViewsCreated=" + mViewsCreated);
3292 p.println(" mDecorViewVisible=" + mDecorViewVisible
3293 + " mDecorViewWasVisible=" + mDecorViewWasVisible
3294 + " mWindowVisible=" + mWindowVisible
The Android Open Source Project10592532009-03-18 17:39:46 -07003295 + " mInShowWindow=" + mInShowWindow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003296 p.println(" Configuration=" + getResources().getConfiguration());
3297 p.println(" mToken=" + mToken);
3298 p.println(" mInputBinding=" + mInputBinding);
3299 p.println(" mInputConnection=" + mInputConnection);
3300 p.println(" mStartedInputConnection=" + mStartedInputConnection);
3301 p.println(" mInputStarted=" + mInputStarted
3302 + " mInputViewStarted=" + mInputViewStarted
3303 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08003304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003305 if (mInputEditorInfo != null) {
3306 p.println(" mInputEditorInfo:");
3307 mInputEditorInfo.dump(p, " ");
3308 } else {
3309 p.println(" mInputEditorInfo: null");
3310 }
3311
3312 p.println(" mShowInputRequested=" + mShowInputRequested
3313 + " mLastShowInputRequested=" + mLastShowInputRequested
Tarandeep Singheadb1392018-11-09 18:15:57 +01003314 + " mCanPreRender=" + mCanPreRender
3315 + " mIsPreRendered=" + mIsPreRendered
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003316 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
3317 p.println(" mCandidatesVisibility=" + mCandidatesVisibility
3318 + " mFullscreenApplied=" + mFullscreenApplied
The Android Open Source Project10592532009-03-18 17:39:46 -07003319 + " mIsFullscreen=" + mIsFullscreen
3320 + " mExtractViewHidden=" + mExtractViewHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003321
3322 if (mExtractedText != null) {
3323 p.println(" mExtractedText:");
3324 p.println(" text=" + mExtractedText.text.length() + " chars"
3325 + " startOffset=" + mExtractedText.startOffset);
3326 p.println(" selectionStart=" + mExtractedText.selectionStart
3327 + " selectionEnd=" + mExtractedText.selectionEnd
3328 + " flags=0x" + Integer.toHexString(mExtractedText.flags));
3329 } else {
3330 p.println(" mExtractedText: null");
3331 }
3332 p.println(" mExtractedToken=" + mExtractedToken);
3333 p.println(" mIsInputViewShown=" + mIsInputViewShown
3334 + " mStatusIcon=" + mStatusIcon);
3335 p.println("Last computed insets:");
3336 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
3337 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
Jeff Brownfbf09772011-01-16 14:06:57 -08003338 + " touchableInsets=" + mTmpInsets.touchableInsets
3339 + " touchableRegion=" + mTmpInsets.touchableRegion);
Yohei Yukawa7b739a82015-12-21 13:30:44 -08003340 p.println(" mSettingsObserver=" + mSettingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003341 }
3342}