blob: 7e4d68d9925e66cec7c1cb146f0db4df80f6ae44 [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;
Adam Hebc67f2e2019-11-13 14:34:56 -080052import android.os.RemoteException;
The Android Open Source Project4df24232009-03-05 14:34:35 -080053import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.SystemClock;
55import android.provider.Settings;
56import android.text.InputType;
57import android.text.Layout;
58import android.text.Spannable;
59import android.text.method.MovementMethod;
60import android.util.Log;
61import android.util.PrintWriterPrinter;
62import android.util.Printer;
Andrii Kuliane57f2dc2020-01-26 20:59:07 -080063import android.util.Size;
Dianne Hackborne30e02f2014-05-27 18:24:45 -070064import android.view.Gravity;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080065import android.view.KeyCharacterMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.view.KeyEvent;
67import android.view.LayoutInflater;
68import android.view.MotionEvent;
69import android.view.View;
70import android.view.ViewGroup;
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070071import android.view.ViewRootImpl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import android.view.ViewTreeObserver;
73import android.view.Window;
Tiger Huang4a7835f2019-11-06 00:07:56 +080074import android.view.WindowInsets;
Tiger Huangc8364e32020-03-10 21:20:58 +080075import android.view.WindowInsets.Side;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import android.view.WindowManager;
The Android Open Source Project10592532009-03-18 17:39:46 -070077import android.view.animation.AnimationUtils;
Feng Caoe65a97c2020-02-28 11:39:56 -080078import android.view.autofill.AutofillId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079import android.view.inputmethod.CompletionInfo;
Yohei Yukawac2ddd602014-05-06 21:22:49 +090080import android.view.inputmethod.CursorAnchorInfo;
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080081import android.view.inputmethod.EditorInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082import android.view.inputmethod.ExtractedText;
83import android.view.inputmethod.ExtractedTextRequest;
Adam Hebc67f2e2019-11-13 14:34:56 -080084import android.view.inputmethod.InlineSuggestionsRequest;
85import android.view.inputmethod.InlineSuggestionsResponse;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086import android.view.inputmethod.InputBinding;
87import android.view.inputmethod.InputConnection;
Yohei Yukawa25e08132016-06-22 16:31:41 -070088import android.view.inputmethod.InputContentInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089import android.view.inputmethod.InputMethod;
90import android.view.inputmethod.InputMethodManager;
satokab751aa2010-09-14 19:17:36 +090091import android.view.inputmethod.InputMethodSubtype;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092import android.widget.FrameLayout;
Mark Renouf91eb2652016-04-11 16:03:26 -040093import android.widget.ImageButton;
The Android Open Source Project10592532009-03-18 17:39:46 -070094import android.widget.LinearLayout;
Mark Renouf91eb2652016-04-11 16:03:26 -040095import android.widget.TextView;
The Android Open Source Project10592532009-03-18 17:39:46 -070096
Yohei Yukawac07fd4c2018-09-11 11:37:13 -070097import com.android.internal.annotations.GuardedBy;
Yohei Yukawac54c1172018-09-06 11:39:50 -070098import com.android.internal.inputmethod.IInputContentUriToken;
99import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700100import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
Yohei Yukawad746a7e2018-09-18 18:55:02 -0700101import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
Adam Hebc67f2e2019-11-13 14:34:56 -0800102import com.android.internal.view.IInlineSuggestionsRequestCallback;
Feng Cao36960ee2020-02-18 18:23:30 -0800103import com.android.internal.view.InlineSuggestionsRequestInfo;
Yohei Yukawac54c1172018-09-06 11:39:50 -0700104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105import java.io.FileDescriptor;
106import java.io.PrintWriter;
Yohei Yukawa7b739a82015-12-21 13:30:44 -0800107import java.lang.annotation.Retention;
108import java.lang.annotation.RetentionPolicy;
Vinit Nayak31595bc2019-09-30 15:04:19 -0700109import java.util.Collections;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
111/**
112 * InputMethodService provides a standard implementation of an InputMethod,
113 * which final implementations can derive from and customize. See the
114 * base class {@link AbstractInputMethodService} and the {@link InputMethod}
115 * interface for more information on the basics of writing input methods.
116 *
117 * <p>In addition to the normal Service lifecycle methods, this class
118 * introduces some new specific callbacks that most subclasses will want
119 * to make use of:</p>
120 * <ul>
121 * <li> {@link #onInitializeInterface()} for user-interface initialization,
122 * in particular to deal with configuration changes while the service is
123 * running.
124 * <li> {@link #onBindInput} to find out about switching to a new client.
125 * <li> {@link #onStartInput} to deal with an input session starting with
126 * the client.
127 * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
128 * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
129 * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
130 * starting within the input area of the IME.
131 * </ul>
132 *
133 * <p>An input method has significant discretion in how it goes about its
134 * work: the {@link android.inputmethodservice.InputMethodService} provides
135 * a basic framework for standard UI elements (input view, candidates view,
136 * and running in fullscreen mode), but it is up to a particular implementor
137 * to decide how to use them. For example, one input method could implement
138 * an input area with a keyboard, another could allow the user to draw text,
139 * while a third could have no input area (and thus not be visible to the
140 * user) but instead listen to audio and perform text to speech conversion.</p>
141 *
142 * <p>In the implementation provided here, all of these elements are placed
143 * together in a single window managed by the InputMethodService. It will
144 * execute callbacks as it needs information about them, and provides APIs for
145 * programmatic control over them. They layout of these elements is explicitly
146 * defined:</p>
147 *
148 * <ul>
149 * <li>The soft input view, if available, is placed at the bottom of the
150 * screen.
151 * <li>The candidates view, if currently shown, is placed above the soft
152 * input view.
153 * <li>If not running fullscreen, the application is moved or resized to be
154 * above these views; if running fullscreen, the window will completely cover
155 * the application and its top part will contain the extract text of what is
156 * currently being edited by the application.
157 * </ul>
158 *
159 *
160 * <a name="SoftInputView"></a>
161 * <h3>Soft Input View</h3>
162 *
163 * <p>Central to most input methods is the soft input view. This is where most
164 * user interaction occurs: pressing on soft keys, drawing characters, or
165 * however else your input method wants to generate text. Most implementations
166 * will simply have their own view doing all of this work, and return a new
167 * instance of it when {@link #onCreateInputView()} is called. At that point,
168 * as long as the input view is visible, you will see user interaction in
169 * that view and can call back on the InputMethodService to interact with the
170 * application as appropriate.</p>
171 *
172 * <p>There are some situations where you want to decide whether or not your
173 * soft input view should be shown to the user. This is done by implementing
174 * the {@link #onEvaluateInputViewShown()} to return true or false based on
175 * whether it should be shown in the current environment. If any of your
176 * state has changed that may impact this, call
177 * {@link #updateInputViewShown()} to have it re-evaluated. The default
178 * implementation always shows the input view unless there is a hard
179 * keyboard available, which is the appropriate behavior for most input
180 * methods.</p>
181 *
182 *
183 * <a name="CandidatesView"></a>
184 * <h3>Candidates View</h3>
185 *
186 * <p>Often while the user is generating raw text, an input method wants to
187 * provide them with a list of possible interpretations of that text that can
188 * be selected for use. This is accomplished with the candidates view, and
189 * like the soft input view you implement {@link #onCreateCandidatesView()}
190 * to instantiate your own view implementing your candidates UI.</p>
191 *
192 * <p>Management of the candidates view is a little different than the input
193 * view, because the candidates view tends to be more transient, being shown
194 * only when there are possible candidates for the current text being entered
195 * by the user. To control whether the candidates view is shown, you use
196 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate
197 * view tends to be shown and hidden a lot, it does not impact the application
198 * UI in the same way as the soft input view: it will never cause application
199 * windows to resize, only cause them to be panned if needed for the user to
200 * see the current focus.</p>
201 *
202 *
203 * <a name="FullscreenMode"></a>
204 * <h3>Fullscreen Mode</h3>
205 *
206 * <p>Sometimes your input method UI is too large to integrate with the
207 * application UI, so you just want to take over the screen. This is
208 * accomplished by switching to full-screen mode, causing the input method
209 * window to fill the entire screen and add its own "extracted text" editor
210 * showing the user the text that is being typed. Unlike the other UI elements,
211 * there is a standard implementation for the extract editor that you should
212 * not need to change. The editor is placed at the top of the IME, above the
213 * input and candidates views.</p>
214 *
215 * <p>Similar to the input view, you control whether the IME is running in
216 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
217 * to return true or false based on
218 * whether it should be fullscreen in the current environment. If any of your
219 * state has changed that may impact this, call
220 * {@link #updateFullscreenMode()} to have it re-evaluated. The default
221 * implementation selects fullscreen mode when the screen is in a landscape
222 * orientation, which is appropriate behavior for most input methods that have
223 * a significant input area.</p>
224 *
225 * <p>When in fullscreen mode, you have some special requirements because the
226 * user can not see the application UI. In particular, you should implement
227 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
228 * generated by your application, typically in your candidates view like you
229 * would normally show candidates.
230 *
231 *
232 * <a name="GeneratingText"></a>
233 * <h3>Generating Text</h3>
234 *
235 * <p>The key part of an IME is of course generating text for the application.
236 * This is done through calls to the
237 * {@link android.view.inputmethod.InputConnection} interface to the
238 * application, which can be retrieved from {@link #getCurrentInputConnection()}.
239 * This interface allows you to generate raw key events or, if the target
240 * supports it, directly edit in strings of candidates and committed text.</p>
241 *
242 * <p>Information about what the target is expected and supports can be found
243 * through the {@link android.view.inputmethod.EditorInfo} class, which is
244 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most
245 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
246 * EditorInfo.inputType}; in particular, if this is
247 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
248 * then the target does not support complex edits and you need to only deliver
249 * raw key events to it. An input method will also want to look at other
250 * values here, to for example detect password mode, auto complete text views,
251 * phone number entry, etc.</p>
252 *
253 * <p>When the user switches between input targets, you will receive calls to
254 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
255 * You can use these to reset and initialize your input state for the current
256 * target. For example, you will often want to clear any input state, and
257 * update a soft keyboard to be appropriate for the new inputType.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -0700258 *
259 * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
260 * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
261 * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 */
263public class InputMethodService extends AbstractInputMethodService {
264 static final String TAG = "InputMethodService";
265 static final boolean DEBUG = false;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800266
267 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700268 * Allows the system to optimize the back button affordance based on the presence of software
269 * keyboard.
270 *
271 * <p>For instance, on devices that have navigation bar and software-rendered back button, the
272 * system may use a different icon while {@link #isInputViewShown()} returns {@code true}, to
273 * indicate that the back button has "dismiss" affordance.</p>
274 *
275 * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
276 * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
277 * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
278 * not take this mode into account.</p>
279 *
280 * <p>For API level {@link android.os.Build.VERSION_CODES#O_MR1} and lower devices, this is the
281 * only mode you can safely specify without worrying about the compatibility.</p>
282 *
283 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800284 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700285 public static final int BACK_DISPOSITION_DEFAULT = 0;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800286
287 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700288 * Deprecated flag.
289 *
290 * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
291 *
292 * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
293 * handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
294 * {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
295 * of this mode had not been well defined. Most likely the end result would be the
296 * same as {@link #BACK_DISPOSITION_DEFAULT}. Either way it is not recommended to
297 * use this mode
298 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800299 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700300 @Deprecated
301 public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800302
303 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700304 * Deprecated flag.
305 *
306 * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
307 *
308 * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
309 * handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
310 * {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
311 * of this mode had not been well defined. In AOSP implementation running on devices
312 * that have navigation bar, specifying this flag could change the software back
313 * button to "Dismiss" icon no matter whether the software keyboard is shown or not,
314 * but there would be no easy way to restore the icon state even after IME lost the
315 * connection to the application. To avoid user confusions, do not specify this mode
316 * anyway
317 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800318 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700319 @Deprecated
320 public static final int BACK_DISPOSITION_WILL_DISMISS = 2;
321
322 /**
323 * Asks the system to not adjust the back button affordance even when the software keyboard is
324 * shown.
325 *
326 * <p>This mode is useful for UI modes where IME's main soft input window is used for some
327 * supplemental UI, such as floating candidate window for languages such as Chinese and
328 * Japanese, where users expect the back button is, or at least looks to be, handled by the
329 * target application rather than the UI shown by the IME even while {@link #isInputViewShown()}
330 * returns {@code true}.</p>
331 *
332 * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
333 * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
334 * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
335 * not take this mode into account.</p>
336 *
337 * @see #setBackDisposition(int)
338 */
339 public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
340
341 /**
342 * Enum flag to be used for {@link #setBackDisposition(int)}.
343 *
344 * @hide
345 */
346 @Retention(SOURCE)
347 @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
348 BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
349 prefix = "BACK_DISPOSITION_")
350 public @interface BackDispositionMode {}
Joe Onorato857fd9b2011-01-27 15:08:35 -0800351
352 /**
353 * @hide
354 * The IME is active. It may or may not be visible.
355 */
356 public static final int IME_ACTIVE = 0x1;
357
358 /**
359 * @hide
360 * The IME is visible.
361 */
362 public static final int IME_VISIBLE = 0x2;
363
Tarandeep Singheadb1392018-11-09 18:15:57 +0100364 /**
365 * @hide
366 * The IME is active and ready with views but set invisible.
367 * This flag cannot be combined with {@link #IME_VISIBLE}.
368 */
369 public static final int IME_INVISIBLE = 0x4;
370
Tarandeep Singh3fecef12018-01-22 14:33:33 -0800371 // Min and max values for back disposition.
372 private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700373 private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
Tarandeep Singh3fecef12018-01-22 14:33:33 -0800374
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 InputMethodManager mImm;
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700376 private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
Yohei Yukawac54c1172018-09-06 11:39:50 -0700377
Mathew Inwood31755f92018-12-20 13:53:36 +0000378 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Dianne Hackbornd922ae02011-01-14 11:43:24 -0800379 int mTheme = 0;
The Android Open Source Project10592532009-03-18 17:39:46 -0700380
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 LayoutInflater mInflater;
The Android Open Source Project10592532009-03-18 17:39:46 -0700382 TypedArray mThemeAttrs;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100383 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 View mRootView;
385 SoftInputWindow mWindow;
386 boolean mInitialized;
Tarandeep Singheadb1392018-11-09 18:15:57 +0100387 boolean mViewsCreated;
388 // IME views visibility.
389 boolean mDecorViewVisible;
390 boolean mDecorViewWasVisible;
The Android Open Source Project10592532009-03-18 17:39:46 -0700391 boolean mInShowWindow;
Tarandeep Singheadb1392018-11-09 18:15:57 +0100392 // True if pre-rendering of IME views/window is supported.
393 boolean mCanPreRender;
394 // If IME is pre-rendered.
395 boolean mIsPreRendered;
396 // IME window visibility.
397 // Use (mDecorViewVisible && mWindowVisible) to check if IME is visible to the user.
398 boolean mWindowVisible;
399
The Android Open Source Project10592532009-03-18 17:39:46 -0700400 ViewGroup mFullscreenArea;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 FrameLayout mExtractFrame;
402 FrameLayout mCandidatesFrame;
403 FrameLayout mInputFrame;
404
405 IBinder mToken;
406
407 InputBinding mInputBinding;
408 InputConnection mInputConnection;
409 boolean mInputStarted;
410 boolean mInputViewStarted;
411 boolean mCandidatesViewStarted;
412 InputConnection mStartedInputConnection;
413 EditorInfo mInputEditorInfo;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 int mShowInputFlags;
416 boolean mShowInputRequested;
417 boolean mLastShowInputRequested;
418 int mCandidatesVisibility;
419 CompletionInfo[] mCurCompletions;
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 boolean mFullscreenApplied;
422 boolean mIsFullscreen;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100423 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 View mExtractView;
The Android Open Source Project10592532009-03-18 17:39:46 -0700425 boolean mExtractViewHidden;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100426 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 ExtractEditText mExtractEditText;
428 ViewGroup mExtractAccessories;
Mark Renouf91eb2652016-04-11 16:03:26 -0400429 View mExtractAction;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 ExtractedText mExtractedText;
431 int mExtractedToken;
432
433 View mInputView;
434 boolean mIsInputViewShown;
435
436 int mStatusIcon;
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700437
438 @BackDispositionMode
Joe Onorato857fd9b2011-01-27 15:08:35 -0800439 int mBackDisposition;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440
Yohei Yukawac07fd4c2018-09-11 11:37:13 -0700441 private Object mLock = new Object();
442 @GuardedBy("mLock")
443 private boolean mNotifyUserActionSent;
444
Mathew Inwood31755f92018-12-20 13:53:36 +0000445 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 final Insets mTmpInsets = new Insets();
447 final int[] mTmpLocation = new int[2];
satokab751aa2010-09-14 19:17:36 +0900448
Feng Cao7c85eb72020-02-28 11:39:56 -0800449 /**
450 * We use a separate {@code mInlineLock} to make sure {@code mInlineSuggestionSession} is
451 * only accessed synchronously. Although when the lock is introduced, all the calls are from
452 * the main thread so the lock is not really necessarily (but for the same reason it also
453 * doesn't hurt), it's still being added as a safety guard to make sure in the future we
454 * don't add more code causing race condition when updating the {@code
455 * mInlineSuggestionSession}.
456 */
457 private final Object mInlineLock = new Object();
458 @GuardedBy("mInlineLock")
Adam Hebc67f2e2019-11-13 14:34:56 -0800459 @Nullable
Feng Caoabd6b072020-02-12 19:08:44 -0800460 private InlineSuggestionSession mInlineSuggestionSession;
Adam Hebc67f2e2019-11-13 14:34:56 -0800461
JianYang Liu7eec3162020-01-23 17:09:26 -0800462 private boolean mAutomotiveHideNavBarForKeyboard;
463 private boolean mIsAutomotive;
464
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800465 /**
466 * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
467 * The original app window token is passed from client app window.
468 * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy
469 * token to identify this window.
470 * This dummy token is only valid for a single call to {@link InputMethodImpl#showSoftInput},
471 * after which it is set null until next call.
472 */
473 private IBinder mCurShowInputToken;
474
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800475 /**
476 * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#hideSoftInput}
477 * The original app window token is passed from client app window.
478 * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy
479 * token to identify this window.
480 * This dummy token is only valid for a single call to {@link InputMethodImpl#hideSoftInput},
481 * after which it is set {@code null} until next call.
482 */
483 private IBinder mCurHideInputToken;
484
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800485 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
Vinit Nayak31595bc2019-09-30 15:04:19 -0700486 onComputeInsets(mTmpInsets);
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800487 if (isExtractViewShown()) {
488 // In true fullscreen mode, we just say the window isn't covering
489 // any content so we don't impact whatever is behind.
490 View decor = getWindow().getWindow().getDecorView();
491 info.contentInsets.top = info.visibleInsets.top = decor.getHeight();
492 info.touchableRegion.setEmpty();
493 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
494 } else {
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800495 info.contentInsets.top = mTmpInsets.contentTopInsets;
496 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
497 info.touchableRegion.set(mTmpInsets.touchableRegion);
498 info.setTouchableInsets(mTmpInsets.touchableInsets);
499 }
Vinit Nayak31595bc2019-09-30 15:04:19 -0700500
501 if (mInputFrame != null) {
502 setImeExclusionRect(mTmpInsets.visibleTopInsets);
503 }
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800504 };
505
506 final View.OnClickListener mActionClickListener = v -> {
507 final EditorInfo ei = getCurrentInputEditorInfo();
508 final InputConnection ic = getCurrentInputConnection();
509 if (ei != null && ic != null) {
510 if (ei.actionId != 0) {
511 ic.performEditorAction(ei.actionId);
512 } else if ((ei.imeOptions & EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE) {
513 ic.performEditorAction(ei.imeOptions & EditorInfo.IME_MASK_ACTION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 }
515 }
516 };
517
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 /**
519 * Concrete implementation of
520 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
521 * all of the standard behavior for an input method.
522 */
523 public class InputMethodImpl extends AbstractInputMethodImpl {
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800524
525 private boolean mSystemCallingShowSoftInput;
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800526 private boolean mSystemCallingHideSoftInput;
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800527
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700529 * {@inheritDoc}
Yohei Yukawac54c1172018-09-06 11:39:50 -0700530 * @hide
531 */
532 @MainThread
533 @Override
lumarke0af3942019-05-29 17:11:57 +0800534 public final void initializeInternal(@NonNull IBinder token, int displayId,
Yohei Yukawac54c1172018-09-06 11:39:50 -0700535 IInputMethodPrivilegedOperations privilegedOperations) {
lumarke0af3942019-05-29 17:11:57 +0800536 if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
537 Log.w(TAG, "The token has already registered, ignore this initialization.");
538 return;
539 }
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700540 mPrivOps.set(privilegedOperations);
Yohei Yukawad746a7e2018-09-18 18:55:02 -0700541 InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
lumark90120a82018-08-15 00:33:03 +0800542 updateInputMethodDisplay(displayId);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700543 attachToken(token);
544 }
545
546 /**
547 * {@inheritDoc}
Adam Hebc67f2e2019-11-13 14:34:56 -0800548 * @hide
549 */
550 @MainThread
551 @Override
Feng Cao36960ee2020-02-18 18:23:30 -0800552 public void onCreateInlineSuggestionsRequest(
553 @NonNull InlineSuggestionsRequestInfo requestInfo,
554 @NonNull IInlineSuggestionsRequestCallback cb) {
Joanne Chungd9a916f2020-01-02 19:03:16 +0800555 if (DEBUG) {
556 Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
557 }
Feng Cao36960ee2020-02-18 18:23:30 -0800558 handleOnCreateInlineSuggestionsRequest(requestInfo, cb);
Adam Hebc67f2e2019-11-13 14:34:56 -0800559 }
560
561 /**
562 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700564 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700565 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 public void attachToken(IBinder token) {
Yohei Yukawa674cc4b2018-09-06 10:47:15 -0700567 if (mToken != null) {
568 throw new IllegalStateException(
569 "attachToken() must be called at most once. token=" + token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 }
Yohei Yukawa674cc4b2018-09-06 10:47:15 -0700571 mToken = token;
572 mWindow.setToken(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 }
Tarandeep Singhd8d03a82017-11-28 13:35:32 -0800574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700576 * {@inheritDoc}
lumark90120a82018-08-15 00:33:03 +0800577 * @hide
578 */
579 @MainThread
580 @Override
581 public void updateInputMethodDisplay(int displayId) {
582 // Update display for adding IME window to the right display.
Andrii Kuliane57f2dc2020-01-26 20:59:07 -0800583 // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
584 // for update resources & configuration correctly when show soft input
585 // in non-default display.
586 updateDisplay(displayId);
lumark90120a82018-08-15 00:33:03 +0800587 }
588
589 /**
590 * {@inheritDoc}
Yohei Yukawa16f04072017-10-18 20:19:43 -0700591 *
592 * <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700594 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700595 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 public void bindInput(InputBinding binding) {
597 mInputBinding = binding;
598 mInputConnection = binding.getConnection();
599 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
600 + " ic=" + mInputConnection);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700601 reportFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 initialize();
603 onBindInput();
604 }
605
606 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700607 * {@inheritDoc}
608 *
609 * <p>Calls {@link InputMethodService#onUnbindInput()} when done.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700611 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700612 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 public void unbindInput() {
614 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
615 + " ic=" + mInputConnection);
Tarandeep Singh0fe4f782020-03-05 13:37:53 -0800616 // Unbind input is per process per display.
617 // TODO(b/150902448): free-up IME surface when target is changing.
618 // e.g. DisplayContent#setInputMethodTarget()
619 removeImeSurface();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 onUnbindInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 mInputBinding = null;
622 mInputConnection = null;
623 }
624
Yohei Yukawa16f04072017-10-18 20:19:43 -0700625 /**
626 * {@inheritDoc}
627 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700628 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700629 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 public void startInput(InputConnection ic, EditorInfo attribute) {
631 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
632 doStartInput(ic, attribute, false);
633 }
634
Yohei Yukawa16f04072017-10-18 20:19:43 -0700635 /**
636 * {@inheritDoc}
637 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700638 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700639 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 public void restartInput(InputConnection ic, EditorInfo attribute) {
641 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
642 doStartInput(ic, attribute, true);
643 }
644
645 /**
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800646 * {@inheritDoc}
647 * @hide
648 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700649 @MainThread
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800650 @Override
Tarandeep Singheadb1392018-11-09 18:15:57 +0100651 public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800652 @NonNull EditorInfo editorInfo, boolean restarting,
Tarandeep Singheadb1392018-11-09 18:15:57 +0100653 @NonNull IBinder startInputToken, boolean shouldPreRenderIme) {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700654 mPrivOps.reportStartInput(startInputToken);
Tarandeep Singheadb1392018-11-09 18:15:57 +0100655 mCanPreRender = shouldPreRenderIme;
656 if (DEBUG) Log.v(TAG, "Will Pre-render IME: " + mCanPreRender);
657
658 if (restarting) {
659 restartInput(inputConnection, editorInfo);
660 } else {
661 startInput(inputConnection, editorInfo);
662 }
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800663 }
664
665 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700666 * {@inheritDoc}
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800667 * @hide
668 */
669 @MainThread
670 @Override
671 public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
672 IBinder hideInputToken) {
673 mSystemCallingHideSoftInput = true;
674 mCurHideInputToken = hideInputToken;
675 hideSoftInput(flags, resultReceiver);
676 mCurHideInputToken = null;
677 mSystemCallingHideSoftInput = false;
678 }
679
680 /**
681 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700683 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700684 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -0800685 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 if (DEBUG) Log.v(TAG, "hideSoftInput()");
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800687 if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
688 && !mSystemCallingHideSoftInput) {
689 Log.e(TAG, "IME shouldn't call hideSoftInput on itself."
690 + " Use requestHideSelf(int) itself");
691 return;
692 }
Tarandeep Singheadb1392018-11-09 18:15:57 +0100693 final boolean wasVisible = mIsPreRendered
694 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700695 applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */);
Tarandeep Singheadb1392018-11-09 18:15:57 +0100696 if (mIsPreRendered) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100697 if (DEBUG) {
698 Log.v(TAG, "Making IME window invisible");
699 }
700 setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
701 onPreRenderedWindowVisibilityChanged(false /* setVisible */);
702 } else {
703 mShowInputFlags = 0;
704 mShowInputRequested = false;
705 doHideWindow();
706 }
707 final boolean isVisible = mIsPreRendered
708 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
709 final boolean visibilityChanged = isVisible != wasVisible;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800710 if (resultReceiver != null) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100711 resultReceiver.send(visibilityChanged
The Android Open Source Project4df24232009-03-05 14:34:35 -0800712 ? InputMethodManager.RESULT_HIDDEN
Tarandeep Singheadb1392018-11-09 18:15:57 +0100713 : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
The Android Open Source Project4df24232009-03-05 14:34:35 -0800714 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
715 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 }
717
718 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700719 * {@inheritDoc}
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800720 * @hide
721 */
722 @MainThread
723 @Override
724 public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
725 IBinder showInputToken) {
726 mSystemCallingShowSoftInput = true;
727 mCurShowInputToken = showInputToken;
728 showSoftInput(flags, resultReceiver);
729 mCurShowInputToken = null;
730 mSystemCallingShowSoftInput = false;
731 }
732
733 /**
734 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700736 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700737 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -0800738 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700739 if (DEBUG) Log.v(TAG, "showSoftInput()");
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800740 // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
741 if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
742 && !mSystemCallingShowSoftInput) {
743 Log.e(TAG," IME shouldn't call showSoftInput on itself."
744 + " Use requestShowSelf(int) itself");
745 return;
746 }
Tarandeep Singheadb1392018-11-09 18:15:57 +0100747 final boolean wasVisible = mIsPreRendered
748 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700749 if (dispatchOnShowInputRequested(flags, false)) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100750 if (mIsPreRendered) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100751 if (DEBUG) {
752 Log.v(TAG, "Making IME window visible");
753 }
754 onPreRenderedWindowVisibilityChanged(true /* setVisible */);
755 } else {
756 showWindow(true);
757 }
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700758 applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 }
satok865b9772011-01-21 02:45:06 +0900760 // If user uses hard keyboard, IME button should always be shown.
Tarandeep Singheadb1392018-11-09 18:15:57 +0100761 setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
762 final boolean isVisible = mIsPreRendered
763 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
764 final boolean visibilityChanged = isVisible != wasVisible;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800765 if (resultReceiver != null) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100766 resultReceiver.send(visibilityChanged
The Android Open Source Project4df24232009-03-05 14:34:35 -0800767 ? InputMethodManager.RESULT_SHOWN
Tarandeep Singheadb1392018-11-09 18:15:57 +0100768 : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
The Android Open Source Project4df24232009-03-05 14:34:35 -0800769 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
770 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
satokab751aa2010-09-14 19:17:36 +0900772
Yohei Yukawa16f04072017-10-18 20:19:43 -0700773 /**
774 * {@inheritDoc}
775 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700776 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700777 @Override
satokab751aa2010-09-14 19:17:36 +0900778 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
Yohei Yukawac07fd4c2018-09-11 11:37:13 -0700779 dispatchOnCurrentInputMethodSubtypeChanged(subtype);
satokab751aa2010-09-14 19:17:36 +0900780 }
Tarandeep Singhbb0e2f72020-01-15 13:58:29 -0800781
782 /**
783 * {@inheritDoc}
784 * @hide
785 */
786 @Override
787 public void setCurrentShowInputToken(IBinder showInputToken) {
788 mCurShowInputToken = showInputToken;
789 }
Tarandeep Singh4fe5b652020-02-20 17:20:19 -0800790
791 /**
792 * {@inheritDoc}
793 * @hide
794 */
795 @Override
796 public void setCurrentHideInputToken(IBinder hideInputToken) {
797 mCurHideInputToken = hideInputToken;
798 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 }
satokab751aa2010-09-14 19:17:36 +0900800
Adam Hebc67f2e2019-11-13 14:34:56 -0800801 // TODO(b/137800469): Add detailed docs explaining the inline suggestions process.
802 /**
Feng Cao36960ee2020-02-18 18:23:30 -0800803 * This method should be implemented by subclass which supports displaying autofill inline
804 * suggestion.
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
Feng Caoabd6b072020-02-12 19:08:44 -0800827 @MainThread
Feng Cao36960ee2020-02-18 18:23:30 -0800828 private void handleOnCreateInlineSuggestionsRequest(
829 @NonNull InlineSuggestionsRequestInfo requestInfo,
Feng Caoabd6b072020-02-12 19:08:44 -0800830 @NonNull IInlineSuggestionsRequestCallback callback) {
Adam Hebc67f2e2019-11-13 14:34:56 -0800831 if (!mInputStarted) {
Joanne Chungd9a916f2020-01-02 19:03:16 +0800832 try {
833 Log.w(TAG, "onStartInput() not called yet");
834 callback.onInlineSuggestionsUnsupported();
835 } catch (RemoteException e) {
836 Log.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
837 }
Adam Hebc67f2e2019-11-13 14:34:56 -0800838 return;
839 }
840
Feng Cao7c85eb72020-02-28 11:39:56 -0800841 synchronized (mInlineLock) {
842 if (mInlineSuggestionSession != null) {
843 mInlineSuggestionSession.invalidateSession();
844 }
845 mInlineSuggestionSession = new InlineSuggestionSession(requestInfo.getComponentName(),
846 callback, this::getEditorInfoPackageName, this::getEditorInfoAutofillId,
847 () -> onCreateInlineSuggestionsRequest(requestInfo.getUiExtras()),
848 this::getHostInputToken, this::onInlineSuggestionsResponse, mInputViewStarted);
Feng Caoabd6b072020-02-12 19:08:44 -0800849 }
Adam Hebc67f2e2019-11-13 14:34:56 -0800850 }
851
Feng Caoabd6b072020-02-12 19:08:44 -0800852 @Nullable
853 private String getEditorInfoPackageName() {
854 if (mInputEditorInfo != null) {
855 return mInputEditorInfo.packageName;
Adam Hebc67f2e2019-11-13 14:34:56 -0800856 }
Feng Caoabd6b072020-02-12 19:08:44 -0800857 return null;
Adam Hebc67f2e2019-11-13 14:34:56 -0800858 }
859
Feng Caoe65a97c2020-02-28 11:39:56 -0800860 @Nullable
861 private AutofillId getEditorInfoAutofillId() {
862 if (mInputEditorInfo != null) {
863 return mInputEditorInfo.autofillId;
864 }
865 return null;
866 }
867
Adam He40116252020-02-04 14:55:39 -0800868 /**
869 * Returns the {@link IBinder} input token from the host view root.
870 */
871 @Nullable
872 private IBinder getHostInputToken() {
873 ViewRootImpl viewRoot = null;
874 if (mRootView != null) {
875 viewRoot = mRootView.getViewRootImpl();
876 }
877 return viewRoot == null ? null : viewRoot.getInputToken();
878 }
879
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800880 private void notifyImeHidden() {
881 setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
882 onPreRenderedWindowVisibilityChanged(false /* setVisible */);
883 }
884
Tarandeep Singh94c9a832020-02-03 14:55:30 -0800885 private void removeImeSurface() {
886 if (!mShowInputRequested && !mWindowVisible) {
887 // hiding a window removes its surface.
888 mWindow.hide();
889 }
890 }
891
Yohei Yukawac54c1172018-09-06 11:39:50 -0700892 private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700893 mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700894 }
895
Vinit Nayak31595bc2019-09-30 15:04:19 -0700896 /** Set region of the keyboard to be avoided from back gesture */
897 private void setImeExclusionRect(int visibleTopInsets) {
898 View inputFrameRootView = mInputFrame.getRootView();
899 Rect r = new Rect(0, visibleTopInsets, inputFrameRootView.getWidth(),
900 inputFrameRootView.getHeight());
901 inputFrameRootView.setSystemGestureExclusionRects(Collections.singletonList(r));
902 }
903
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 /**
905 * Concrete implementation of
906 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
907 * all of the standard behavior for an input method session.
908 */
909 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
910 public void finishInput() {
911 if (!isEnabled()) {
912 return;
913 }
914 if (DEBUG) Log.v(TAG, "finishInput() in " + this);
915 doFinishInput();
916 }
917
918 /**
919 * Call {@link InputMethodService#onDisplayCompletions
920 * InputMethodService.onDisplayCompletions()}.
921 */
922 public void displayCompletions(CompletionInfo[] completions) {
923 if (!isEnabled()) {
924 return;
925 }
926 mCurCompletions = completions;
927 onDisplayCompletions(completions);
928 }
929
930 /**
931 * Call {@link InputMethodService#onUpdateExtractedText
932 * InputMethodService.onUpdateExtractedText()}.
933 */
934 public void updateExtractedText(int token, ExtractedText text) {
935 if (!isEnabled()) {
936 return;
937 }
938 onUpdateExtractedText(token, text);
939 }
940
941 /**
942 * Call {@link InputMethodService#onUpdateSelection
943 * InputMethodService.onUpdateSelection()}.
944 */
945 public void updateSelection(int oldSelStart, int oldSelEnd,
946 int newSelStart, int newSelEnd,
947 int candidatesStart, int candidatesEnd) {
948 if (!isEnabled()) {
949 return;
950 }
951 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
952 newSelStart, newSelEnd, candidatesStart, candidatesEnd);
953 }
satok863fcd62011-06-21 17:38:02 +0900954
955 @Override
956 public void viewClicked(boolean focusChanged) {
957 if (!isEnabled()) {
958 return;
959 }
960 InputMethodService.this.onViewClicked(focusChanged);
961 }
962
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 /**
964 * Call {@link InputMethodService#onUpdateCursor
965 * InputMethodService.onUpdateCursor()}.
966 */
967 public void updateCursor(Rect newCursor) {
968 if (!isEnabled()) {
969 return;
970 }
971 InputMethodService.this.onUpdateCursor(newCursor);
972 }
973
974 /**
975 * Call {@link InputMethodService#onAppPrivateCommand
976 * InputMethodService.onAppPrivateCommand()}.
977 */
978 public void appPrivateCommand(String action, Bundle data) {
979 if (!isEnabled()) {
980 return;
981 }
982 InputMethodService.this.onAppPrivateCommand(action, data);
983 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800984
985 /**
986 *
987 */
988 public void toggleSoftInput(int showFlags, int hideFlags) {
989 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
990 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900991
992 /**
993 * Call {@link InputMethodService#onUpdateCursorAnchorInfo
994 * InputMethodService.onUpdateCursorAnchorInfo()}.
995 */
996 public void updateCursorAnchorInfo(CursorAnchorInfo info) {
997 if (!isEnabled()) {
998 return;
999 }
1000 InputMethodService.this.onUpdateCursorAnchorInfo(info);
1001 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -08001002
1003 /**
1004 * Notify IME that window is hidden.
1005 * @hide
1006 */
1007 public final void notifyImeHidden() {
1008 InputMethodService.this.notifyImeHidden();
1009 }
Tarandeep Singh94c9a832020-02-03 14:55:30 -08001010
1011 /**
1012 * Notify IME that surface can be now removed.
1013 * @hide
1014 */
1015 public final void removeImeSurface() {
1016 InputMethodService.this.removeImeSurface();
1017 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 }
1019
1020 /**
1021 * Information about where interesting parts of the input method UI appear.
1022 */
1023 public static final class Insets {
1024 /**
1025 * This is the top part of the UI that is the main content. It is
1026 * used to determine the basic space needed, to resize/pan the
1027 * application behind. It is assumed that this inset does not
1028 * change very much, since any change will cause a full resize/pan
1029 * of the application behind. This value is relative to the top edge
1030 * of the input method window.
1031 */
1032 public int contentTopInsets;
1033
1034 /**
1035 * This is the top part of the UI that is visibly covering the
1036 * application behind it. This provides finer-grained control over
1037 * visibility, allowing you to change it relatively frequently (such
1038 * as hiding or showing candidates) without disrupting the underlying
1039 * UI too much. For example, this will never resize the application
1040 * UI, will only pan if needed to make the current focus visible, and
1041 * will not aggressively move the pan position when this changes unless
1042 * needed to make the focus visible. This value is relative to the top edge
1043 * of the input method window.
1044 */
1045 public int visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -08001046
1047 /**
1048 * This is the region of the UI that is touchable. It is used when
1049 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
1050 * The region should be specified relative to the origin of the window frame.
1051 */
1052 public final Region touchableRegion = new Region();
1053
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 /**
1055 * Option for {@link #touchableInsets}: the entire window frame
1056 * can be touched.
1057 */
1058 public static final int TOUCHABLE_INSETS_FRAME
1059 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
1060
1061 /**
1062 * Option for {@link #touchableInsets}: the area inside of
1063 * the content insets can be touched.
1064 */
1065 public static final int TOUCHABLE_INSETS_CONTENT
1066 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
1067
1068 /**
1069 * Option for {@link #touchableInsets}: the area inside of
1070 * the visible insets can be touched.
1071 */
1072 public static final int TOUCHABLE_INSETS_VISIBLE
1073 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001074
1075 /**
1076 * Option for {@link #touchableInsets}: the region specified by
1077 * {@link #touchableRegion} can be touched.
1078 */
1079 public static final int TOUCHABLE_INSETS_REGION
1080 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
1081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 /**
1083 * Determine which area of the window is touchable by the user. May
1084 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
Jeff Brownfbf09772011-01-16 14:06:57 -08001085 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
1086 * or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 */
1088 public int touchableInsets;
1089 }
satok865b9772011-01-21 02:45:06 +09001090
The Android Open Source Project10592532009-03-18 17:39:46 -07001091 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001092 * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
1093 *
1094 * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
1095 * Basically this functionality still needs to be considered as implementation details.</p>
1096 */
1097 @MainThread
1098 private static final class SettingsObserver extends ContentObserver {
1099 @Retention(RetentionPolicy.SOURCE)
1100 @IntDef({
1101 ShowImeWithHardKeyboardType.UNKNOWN,
1102 ShowImeWithHardKeyboardType.FALSE,
1103 ShowImeWithHardKeyboardType.TRUE,
1104 })
1105 private @interface ShowImeWithHardKeyboardType {
1106 int UNKNOWN = 0;
1107 int FALSE = 1;
1108 int TRUE = 2;
1109 }
1110 @ShowImeWithHardKeyboardType
1111 private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
1112
1113 private final InputMethodService mService;
1114
1115 private SettingsObserver(InputMethodService service) {
1116 super(new Handler(service.getMainLooper()));
1117 mService = service;
1118 }
1119
1120 /**
1121 * A factory method that internally enforces two-phase initialization to make sure that the
1122 * object reference will not be escaped until the object is properly constructed.
1123 *
1124 * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread. Hence
1125 * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
1126 *
1127 * @param service {@link InputMethodService} that needs to receive the callback.
1128 * @return {@link SettingsObserver} that is already registered to
1129 * {@link android.content.ContentResolver}. The caller must call
1130 * {@link SettingsObserver#unregister()}.
1131 */
1132 public static SettingsObserver createAndRegister(InputMethodService service) {
1133 final SettingsObserver observer = new SettingsObserver(service);
1134 // The observer is properly constructed. Let's start accepting the event.
1135 service.getContentResolver().registerContentObserver(
1136 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
1137 false, observer);
1138 return observer;
1139 }
1140
1141 void unregister() {
1142 mService.getContentResolver().unregisterContentObserver(this);
1143 }
1144
Mathew Inwood1dd7d112018-07-31 14:53:29 +01001145 @UnsupportedAppUsage
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001146 private boolean shouldShowImeWithHardKeyboard() {
1147 // Lazily initialize as needed.
1148 if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
1149 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
1150 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
1151 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
1152 }
1153 switch (mShowImeWithHardKeyboard) {
1154 case ShowImeWithHardKeyboardType.TRUE:
1155 return true;
1156 case ShowImeWithHardKeyboardType.FALSE:
1157 return false;
1158 default:
1159 Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
1160 return false;
1161 }
1162 }
1163
1164 @Override
1165 public void onChange(boolean selfChange, Uri uri) {
1166 final Uri showImeWithHardKeyboardUri =
1167 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
1168 if (showImeWithHardKeyboardUri.equals(uri)) {
1169 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
1170 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
1171 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001172 // In Android M and prior, state change of
1173 // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered
1174 // #onConfigurationChanged(). For compatibility reasons, we reset the internal
1175 // state as if configuration was changed.
1176 mService.resetStateForNewConfiguration();
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001177 }
1178 }
1179
1180 @Override
1181 public String toString() {
1182 return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}";
1183 }
1184 }
Mathew Inwood1dd7d112018-07-31 14:53:29 +01001185 @UnsupportedAppUsage
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001186 private SettingsObserver mSettingsObserver;
1187
1188 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001189 * You can call this to customize the theme used by your IME's window.
1190 * This theme should typically be one that derives from
1191 * {@link android.R.style#Theme_InputMethod}, which is the default theme
1192 * you will get. This must be set before {@link #onCreate}, so you
1193 * will typically call it in your constructor with the resource ID
1194 * of your custom theme.
1195 */
satokab751aa2010-09-14 19:17:36 +09001196 @Override
The Android Open Source Project10592532009-03-18 17:39:46 -07001197 public void setTheme(int theme) {
1198 if (mWindow != null) {
1199 throw new IllegalStateException("Must be called before onCreate()");
1200 }
1201 mTheme = theme;
1202 }
satok865b9772011-01-21 02:45:06 +09001203
Dianne Hackborn836531b2012-08-01 19:00:38 -07001204 /**
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001205 * You can call this to try to enable accelerated drawing for your IME. This must be set before
1206 * {@link #onCreate()}, so you will typically call it in your constructor. It is not always
1207 * possible to use hardware accelerated drawing in an IME (for example on low-end devices that
1208 * do not have the resources to support this), so the call {@code true} if it succeeds otherwise
1209 * {@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 -07001210 *
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001211 * <p>In API 21 and later, system may automatically enable hardware accelerated drawing for your
1212 * IME on capable devices even if this method is not explicitly called. Make sure that your IME
1213 * is able to handle either case.</p>
1214 *
1215 * @return {@code true} if accelerated drawing is successfully enabled otherwise {@code false}.
1216 * On API 21 and later devices the return value is basically just a hint and your IME
1217 * does not need to change the behavior based on the it
1218 * @deprecated Starting in API 21, hardware acceleration is always enabled on capable devices
Dianne Hackborn836531b2012-08-01 19:00:38 -07001219 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001220 @Deprecated
Dianne Hackborn836531b2012-08-01 19:00:38 -07001221 public boolean enableHardwareAcceleration() {
1222 if (mWindow != null) {
1223 throw new IllegalStateException("Must be called before onCreate()");
1224 }
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001225 return ActivityManager.isHighEndGfx();
Dianne Hackborn836531b2012-08-01 19:00:38 -07001226 }
1227
Alan Viverette5effd7e2014-05-05 12:25:33 -07001228 @Override public void onCreate() {
1229 mTheme = Resources.selectSystemTheme(mTheme,
1230 getApplicationInfo().targetSdkVersion,
1231 android.R.style.Theme_InputMethod,
1232 android.R.style.Theme_Holo_InputMethod,
1233 android.R.style.Theme_DeviceDefault_InputMethod,
1234 android.R.style.Theme_DeviceDefault_InputMethod);
The Android Open Source Project10592532009-03-18 17:39:46 -07001235 super.setTheme(mTheme);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 super.onCreate();
1237 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001238 mSettingsObserver = SettingsObserver.createAndRegister(this);
JianYang Liu7eec3162020-01-23 17:09:26 -08001239
1240 mIsAutomotive = isAutomotive();
1241 mAutomotiveHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
1242 com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
1243
lumark90120a82018-08-15 00:33:03 +08001244 // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
1245 // for update resources & configuration correctly when show soft input
1246 // in non-default display.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 mInflater = (LayoutInflater)getSystemService(
1248 Context.LAYOUT_INFLATER_SERVICE);
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001249 mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
Dianne Hackborne30e02f2014-05-27 18:24:45 -07001250 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
Tiger Huangc8364e32020-03-10 21:20:58 +08001251 mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
1252 mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
Jorim Jaggi0da8fd12020-01-10 01:23:21 +01001253
JianYang Liu7eec3162020-01-23 17:09:26 -08001254 // IME layout should always be inset by navigation bar, no matter its current visibility,
1255 // unless automotive requests it, since automotive may hide the navigation bar.
Tiger Huang4a7835f2019-11-06 00:07:56 +08001256 mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
1257 (v, insets) -> v.onApplyWindowInsets(
Jorim Jaggi0da8fd12020-01-10 01:23:21 +01001258 new WindowInsets.Builder(insets).setInsets(
Tiger Huang52724442020-01-20 21:38:42 +08001259 navigationBars(),
JianYang Liu7eec3162020-01-23 17:09:26 -08001260 mIsAutomotive && mAutomotiveHideNavBarForKeyboard
1261 ? android.graphics.Insets.NONE
1262 : insets.getInsetsIgnoringVisibility(navigationBars())
1263 )
Jorim Jaggi0da8fd12020-01-10 01:23:21 +01001264 .build()));
1265
Yohei Yukawa8f162c62018-01-10 13:18:09 -08001266 // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
1267 // by default (but IME developers can opt this out later if they want a new behavior).
1268 mWindow.getWindow().setFlags(
1269 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
1270
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 initViews();
Romain Guy980a9382010-01-08 15:06:28 -08001272 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 }
satokab751aa2010-09-14 19:17:36 +09001274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 /**
1276 * This is a hook that subclasses can use to perform initialization of
1277 * their interface. It is called for you prior to any of your UI objects
1278 * being created, both after the service is first created and after a
1279 * configuration change happens.
1280 */
1281 public void onInitializeInterface() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001282 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 }
satokab751aa2010-09-14 19:17:36 +09001284
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 void initialize() {
1286 if (!mInitialized) {
1287 mInitialized = true;
1288 onInitializeInterface();
1289 }
1290 }
satokab751aa2010-09-14 19:17:36 +09001291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 void initViews() {
1293 mInitialized = false;
Tarandeep Singheadb1392018-11-09 18:15:57 +01001294 mViewsCreated = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 mShowInputRequested = false;
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001296 mShowInputFlags = 0;
1297
The Android Open Source Project10592532009-03-18 17:39:46 -07001298 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 mRootView = mInflater.inflate(
1300 com.android.internal.R.layout.input_method, null);
1301 mWindow.setContentView(mRootView);
Seonggoo Kang72745ff2014-12-24 13:55:50 +09001302 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001303 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
Jeff Sharkey6e2bee72012-10-01 13:39:08 -07001304 if (Settings.Global.getInt(getContentResolver(),
1305 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 mWindow.getWindow().setWindowAnimations(
1307 com.android.internal.R.style.Animation_InputMethodFancy);
1308 }
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001309 mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
The Android Open Source Project10592532009-03-18 17:39:46 -07001310 mExtractViewHidden = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001311 mExtractFrame = mRootView.findViewById(android.R.id.extractArea);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 mExtractView = null;
1313 mExtractEditText = null;
1314 mExtractAccessories = null;
1315 mExtractAction = null;
1316 mFullscreenApplied = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001317
1318 mCandidatesFrame = mRootView.findViewById(android.R.id.candidatesArea);
1319 mInputFrame = mRootView.findViewById(android.R.id.inputArea);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320 mInputView = null;
1321 mIsInputViewShown = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 mExtractFrame.setVisibility(View.GONE);
1324 mCandidatesVisibility = getCandidatesHiddenVisibility();
1325 mCandidatesFrame.setVisibility(mCandidatesVisibility);
1326 mInputFrame.setVisibility(View.GONE);
1327 }
satokab751aa2010-09-14 19:17:36 +09001328
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 @Override public void onDestroy() {
1330 super.onDestroy();
1331 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
1332 mInsetsComputer);
Satoshi Kataokac56191f2013-05-30 13:14:47 +09001333 doFinishInput();
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07001334 mWindow.dismissForDestroyIfNecessary();
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001335 if (mSettingsObserver != null) {
1336 mSettingsObserver.unregister();
1337 mSettingsObserver = null;
1338 }
Yohei Yukawaeec552e2018-09-09 20:48:41 -07001339 if (mToken != null) {
1340 // This is completely optional, but allows us to show more explicit error messages
1341 // when IME developers are doing something unsupported.
Yohei Yukawad746a7e2018-09-18 18:55:02 -07001342 InputMethodPrivilegedOperationsRegistry.remove(mToken);
Yohei Yukawaeec552e2018-09-09 20:48:41 -07001343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 }
satokf17db9f2011-09-14 18:55:58 +09001345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 /**
1347 * Take care of handling configuration changes. Subclasses of
1348 * InputMethodService generally don't need to deal directly with
1349 * this on their own; the standard implementation here takes care of
1350 * regenerating the input method UI as a result of the configuration
1351 * change, so you can rely on your {@link #onCreateInputView} and
1352 * other methods being called as appropriate due to a configuration change.
1353 *
1354 * <p>When a configuration change does happen,
1355 * {@link #onInitializeInterface()} is guaranteed to be called the next
1356 * time prior to any of the other input or UI creation callbacks. The
1357 * following will be called immediately depending if appropriate for current
1358 * state: {@link #onStartInput} if input is active, and
1359 * {@link #onCreateInputView} and {@link #onStartInputView} and related
1360 * appropriate functions if the UI is displayed.
1361 */
1362 @Override public void onConfigurationChanged(Configuration newConfig) {
1363 super.onConfigurationChanged(newConfig);
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001364 resetStateForNewConfiguration();
1365 }
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001366
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001367 private void resetStateForNewConfiguration() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01001368 boolean visible = mDecorViewVisible;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 int showFlags = mShowInputFlags;
1370 boolean showingInput = mShowInputRequested;
1371 CompletionInfo[] completions = mCurCompletions;
1372 initViews();
1373 mInputViewStarted = false;
1374 mCandidatesViewStarted = false;
1375 if (mInputStarted) {
1376 doStartInput(getCurrentInputConnection(),
1377 getCurrentInputEditorInfo(), true);
1378 }
1379 if (visible) {
1380 if (showingInput) {
1381 // If we were last showing the soft keyboard, try to do so again.
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001382 if (dispatchOnShowInputRequested(showFlags, true)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 showWindow(true);
1384 if (completions != null) {
1385 mCurCompletions = completions;
1386 onDisplayCompletions(completions);
1387 }
1388 } else {
satok2f913d92012-05-10 01:48:03 +09001389 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 }
1391 } else if (mCandidatesVisibility == View.VISIBLE) {
1392 // If the candidates are currently visible, make sure the
1393 // window is shown for them.
1394 showWindow(false);
1395 } else {
1396 // Otherwise hide the window.
satok2f913d92012-05-10 01:48:03 +09001397 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 }
satok865b9772011-01-21 02:45:06 +09001399 // If user uses hard keyboard, IME button should always be shown.
Joe Onorato857fd9b2011-01-27 15:08:35 -08001400 boolean showing = onEvaluateInputViewShown();
Yohei Yukawac54c1172018-09-06 11:39:50 -07001401 setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402 }
1403 }
1404
1405 /**
1406 * Implement to return our standard {@link InputMethodImpl}. Subclasses
1407 * can override to provide their own customized version.
1408 */
satokab751aa2010-09-14 19:17:36 +09001409 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 public AbstractInputMethodImpl onCreateInputMethodInterface() {
1411 return new InputMethodImpl();
1412 }
1413
1414 /**
1415 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
1416 * can override to provide their own customized version.
1417 */
satokab751aa2010-09-14 19:17:36 +09001418 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
1420 return new InputMethodSessionImpl();
1421 }
1422
1423 public LayoutInflater getLayoutInflater() {
1424 return mInflater;
1425 }
1426
1427 public Dialog getWindow() {
1428 return mWindow;
1429 }
Yohei Yukawa386f50e2018-03-14 13:03:42 -07001430
1431 /**
1432 * Sets the disposition mode that indicates the expected affordance for the back button.
1433 *
1434 * <p>Keep in mind that specifying this flag does not change the the default behavior of
1435 * {@link #onKeyDown(int, KeyEvent)}. It is IME developers' responsibility for making sure that
1436 * their custom implementation of {@link #onKeyDown(int, KeyEvent)} is consistent with the mode
1437 * specified to this API.</p>
1438 *
1439 * @see #getBackDisposition()
1440 * @param disposition disposition mode to be set
1441 */
1442 public void setBackDisposition(@BackDispositionMode int disposition) {
Tarandeep Singh3fecef12018-01-22 14:33:33 -08001443 if (disposition == mBackDisposition) {
1444 return;
1445 }
1446 if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
1447 Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
1448 return;
1449 }
Joe Onorato857fd9b2011-01-27 15:08:35 -08001450 mBackDisposition = disposition;
Tarandeep Singheadb1392018-11-09 18:15:57 +01001451 setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
Joe Onorato857fd9b2011-01-27 15:08:35 -08001452 }
1453
Yohei Yukawa386f50e2018-03-14 13:03:42 -07001454 /**
1455 * Retrieves the current disposition mode that indicates the expected back button affordance.
1456 *
1457 * @see #setBackDisposition(int)
1458 * @return currently selected disposition mode
1459 */
1460 @BackDispositionMode
Joe Onorato857fd9b2011-01-27 15:08:35 -08001461 public int getBackDisposition() {
1462 return mBackDisposition;
1463 }
1464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001465 /**
1466 * Return the maximum width, in pixels, available the input method.
1467 * Input methods are positioned at the bottom of the screen and, unless
1468 * running in fullscreen, will generally want to be as short as possible
1469 * so should compute their height based on their contents. However, they
1470 * can stretch as much as needed horizontally. The function returns to
1471 * you the maximum amount of space available horizontally, which you can
1472 * use if needed for UI placement.
1473 *
1474 * <p>In many cases this is not needed, you can just rely on the normal
1475 * view layout mechanisms to position your views within the full horizontal
1476 * space given to the input method.
1477 *
1478 * <p>Note that this value can change dynamically, in particular when the
1479 * screen orientation changes.
1480 */
1481 public int getMaxWidth() {
Andrii Kuliane57f2dc2020-01-26 20:59:07 -08001482 final WindowManager windowManager = getSystemService(WindowManager.class);
1483 final Size windowSize = windowManager.getCurrentWindowMetrics().getSize();
1484 return windowSize.getWidth();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 }
1486
1487 /**
1488 * Return the currently active InputBinding for the input method, or
1489 * null if there is none.
1490 */
1491 public InputBinding getCurrentInputBinding() {
1492 return mInputBinding;
1493 }
1494
1495 /**
1496 * Retrieve the currently active InputConnection that is bound to
1497 * the input method, or null if there is none.
1498 */
1499 public InputConnection getCurrentInputConnection() {
1500 InputConnection ic = mStartedInputConnection;
1501 if (ic != null) {
1502 return ic;
1503 }
1504 return mInputConnection;
1505 }
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001506
1507 /**
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001508 * Force switch to the last used input method and subtype. If the last input method didn't have
1509 * any subtypes, the framework will simply switch to the last input method with no subtype
1510 * specified.
1511 * @return true if the current input method and subtype was successfully switched to the last
1512 * used input method and subtype.
1513 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001514 public final boolean switchToPreviousInputMethod() {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001515 return mPrivOps.switchToPreviousInputMethod();
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001516 }
1517
1518 /**
1519 * Force switch to the next input method and subtype. If there is no IME enabled except
1520 * current IME and subtype, do nothing.
1521 * @param onlyCurrentIme if true, the framework will find the next subtype which
1522 * belongs to the current IME
1523 * @return true if the current input method and subtype was successfully switched to the next
1524 * input method and subtype.
1525 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001526 public final boolean switchToNextInputMethod(boolean onlyCurrentIme) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001527 return mPrivOps.switchToNextInputMethod(onlyCurrentIme);
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001528 }
1529
1530 /**
1531 * Returns true if the current IME needs to offer the users ways to switch to a next input
1532 * method (e.g. a globe key.).
1533 * When an IME sets supportsSwitchingToNextInputMethod and this method returns true,
1534 * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly.
1535 * <p> Note that the system determines the most appropriate next input method
1536 * and subtype in order to provide the consistent user experience in switching
1537 * between IMEs and subtypes.
1538 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001539 public final boolean shouldOfferSwitchingToNextInputMethod() {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001540 return mPrivOps.shouldOfferSwitchingToNextInputMethod();
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001541 }
1542
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 public boolean getCurrentInputStarted() {
1544 return mInputStarted;
1545 }
1546
1547 public EditorInfo getCurrentInputEditorInfo() {
1548 return mInputEditorInfo;
1549 }
Yohei Yukawac54c1172018-09-06 11:39:50 -07001550
1551 private void reportFullscreenMode() {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07001552 mPrivOps.reportFullscreenMode(mIsFullscreen);
Yohei Yukawac54c1172018-09-06 11:39:50 -07001553 }
1554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 /**
1556 * Re-evaluate whether the input method should be running in fullscreen
1557 * mode, and update its UI if this has changed since the last time it
1558 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to
1559 * determine whether it should currently run in fullscreen mode. You
1560 * can use {@link #isFullscreenMode()} to determine if the input method
1561 * is currently running in fullscreen mode.
1562 */
1563 public void updateFullscreenMode() {
1564 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
1565 boolean changed = mLastShowInputRequested != mShowInputRequested;
1566 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
1567 changed = true;
1568 mIsFullscreen = isFullscreen;
Yohei Yukawac54c1172018-09-06 11:39:50 -07001569 reportFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001570 mFullscreenApplied = true;
1571 initialize();
The Android Open Source Project10592532009-03-18 17:39:46 -07001572 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1573 mFullscreenArea.getLayoutParams();
1574 if (isFullscreen) {
1575 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
1576 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
1577 lp.height = 0;
1578 lp.weight = 1;
1579 } else {
1580 mFullscreenArea.setBackgroundDrawable(null);
1581 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
1582 lp.weight = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001584 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
1585 mFullscreenArea, lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 if (isFullscreen) {
1587 if (mExtractView == null) {
1588 View v = onCreateExtractTextView();
1589 if (v != null) {
1590 setExtractView(v);
1591 }
1592 }
1593 startExtractingText(false);
1594 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001595 updateExtractFrameVisibility();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 }
1597
1598 if (changed) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001599 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001600 mLastShowInputRequested = mShowInputRequested;
1601 }
1602 }
1603
1604 /**
1605 * Update the given window's parameters for the given mode. This is called
1606 * when the window is first displayed and each time the fullscreen or
1607 * candidates only mode changes.
1608 *
1609 * <p>The default implementation makes the layout for the window
Romain Guy980a9382010-01-08 15:06:28 -08001610 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
1611 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001612 *
1613 * @param win The input method's window.
1614 * @param isFullscreen If true, the window is running in fullscreen mode
1615 * and intended to cover the entire application display.
1616 * @param isCandidatesOnly If true, the window is only showing the
1617 * candidates view and none of the rest of its UI. This is mutually
1618 * exclusive with fullscreen mode.
1619 */
1620 public void onConfigureWindow(Window win, boolean isFullscreen,
1621 boolean isCandidatesOnly) {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001622 final int currentHeight = mWindow.getWindow().getAttributes().height;
1623 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
1624 if (mIsInputViewShown && currentHeight != newHeight) {
Yohei Yukawad0332832017-02-01 13:59:43 -08001625 if (DEBUG) {
1626 Log.w(TAG,"Window size has been changed. This may cause jankiness of resizing "
1627 + "window: " + currentHeight + " -> " + newHeight);
1628 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 }
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001630 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 }
1632
1633 /**
1634 * Return whether the input method is <em>currently</em> running in
1635 * fullscreen mode. This is the mode that was last determined and
1636 * applied by {@link #updateFullscreenMode()}.
1637 */
1638 public boolean isFullscreenMode() {
1639 return mIsFullscreen;
1640 }
1641
1642 /**
1643 * Override this to control when the input method should run in
1644 * fullscreen mode. The default implementation runs in fullsceen only
1645 * when the screen is in landscape mode. If you change what
1646 * this returns, you will need to call {@link #updateFullscreenMode()}
1647 * yourself whenever the returned value may have changed to have it
1648 * re-evaluated and applied.
1649 */
1650 public boolean onEvaluateFullscreenMode() {
1651 Configuration config = getResources().getConfiguration();
Leon Scroggins2edd6822010-01-12 11:36:13 -05001652 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
1653 return false;
1654 }
1655 if (mInputEditorInfo != null
1656 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
1657 return false;
1658 }
1659 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 }
Gilles Debunne34703b62011-09-08 11:16:25 -07001661
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001662 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001663 * Controls the visibility of the extracted text area. This only applies
1664 * when the input method is in fullscreen mode, and thus showing extracted
1665 * text. When false, the extracted text will not be shown, allowing some
1666 * of the application to be seen behind. This is normally set for you
1667 * by {@link #onUpdateExtractingVisibility}. This controls the visibility
1668 * of both the extracted text and candidate view; the latter since it is
1669 * not useful if there is no text to see.
1670 */
1671 public void setExtractViewShown(boolean shown) {
1672 if (mExtractViewHidden == shown) {
1673 mExtractViewHidden = !shown;
1674 updateExtractFrameVisibility();
1675 }
1676 }
1677
1678 /**
1679 * Return whether the fullscreen extract view is shown. This will only
1680 * return true if {@link #isFullscreenMode()} returns true, and in that
1681 * case its value depends on the last call to
1682 * {@link #setExtractViewShown(boolean)}. This effectively lets you
1683 * determine if the application window is entirely covered (when this
1684 * returns true) or if some part of it may be shown (if this returns
1685 * false, though if {@link #isFullscreenMode()} returns true in that case
1686 * then it is probably only a sliver of the application).
1687 */
1688 public boolean isExtractViewShown() {
1689 return mIsFullscreen && !mExtractViewHidden;
1690 }
1691
1692 void updateExtractFrameVisibility() {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001693 final int vis;
The Android Open Source Project10592532009-03-18 17:39:46 -07001694 if (isFullscreenMode()) {
1695 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001696 // "vis" should be applied for the extract frame as well in the fullscreen mode.
1697 mExtractFrame.setVisibility(vis);
The Android Open Source Project10592532009-03-18 17:39:46 -07001698 } else {
1699 vis = View.VISIBLE;
1700 mExtractFrame.setVisibility(View.GONE);
1701 }
1702 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
Tarandeep Singheadb1392018-11-09 18:15:57 +01001703 if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001704 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1705 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1706 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1707 0);
1708 if (animRes != 0) {
1709 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1710 this, animRes));
1711 }
1712 }
1713 mFullscreenArea.setVisibility(vis);
1714 }
1715
1716 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001717 * Compute the interesting insets into your UI. The default implementation
1718 * uses the top of the candidates frame for the visible insets, and the
1719 * top of the input frame for the content insets. The default touchable
1720 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1721 *
The Android Open Source Project10592532009-03-18 17:39:46 -07001722 * <p>Note that this method is not called when
1723 * {@link #isExtractViewShown} returns true, since
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724 * in that case the application is left as-is behind the input method and
1725 * not impacted by anything in its UI.
1726 *
1727 * @param outInsets Fill in with the current UI insets.
1728 */
1729 public void onComputeInsets(Insets outInsets) {
1730 int[] loc = mTmpLocation;
1731 if (mInputFrame.getVisibility() == View.VISIBLE) {
1732 mInputFrame.getLocationInWindow(loc);
1733 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001734 View decor = getWindow().getWindow().getDecorView();
1735 loc[1] = decor.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001736 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001737 if (isFullscreenMode()) {
1738 // In fullscreen mode, we never resize the underlying window.
1739 View decor = getWindow().getWindow().getDecorView();
1740 outInsets.contentTopInsets = decor.getHeight();
1741 } else {
1742 outInsets.contentTopInsets = loc[1];
1743 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001744 if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1745 mCandidatesFrame.getLocationInWindow(loc);
1746 }
1747 outInsets.visibleTopInsets = loc[1];
1748 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001749 outInsets.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 }
1751
1752 /**
1753 * Re-evaluate whether the soft input area should currently be shown, and
1754 * update its UI if this has changed since the last time it
1755 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to
1756 * determine whether the input view should currently be shown. You
1757 * can use {@link #isInputViewShown()} to determine if the input view
1758 * is currently shown.
1759 */
1760 public void updateInputViewShown() {
1761 boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
Tarandeep Singheadb1392018-11-09 18:15:57 +01001762 if (mIsInputViewShown != isShown && mDecorViewVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 mIsInputViewShown = isShown;
1764 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1765 if (mInputView == null) {
1766 initialize();
1767 View v = onCreateInputView();
1768 if (v != null) {
1769 setInputView(v);
1770 }
1771 }
1772 }
1773 }
1774
1775 /**
1776 * Returns true if we have been asked to show our input view.
1777 */
1778 public boolean isShowInputRequested() {
1779 return mShowInputRequested;
1780 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01001781
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 /**
1783 * Return whether the soft input view is <em>currently</em> shown to the
1784 * user. This is the state that was last determined and
1785 * applied by {@link #updateInputViewShown()}.
1786 */
1787 public boolean isInputViewShown() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01001788 return mCanPreRender ? mWindowVisible : mIsInputViewShown && mDecorViewVisible;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001790
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001791 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001792 * Override this to control when the soft input area should be shown to the user. The default
1793 * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
1794 * unless the user shows an intention to use software keyboard. If you change what this
1795 * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
1796 * value may have changed to have it re-evaluated and applied.
1797 *
1798 * <p>When you override this method, it is recommended to call
1799 * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
1800 * returned.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 */
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001802 @CallSuper
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001803 public boolean onEvaluateInputViewShown() {
Yohei Yukawacf8403b2016-01-12 11:54:58 -08001804 if (mSettingsObserver == null) {
1805 Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
1806 return false;
1807 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001808 if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
1809 return true;
1810 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811 Configuration config = getResources().getConfiguration();
1812 return config.keyboard == Configuration.KEYBOARD_NOKEYS
Ken Wakasa8710e762011-01-30 11:02:09 +09001813 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001815
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 /**
1817 * Controls the visibility of the candidates display area. By default
1818 * it is hidden.
1819 */
1820 public void setCandidatesViewShown(boolean shown) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001821 updateCandidatesVisibility(shown);
Tarandeep Singheadb1392018-11-09 18:15:57 +01001822 if (!mShowInputRequested && mDecorViewVisible != shown) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001823 // If we are being asked to show the candidates view while the app
1824 // has not asked for the input view to be shown, then we need
1825 // to update whether the window is shown.
1826 if (shown) {
1827 showWindow(false);
1828 } else {
satok2f913d92012-05-10 01:48:03 +09001829 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 }
1831 }
1832 }
1833
The Android Open Source Project10592532009-03-18 17:39:46 -07001834 void updateCandidatesVisibility(boolean shown) {
1835 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1836 if (mCandidatesVisibility != vis) {
1837 mCandidatesFrame.setVisibility(vis);
1838 mCandidatesVisibility = vis;
1839 }
1840 }
1841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 /**
1843 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1844 * or {@link View#GONE View.GONE}) of the candidates view when it is not
The Android Open Source Project10592532009-03-18 17:39:46 -07001845 * shown. The default implementation returns GONE when
1846 * {@link #isExtractViewShown} returns true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001847 * otherwise VISIBLE. Be careful if you change this to return GONE in
1848 * other situations -- if showing or hiding the candidates view causes
1849 * your window to resize, this can cause temporary drawing artifacts as
1850 * the resize takes place.
1851 */
1852 public int getCandidatesHiddenVisibility() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001853 return isExtractViewShown() ? View.GONE : View.INVISIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001855
Tor Norbye7b9c9122013-05-30 16:48:33 -07001856 public void showStatusIcon(@DrawableRes int iconResId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001857 mStatusIcon = iconResId;
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001858 mPrivOps.updateStatusIcon(getPackageName(), iconResId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001860
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001861 public void hideStatusIcon() {
1862 mStatusIcon = 0;
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001863 mPrivOps.updateStatusIcon(null, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001865
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 /**
1867 * Force switch to a new input method, as identified by <var>id</var>. This
1868 * input method will be destroyed, and the requested one started on the
1869 * current input field.
1870 *
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001871 * @param id Unique identifier of the new input method to start.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001872 */
1873 public void switchInputMethod(String id) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001874 mPrivOps.setInputMethod(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001876
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001877 /**
1878 * Force switch to a new input method, as identified by {@code id}. This
1879 * input method will be destroyed, and the requested one started on the
1880 * current input field.
1881 *
1882 * @param id Unique identifier of the new input method to start.
1883 * @param subtype The new subtype of the new input method to be switched to.
1884 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001885 public final void switchInputMethod(String id, InputMethodSubtype subtype) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001886 mPrivOps.setInputMethodAndSubtype(id, subtype);
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001887 }
1888
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001889 public void setExtractView(View view) {
1890 mExtractFrame.removeAllViews();
1891 mExtractFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001892 ViewGroup.LayoutParams.MATCH_PARENT,
1893 ViewGroup.LayoutParams.MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001894 mExtractView = view;
1895 if (view != null) {
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001896 mExtractEditText = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001897 com.android.internal.R.id.inputExtractEditText);
1898 mExtractEditText.setIME(this);
Mark Renouf91eb2652016-04-11 16:03:26 -04001899 mExtractAction = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001900 com.android.internal.R.id.inputExtractAction);
1901 if (mExtractAction != null) {
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001902 mExtractAccessories = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 com.android.internal.R.id.inputExtractAccessories);
1904 }
1905 startExtractingText(false);
1906 } else {
1907 mExtractEditText = null;
1908 mExtractAccessories = null;
1909 mExtractAction = null;
1910 }
1911 }
1912
1913 /**
1914 * Replaces the current candidates view with a new one. You only need to
1915 * call this when dynamically changing the view; normally, you should
1916 * implement {@link #onCreateCandidatesView()} and create your view when
1917 * first needed by the input method.
1918 */
1919 public void setCandidatesView(View view) {
1920 mCandidatesFrame.removeAllViews();
1921 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001922 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001923 ViewGroup.LayoutParams.WRAP_CONTENT));
1924 }
1925
1926 /**
1927 * Replaces the current input view with a new one. You only need to
1928 * call this when dynamically changing the view; normally, you should
1929 * implement {@link #onCreateInputView()} and create your view when
1930 * first needed by the input method.
1931 */
1932 public void setInputView(View view) {
1933 mInputFrame.removeAllViews();
1934 mInputFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001935 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001936 ViewGroup.LayoutParams.WRAP_CONTENT));
1937 mInputView = view;
1938 }
1939
1940 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 * Called by the framework to create the layout for showing extacted text.
1942 * Only called when in fullscreen mode. The returned view hierarchy must
1943 * have an {@link ExtractEditText} whose ID is
1944 * {@link android.R.id#inputExtractEditText}.
1945 */
1946 public View onCreateExtractTextView() {
1947 return mInflater.inflate(
1948 com.android.internal.R.layout.input_method_extract_view, null);
1949 }
1950
1951 /**
1952 * Create and return the view hierarchy used to show candidates. This will
1953 * be called once, when the candidates are first displayed. You can return
1954 * null to have no candidates view; the default implementation returns null.
1955 *
1956 * <p>To control when the candidates view is displayed, use
1957 * {@link #setCandidatesViewShown(boolean)}.
1958 * To change the candidates view after the first one is created by this
1959 * function, use {@link #setCandidatesView(View)}.
1960 */
1961 public View onCreateCandidatesView() {
1962 return null;
1963 }
1964
1965 /**
1966 * Create and return the view hierarchy used for the input area (such as
1967 * a soft keyboard). This will be called once, when the input area is
1968 * first displayed. You can return null to have no input area; the default
1969 * implementation returns null.
1970 *
1971 * <p>To control when the input view is displayed, implement
1972 * {@link #onEvaluateInputViewShown()}.
1973 * To change the input view after the first one is created by this
1974 * function, use {@link #setInputView(View)}.
1975 */
1976 public View onCreateInputView() {
1977 return null;
1978 }
1979
1980 /**
1981 * Called when the input view is being shown and input has started on
1982 * a new editor. This will always be called after {@link #onStartInput},
1983 * allowing you to do your general setup there and just view-specific
1984 * setup here. You are guaranteed that {@link #onCreateInputView()} will
1985 * have been called some time before this function is called.
1986 *
1987 * @param info Description of the type of text being edited.
1988 * @param restarting Set to true if we are restarting input on the
1989 * same text field as before.
1990 */
1991 public void onStartInputView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001992 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001993 }
1994
1995 /**
1996 * Called when the input view is being hidden from the user. This will
1997 * be called either prior to hiding the window, or prior to switching to
1998 * another target for editing.
1999 *
2000 * <p>The default
2001 * implementation uses the InputConnection to clear any active composing
2002 * text; you can override this (not calling the base class implementation)
2003 * to perform whatever behavior you would like.
2004 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08002005 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 * called immediately after.
2007 */
2008 public void onFinishInputView(boolean finishingInput) {
2009 if (!finishingInput) {
2010 InputConnection ic = getCurrentInputConnection();
2011 if (ic != null) {
2012 ic.finishComposingText();
2013 }
2014 }
2015 }
2016
2017 /**
2018 * Called when only the candidates view has been shown for showing
2019 * processing as the user enters text through a hard keyboard.
2020 * This will always be called after {@link #onStartInput},
2021 * allowing you to do your general setup there and just view-specific
2022 * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
2023 * will have been called some time before this function is called.
2024 *
2025 * <p>Note that this will <em>not</em> be called when the input method
2026 * is running in full editing mode, and thus receiving
2027 * {@link #onStartInputView} to initiate that operation. This is only
2028 * for the case when candidates are being shown while the input method
2029 * editor is hidden but wants to show its candidates UI as text is
2030 * entered through some other mechanism.
2031 *
2032 * @param info Description of the type of text being edited.
2033 * @param restarting Set to true if we are restarting input on the
2034 * same text field as before.
2035 */
2036 public void onStartCandidatesView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002037 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 }
2039
2040 /**
2041 * Called when the candidates view is being hidden from the user. This will
2042 * be called either prior to hiding the window, or prior to switching to
2043 * another target for editing.
2044 *
2045 * <p>The default
2046 * implementation uses the InputConnection to clear any active composing
2047 * text; you can override this (not calling the base class implementation)
2048 * to perform whatever behavior you would like.
2049 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08002050 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002051 * called immediately after.
2052 */
2053 public void onFinishCandidatesView(boolean finishingInput) {
2054 if (!finishingInput) {
2055 InputConnection ic = getCurrentInputConnection();
2056 if (ic != null) {
2057 ic.finishComposingText();
2058 }
2059 }
2060 }
2061
2062 /**
2063 * The system has decided that it may be time to show your input method.
2064 * This is called due to a corresponding call to your
The Android Open Source Project4df24232009-03-05 14:34:35 -08002065 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002066 * method. The default implementation uses
2067 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
2068 * and the current configuration to decide whether the input view should
2069 * be shown at this point.
2070 *
2071 * @param flags Provides additional information about the show request,
The Android Open Source Project4df24232009-03-05 14:34:35 -08002072 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002073 * @param configChange This is true if we are re-showing due to a
2074 * configuration change.
2075 * @return Returns true to indicate that the window should be shown.
2076 */
2077 public boolean onShowInputRequested(int flags, boolean configChange) {
2078 if (!onEvaluateInputViewShown()) {
2079 return false;
2080 }
2081 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
2082 if (!configChange && onEvaluateFullscreenMode()) {
2083 // Don't show if this is not explicitly requested by the user and
2084 // the input method is fullscreen. That would be too disruptive.
2085 // However, we skip this change for a config change, since if
2086 // the IME is already shown we do want to go into fullscreen
2087 // mode at this point.
2088 return false;
2089 }
Yohei Yukawad0d07972016-05-04 11:56:35 -07002090 if (!mSettingsObserver.shouldShowImeWithHardKeyboard() &&
2091 getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 // And if the device has a hard keyboard, even if it is
2093 // currently hidden, don't show the input method implicitly.
2094 // These kinds of devices don't need it that much.
2095 return false;
2096 }
2097 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002098 return true;
2099 }
Yohei Yukawaef5b4652016-04-03 22:50:11 -07002100
2101 /**
2102 * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal
2103 * states depending on its result. Since {@link #onShowInputRequested(int, boolean)} is
2104 * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
2105 * to have this method to ensure that those internal states are always updated no matter how
2106 * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
2107 * @param flags Provides additional information about the show request,
2108 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
2109 * @param configChange This is true if we are re-showing due to a
2110 * configuration change.
2111 * @return Returns true to indicate that the window should be shown.
2112 * @see #onShowInputRequested(int, boolean)
2113 */
2114 private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
2115 final boolean result = onShowInputRequested(flags, configChange);
2116 if (result) {
2117 mShowInputFlags = flags;
2118 } else {
2119 mShowInputFlags = 0;
2120 }
2121 return result;
2122 }
2123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002124 public void showWindow(boolean showInput) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07002125 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002126 + " mShowInputRequested=" + mShowInputRequested
Tarandeep Singheadb1392018-11-09 18:15:57 +01002127 + " mViewsCreated=" + mViewsCreated
2128 + " mDecorViewVisible=" + mDecorViewVisible
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002129 + " mWindowVisible=" + mWindowVisible
Yohei Yukawaef5b4652016-04-03 22:50:11 -07002130 + " mInputStarted=" + mInputStarted
2131 + " mShowInputFlags=" + mShowInputFlags);
2132
The Android Open Source Project10592532009-03-18 17:39:46 -07002133 if (mInShowWindow) {
2134 Log.w(TAG, "Re-entrance in to showWindow");
2135 return;
2136 }
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002137
Tarandeep Singheadb1392018-11-09 18:15:57 +01002138 mDecorViewWasVisible = mDecorViewVisible;
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002139 mInShowWindow = true;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002140 boolean isPreRenderedAndInvisible = mIsPreRendered && !mWindowVisible;
2141 final int previousImeWindowStatus =
2142 (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
2143 ? (isPreRenderedAndInvisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
2144 startViews(prepareWindow(showInput));
2145 final int nextImeWindowStatus = mapToImeWindowStatus();
2146 if (previousImeWindowStatus != nextImeWindowStatus) {
2147 setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
2148 }
2149
2150 // compute visibility
2151 onWindowShown();
2152 mIsPreRendered = mCanPreRender;
2153 if (mIsPreRendered) {
2154 onPreRenderedWindowVisibilityChanged(true /* setVisible */);
2155 } else {
2156 // Pre-rendering not supported.
2157 if (DEBUG) Log.d(TAG, "No pre-rendering supported");
2158 mWindowVisible = true;
2159 }
2160
2161 // request draw for the IME surface.
2162 // When IME is not pre-rendered, this will actually show the IME.
2163 if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
2164 if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
2165 mWindow.show();
2166 }
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002167 maybeNotifyPreRendered();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002168 mDecorViewWasVisible = true;
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002169 mInShowWindow = false;
The Android Open Source Project10592532009-03-18 17:39:46 -07002170 }
satok06487a52010-10-29 11:37:18 +09002171
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002172 /**
2173 * Notify {@link android.view.ImeInsetsSourceConsumer} if IME has been pre-rendered
2174 * for current EditorInfo, when pre-rendering is enabled.
2175 */
2176 private void maybeNotifyPreRendered() {
2177 if (!mCanPreRender || !mIsPreRendered) {
2178 return;
2179 }
2180 mPrivOps.reportPreRendered(getCurrentInputEditorInfo());
2181 }
2182
2183
Tarandeep Singheadb1392018-11-09 18:15:57 +01002184 private boolean prepareWindow(boolean showInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002185 boolean doShowInput = false;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002186 mDecorViewVisible = true;
Yohei Yukawaac8bdd22015-09-11 18:17:11 -07002187 if (!mShowInputRequested && mInputStarted && showInput) {
2188 doShowInput = true;
2189 mShowInputRequested = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002190 }
satok06487a52010-10-29 11:37:18 +09002191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002192 if (DEBUG) Log.v(TAG, "showWindow: updating UI");
2193 initialize();
2194 updateFullscreenMode();
2195 updateInputViewShown();
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002196
Tarandeep Singheadb1392018-11-09 18:15:57 +01002197 if (!mViewsCreated) {
2198 mViewsCreated = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002199 initialize();
2200 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
2201 View v = onCreateCandidatesView();
2202 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
2203 if (v != null) {
2204 setCandidatesView(v);
2205 }
2206 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002207 return doShowInput;
2208 }
2209
2210 private void startViews(boolean doShowInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002211 if (mShowInputRequested) {
2212 if (!mInputViewStarted) {
2213 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
2214 mInputViewStarted = true;
Feng Cao7c85eb72020-02-28 11:39:56 -08002215 synchronized (mInlineLock) {
2216 if (mInlineSuggestionSession != null) {
2217 mInlineSuggestionSession.notifyOnStartInputView(getEditorInfoAutofillId());
2218 }
2219 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002220 onStartInputView(mInputEditorInfo, false);
2221 }
2222 } else if (!mCandidatesViewStarted) {
2223 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
2224 mCandidatesViewStarted = true;
2225 onStartCandidatesView(mInputEditorInfo, false);
2226 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002227 if (doShowInput) startExtractingText(false);
2228 }
satok06487a52010-10-29 11:37:18 +09002229
Tarandeep Singheadb1392018-11-09 18:15:57 +01002230 private void onPreRenderedWindowVisibilityChanged(boolean setVisible) {
2231 mWindowVisible = setVisible;
2232 mShowInputFlags = setVisible ? mShowInputFlags : 0;
2233 mShowInputRequested = setVisible;
2234 mDecorViewVisible = setVisible;
2235 if (setVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002236 onWindowShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002237 }
2238 }
satok06487a52010-10-29 11:37:18 +09002239
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002240 /**
2241 * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002242 * {@link ViewRootImpl.sNewInsetsMode} is enabled.
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002243 * @param setVisible {@code true} to make it visible, false to hide it.
2244 */
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002245 private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
2246 if (!isVisibilityAppliedUsingInsetsConsumer()) {
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002247 return;
2248 }
Tarandeep Singh4fe5b652020-02-20 17:20:19 -08002249 mPrivOps.applyImeVisibility(setVisible
2250 ? mCurShowInputToken : mCurHideInputToken, setVisible);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002251 }
2252
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002253 private boolean isVisibilityAppliedUsingInsetsConsumer() {
2254 return ViewRootImpl.sNewInsetsMode > NEW_INSETS_MODE_NONE;
2255 }
2256
Tarandeep Singheadb1392018-11-09 18:15:57 +01002257 private void finishViews(boolean finishingInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002258 if (mInputViewStarted) {
2259 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
Feng Cao7c85eb72020-02-28 11:39:56 -08002260 synchronized (mInlineLock) {
2261 if (mInlineSuggestionSession != null) {
2262 mInlineSuggestionSession.notifyOnFinishInputView(getEditorInfoAutofillId());
2263 }
2264 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002265 onFinishInputView(finishingInput);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002266 } else if (mCandidatesViewStarted) {
2267 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
Tarandeep Singheadb1392018-11-09 18:15:57 +01002268 onFinishCandidatesView(finishingInput);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002269 }
2270 mInputViewStarted = false;
2271 mCandidatesViewStarted = false;
satokf17db9f2011-09-14 18:55:58 +09002272 }
2273
satok2f913d92012-05-10 01:48:03 +09002274 private void doHideWindow() {
Yohei Yukawac54c1172018-09-06 11:39:50 -07002275 setImeWindowStatus(0, mBackDisposition);
satok2f913d92012-05-10 01:48:03 +09002276 hideWindow();
2277 }
2278
satokf17db9f2011-09-14 18:55:58 +09002279 public void hideWindow() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01002280 if (DEBUG) Log.v(TAG, "CALL: hideWindow");
2281 mIsPreRendered = false;
2282 mWindowVisible = false;
2283 finishViews(false /* finishingInput */);
2284 if (mDecorViewVisible) {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002285 // When insets API is enabled, it is responsible for client and server side
2286 // visibility of IME window.
2287 if (!isVisibilityAppliedUsingInsetsConsumer()) {
2288 mWindow.hide();
2289 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002290 mDecorViewVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002291 onWindowHidden();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002292 mDecorViewWasVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002293 }
Seigo Nonaka93c47ea2015-07-14 15:05:04 +09002294 updateFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002295 }
satok06487a52010-10-29 11:37:18 +09002296
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002297 /**
tiansiming [田思明]b9025932018-02-05 18:28:28 +08002298 * Called immediately before the input method window is shown to the user.
2299 * You could override this to prepare for the window to be shown
2300 * (update view structure etc).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002301 */
2302 public void onWindowShown() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002303 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002304 }
2305
2306 /**
2307 * Called when the input method window has been hidden from the user,
2308 * after previously being visible.
2309 */
2310 public void onWindowHidden() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002311 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002312 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -07002313
2314 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002315 * Called when a new client has bound to the input method. This
2316 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
2317 * and {@link #onFinishInput()} calls as the user navigates through its
2318 * UI. Upon this call you know that {@link #getCurrentInputBinding}
2319 * and {@link #getCurrentInputConnection} return valid objects.
2320 */
2321 public void onBindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002322 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002323 }
2324
2325 /**
2326 * Called when the previous bound client is no longer associated
2327 * with the input method. After returning {@link #getCurrentInputBinding}
2328 * and {@link #getCurrentInputConnection} will no longer return
2329 * valid objects.
2330 */
2331 public void onUnbindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002332 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002333 }
2334
2335 /**
2336 * Called to inform the input method that text input has started in an
2337 * editor. You should use this callback to initialize the state of your
2338 * input to match the state of the editor given to it.
2339 *
2340 * @param attribute The attributes of the editor that input is starting
2341 * in.
2342 * @param restarting Set to true if input is restarting in the same
2343 * editor such as because the application has changed the text in
2344 * the editor. Otherwise will be false, indicating this is a new
2345 * session with the editor.
2346 */
2347 public void onStartInput(EditorInfo attribute, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002348 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002349 }
2350
2351 void doFinishInput() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01002352 if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
2353 finishViews(true /* finishingInput */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002354 if (mInputStarted) {
2355 if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
2356 onFinishInput();
2357 }
2358 mInputStarted = false;
2359 mStartedInputConnection = null;
2360 mCurCompletions = null;
2361 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08002362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002363 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
2364 if (!restarting) {
2365 doFinishInput();
2366 }
2367 mInputStarted = true;
2368 mStartedInputConnection = ic;
2369 mInputEditorInfo = attribute;
2370 initialize();
2371 if (DEBUG) Log.v(TAG, "CALL: onStartInput");
2372 onStartInput(attribute, restarting);
Tarandeep Singheadb1392018-11-09 18:15:57 +01002373 if (mDecorViewVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002374 if (mShowInputRequested) {
2375 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
2376 mInputViewStarted = true;
Feng Cao7c85eb72020-02-28 11:39:56 -08002377 synchronized (mInlineLock) {
2378 if (mInlineSuggestionSession != null) {
2379 mInlineSuggestionSession.notifyOnStartInputView(getEditorInfoAutofillId());
2380 }
2381 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002382 onStartInputView(mInputEditorInfo, restarting);
2383 startExtractingText(true);
2384 } else if (mCandidatesVisibility == View.VISIBLE) {
2385 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
2386 mCandidatesViewStarted = true;
2387 onStartCandidatesView(mInputEditorInfo, restarting);
2388 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002389 } else if (mCanPreRender && mInputEditorInfo != null && mStartedInputConnection != null) {
2390 // Pre-render IME views and window when real EditorInfo is available.
2391 // pre-render IME window and keep it invisible.
2392 if (DEBUG) Log.v(TAG, "Pre-Render IME for " + mInputEditorInfo.fieldName);
2393 if (mInShowWindow) {
2394 Log.w(TAG, "Re-entrance in to showWindow");
2395 return;
2396 }
2397
2398 mDecorViewWasVisible = mDecorViewVisible;
2399 mInShowWindow = true;
2400 startViews(prepareWindow(true /* showInput */));
2401
2402 // compute visibility
2403 mIsPreRendered = true;
2404 onPreRenderedWindowVisibilityChanged(false /* setVisible */);
2405
2406 // request draw for the IME surface.
2407 // When IME is not pre-rendered, this will actually show the IME.
2408 if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
2409 mWindow.show();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002410 maybeNotifyPreRendered();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002411 mDecorViewWasVisible = true;
2412 mInShowWindow = false;
2413 } else {
2414 mIsPreRendered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002415 }
2416 }
2417
2418 /**
2419 * Called to inform the input method that text input has finished in
2420 * the last editor. At this point there may be a call to
2421 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
2422 * new editor, or the input method may be left idle. This method is
2423 * <em>not</em> called when input restarts in the same editor.
2424 *
2425 * <p>The default
2426 * implementation uses the InputConnection to clear any active composing
2427 * text; you can override this (not calling the base class implementation)
2428 * to perform whatever behavior you would like.
2429 */
2430 public void onFinishInput() {
2431 InputConnection ic = getCurrentInputConnection();
2432 if (ic != null) {
2433 ic.finishComposingText();
2434 }
2435 }
2436
2437 /**
2438 * Called when the application has reported auto-completion candidates that
2439 * it would like to have the input method displayed. Typically these are
2440 * only used when an input method is running in full-screen mode, since
2441 * otherwise the user can see and interact with the pop-up window of
2442 * completions shown by the application.
2443 *
2444 * <p>The default implementation here does nothing.
2445 */
2446 public void onDisplayCompletions(CompletionInfo[] completions) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002447 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002448 }
2449
2450 /**
2451 * Called when the application has reported new extracted text to be shown
2452 * due to changes in its current text state. The default implementation
2453 * here places the new text in the extract edit text, when the input
2454 * method is running in fullscreen mode.
2455 */
2456 public void onUpdateExtractedText(int token, ExtractedText text) {
2457 if (mExtractedToken != token) {
2458 return;
2459 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002460 if (text != null) {
2461 if (mExtractEditText != null) {
2462 mExtractedText = text;
2463 mExtractEditText.setExtractedText(text);
2464 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002465 }
2466 }
2467
2468 /**
2469 * Called when the application has reported a new selection region of
2470 * the text. This is called whether or not the input method has requested
2471 * extracted text updates, although if so it will not receive this call
2472 * if the extracted text has changed as well.
Jean Chalardc743cb92013-09-12 16:28:45 +09002473 *
2474 * <p>Be careful about changing the text in reaction to this call with
2475 * methods such as setComposingText, commitText or
2476 * deleteSurroundingText. If the cursor moves as a result, this method
2477 * will be called again, which may result in an infinite loop.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002478 *
2479 * <p>The default implementation takes care of updating the cursor in
2480 * the extract text, if it is being shown.
2481 */
2482 public void onUpdateSelection(int oldSelStart, int oldSelEnd,
2483 int newSelStart, int newSelEnd,
2484 int candidatesStart, int candidatesEnd) {
2485 final ExtractEditText eet = mExtractEditText;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07002486 if (eet != null && isFullscreenMode() && mExtractedText != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002487 final int off = mExtractedText.startOffset;
2488 eet.startInternalChanges();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07002489 newSelStart -= off;
2490 newSelEnd -= off;
2491 final int len = eet.getText().length();
2492 if (newSelStart < 0) newSelStart = 0;
2493 else if (newSelStart > len) newSelStart = len;
2494 if (newSelEnd < 0) newSelEnd = 0;
2495 else if (newSelEnd > len) newSelEnd = len;
2496 eet.setSelection(newSelStart, newSelEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002497 eet.finishInternalChanges();
2498 }
2499 }
2500
2501 /**
satok863fcd62011-06-21 17:38:02 +09002502 * Called when the user tapped or clicked a text view.
2503 * IMEs can't rely on this method being called because this was not part of the original IME
2504 * protocol, so applications with custom text editing written before this method appeared will
2505 * not call to inform the IME of this interaction.
2506 * @param focusChanged true if the user changed the focused view by this click.
Yohei Yukawa0eb8d162019-01-22 21:47:57 -08002507 * @see InputMethodManager#viewClicked(View)
2508 * @deprecated The method may not be called for composite {@link View} that works as a giant
2509 * "Canvas", which can host its own UI hierarchy and sub focus state.
2510 * {@link android.webkit.WebView} is a good example. Application / IME developers
2511 * should not rely on this method. If your goal is just being notified when an
2512 * on-going input is interrupted, simply monitor {@link #onFinishInput()}.
satok863fcd62011-06-21 17:38:02 +09002513 */
Yohei Yukawa0eb8d162019-01-22 21:47:57 -08002514 @Deprecated
satok863fcd62011-06-21 17:38:02 +09002515 public void onViewClicked(boolean focusChanged) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002516 // Intentionally empty
satok863fcd62011-06-21 17:38:02 +09002517 }
2518
2519 /**
Yohei Yukawaa277db22014-08-21 18:38:44 -07002520 * Called when the application has reported a new location of its text
2521 * cursor. This is only called if explicitly requested by the input method.
2522 * The default implementation does nothing.
Andrew Solovay5c05ded2018-10-02 14:14:42 -07002523 * @deprecated Use {@link #onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002524 */
Yohei Yukawaa277db22014-08-21 18:38:44 -07002525 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002526 public void onUpdateCursor(Rect newCursor) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002527 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002528 }
2529
2530 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +09002531 * Called when the application has reported a new location of its text insertion point and
2532 * characters in the composition string. This is only called if explicitly requested by the
2533 * input method. The default implementation does nothing.
2534 * @param cursorAnchorInfo The positional information of the text insertion point and the
2535 * composition string.
2536 */
2537 public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
2538 // Intentionally empty
2539 }
2540
2541 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002542 * Close this input method's soft input area, removing it from the display.
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002543 *
2544 * The input method will continue running, but the user can no longer use it to generate input
2545 * by touching the screen.
2546 *
2547 * @see InputMethodManager#HIDE_IMPLICIT_ONLY
2548 * @see InputMethodManager#HIDE_NOT_ALWAYS
2549 * @param flags Provides additional operating flags.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002550 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08002551 public void requestHideSelf(int flags) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07002552 mPrivOps.hideMySoftInput(flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002553 }
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002555 /**
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002556 * Show the input method's soft input area, so the user sees the input method window and can
2557 * interact with it.
2558 *
2559 * @see InputMethodManager#SHOW_IMPLICIT
2560 * @see InputMethodManager#SHOW_FORCED
2561 * @param flags Provides additional operating flags.
The Android Open Source Project4df24232009-03-05 14:34:35 -08002562 */
Tarandeep Singh45136992018-03-08 10:52:03 -08002563 public final void requestShowSelf(int flags) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07002564 mPrivOps.showMySoftInput(flags);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002565 }
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002566
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002567 private boolean handleBack(boolean doIt) {
2568 if (mShowInputRequested) {
2569 // If the soft input area is shown, back closes it and we
2570 // consume the back key.
2571 if (doIt) requestHideSelf(0);
2572 return true;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002573 } else if (mDecorViewVisible) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002574 if (mCandidatesVisibility == View.VISIBLE) {
2575 // If we are showing candidates even if no input area, then
2576 // hide them.
2577 if (doIt) setCandidatesViewShown(false);
2578 } else {
2579 // If we have the window visible for some other reason --
2580 // most likely to show candidates -- then just get rid
2581 // of it. This really shouldn't happen, but just in case...
satok2f913d92012-05-10 01:48:03 +09002582 if (doIt) doHideWindow();
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002583 }
2584 return true;
2585 }
2586 return false;
2587 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002588
2589 /**
Andrew Solovay5c05ded2018-10-02 14:14:42 -07002590 * @return {@link ExtractEditText} if it is considered to be visible and active. Otherwise
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002591 * {@code null} is returned.
2592 */
2593 private ExtractEditText getExtractEditTextIfVisible() {
2594 if (!isExtractViewShown() || !isInputViewShown()) {
2595 return null;
2596 }
2597 return mExtractEditText;
2598 }
2599
The Android Open Source Project4df24232009-03-05 14:34:35 -08002600 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -07002601 * Called back when a {@link KeyEvent} is forwarded from the target application.
2602 *
2603 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK} if the IME is
2604 * currently shown , to possibly hide it when the key goes up (if not canceled or long pressed).
2605 * In addition, in fullscreen mode only, it will consume DPAD movement events to move the cursor
2606 * in the extracted text view, not allowing them to perform navigation in the underlying
2607 * application.</p>
2608 *
2609 * <p>The default implementation does not take flags specified to
2610 * {@link #setBackDisposition(int)} into account, even on API version
2611 * {@link android.os.Build.VERSION_CODES#P} and later devices. IME developers are responsible
2612 * for making sure that their special handling for {@link KeyEvent#KEYCODE_BACK} are consistent
2613 * with the flag they specified to {@link #setBackDisposition(int)}.</p>
2614 *
2615 * @param keyCode The value in {@code event.getKeyCode()}
2616 * @param event Description of the key event
2617 *
2618 * @return {@code true} if the event is consumed by the IME and the application no longer needs
2619 * to consume it. Return {@code false} when the event should be handled as if the IME
2620 * had not seen the event at all.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002621 */
2622 public boolean onKeyDown(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002623 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002624 final ExtractEditText eet = getExtractEditTextIfVisible();
2625 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2626 return true;
2627 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002628 if (handleBack(false)) {
2629 event.startTracking();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002630 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002631 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002632 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002633 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002634 return doMovementKey(keyCode, event, MOVEMENT_DOWN);
2635 }
2636
2637 /**
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002638 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
2639 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
2640 * the event).
2641 */
2642 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
2643 return false;
2644 }
2645
2646 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002647 * Override this to intercept special key multiple events before they are
2648 * processed by the
2649 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002650 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002651 * will occur as if the IME had not seen the event at all.
2652 *
2653 * <p>The default implementation always returns false, except when
2654 * in fullscreen mode, where it will consume DPAD movement
2655 * events to move the cursor in the extracted text view, not allowing
2656 * them to perform navigation in the underlying application.
2657 */
2658 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
2659 return doMovementKey(keyCode, event, count);
2660 }
2661
2662 /**
2663 * Override this to intercept key up events before they are processed by the
2664 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002665 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002666 * will occur as if the IME had not seen the event at all.
2667 *
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002668 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
2669 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
2670 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002671 * events to move the cursor in the extracted text view, not allowing
2672 * them to perform navigation in the underlying application.
2673 */
2674 public boolean onKeyUp(int keyCode, KeyEvent event) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002675 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
2676 final ExtractEditText eet = getExtractEditTextIfVisible();
2677 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2678 return true;
2679 }
2680 if (event.isTracking() && !event.isCanceled()) {
2681 return handleBack(true);
2682 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002683 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002684 return doMovementKey(keyCode, event, MOVEMENT_UP);
2685 }
2686
Victoria Leaseb38070c2012-08-24 13:46:02 -07002687 /**
2688 * Override this to intercept trackball motion events before they are
2689 * processed by the application.
2690 * If you return true, the application will not itself process the event.
2691 * If you return false, the normal application processing will occur as if
2692 * the IME had not seen the event at all.
2693 */
satokab751aa2010-09-14 19:17:36 +09002694 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002695 public boolean onTrackballEvent(MotionEvent event) {
Victoria Leaseb38070c2012-08-24 13:46:02 -07002696 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
2697 return false;
2698 }
2699
2700 /**
2701 * Override this to intercept generic motion events before they are
2702 * processed by the application.
2703 * If you return true, the application will not itself process the event.
2704 * If you return false, the normal application processing will occur as if
2705 * the IME had not seen the event at all.
2706 */
2707 @Override
2708 public boolean onGenericMotionEvent(MotionEvent event) {
2709 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002710 return false;
2711 }
2712
2713 public void onAppPrivateCommand(String action, Bundle data) {
2714 }
2715
The Android Open Source Project4df24232009-03-05 14:34:35 -08002716 /**
2717 * Handle a request by the system to toggle the soft input area.
2718 */
2719 private void onToggleSoftInput(int showFlags, int hideFlags) {
2720 if (DEBUG) Log.v(TAG, "toggleSoftInput()");
2721 if (isInputViewShown()) {
2722 requestHideSelf(hideFlags);
2723 } else {
2724 requestShowSelf(showFlags);
2725 }
2726 }
2727
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002728 static final int MOVEMENT_DOWN = -1;
2729 static final int MOVEMENT_UP = -2;
2730
2731 void reportExtractedMovement(int keyCode, int count) {
2732 int dx = 0, dy = 0;
2733 switch (keyCode) {
2734 case KeyEvent.KEYCODE_DPAD_LEFT:
2735 dx = -count;
2736 break;
2737 case KeyEvent.KEYCODE_DPAD_RIGHT:
2738 dx = count;
2739 break;
2740 case KeyEvent.KEYCODE_DPAD_UP:
2741 dy = -count;
2742 break;
2743 case KeyEvent.KEYCODE_DPAD_DOWN:
2744 dy = count;
2745 break;
2746 }
satokab751aa2010-09-14 19:17:36 +09002747 onExtractedCursorMovement(dx, dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002748 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002749
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002750 boolean doMovementKey(int keyCode, KeyEvent event, int count) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002751 final ExtractEditText eet = getExtractEditTextIfVisible();
2752 if (eet != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002753 // If we are in fullscreen mode, the cursor will move around
2754 // the extract edit text, but should NOT cause focus to move
2755 // to other fields.
2756 MovementMethod movement = eet.getMovementMethod();
2757 Layout layout = eet.getLayout();
2758 if (movement != null && layout != null) {
2759 // We want our own movement method to handle the key, so the
2760 // cursor will properly move in our own word wrapping.
2761 if (count == MOVEMENT_DOWN) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002762 if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002763 reportExtractedMovement(keyCode, 1);
2764 return true;
2765 }
2766 } else if (count == MOVEMENT_UP) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002767 if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002768 return true;
2769 }
2770 } else {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002771 if (movement.onKeyOther(eet, eet.getText(), event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002772 reportExtractedMovement(keyCode, count);
2773 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07002774 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002775 if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002776 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002777 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002778 while (--count > 0) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002779 movement.onKeyDown(eet, eet.getText(), keyCode, down);
2780 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002781 }
2782 reportExtractedMovement(keyCode, count);
2783 }
2784 }
2785 }
2786 }
2787 // Regardless of whether the movement method handled the key,
2788 // we never allow DPAD navigation to the application.
2789 switch (keyCode) {
2790 case KeyEvent.KEYCODE_DPAD_LEFT:
2791 case KeyEvent.KEYCODE_DPAD_RIGHT:
2792 case KeyEvent.KEYCODE_DPAD_UP:
2793 case KeyEvent.KEYCODE_DPAD_DOWN:
2794 return true;
2795 }
2796 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002797
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002798 return false;
2799 }
2800
2801 /**
2802 * Send the given key event code (as defined by {@link KeyEvent}) to the
2803 * current input connection is a key down + key up event pair. The sent
2804 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
2805 * set, so that the recipient can identify them as coming from a software
2806 * input method, and
2807 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
2808 * that they don't impact the current touch mode of the UI.
2809 *
Jean Chalard405bc512012-05-29 19:12:34 +09002810 * <p>Note that it's discouraged to send such key events in normal operation;
2811 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
2812 * text fields, or for non-rich input methods. A reasonably capable software
2813 * input method should use the
2814 * {@link android.view.inputmethod.InputConnection#commitText} family of methods
2815 * to send text to an application, rather than sending key events.</p>
2816 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002817 * @param keyEventCode The raw key code to send, as defined by
2818 * {@link KeyEvent}.
2819 */
2820 public void sendDownUpKeyEvents(int keyEventCode) {
2821 InputConnection ic = getCurrentInputConnection();
2822 if (ic == null) return;
2823 long eventTime = SystemClock.uptimeMillis();
2824 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002825 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002826 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
Tadashi G. Takaokacb95cd62012-10-26 17:20:59 +09002827 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002828 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002829 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2830 }
2831
2832 /**
2833 * Ask the input target to execute its default action via
2834 * {@link InputConnection#performEditorAction
2835 * InputConnection.performEditorAction()}.
2836 *
2837 * @param fromEnterKey If true, this will be executed as if the user had
2838 * pressed an enter key on the keyboard, that is it will <em>not</em>
2839 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
2840 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
2841 * sent regardless of how the editor has set that flag.
2842 *
2843 * @return Returns a boolean indicating whether an action has been sent.
2844 * If false, either the editor did not specify a default action or it
2845 * does not want an action from the enter key. If true, the action was
2846 * sent (or there was no input connection at all).
2847 */
2848 public boolean sendDefaultEditorAction(boolean fromEnterKey) {
2849 EditorInfo ei = getCurrentInputEditorInfo();
2850 if (ei != null &&
2851 (!fromEnterKey || (ei.imeOptions &
2852 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
2853 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
2854 EditorInfo.IME_ACTION_NONE) {
2855 // If the enter key was pressed, and the editor has a default
2856 // action associated with pressing enter, then send it that
2857 // explicit action instead of the key event.
2858 InputConnection ic = getCurrentInputConnection();
2859 if (ic != null) {
2860 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
2861 }
2862 return true;
2863 }
2864
2865 return false;
2866 }
2867
2868 /**
2869 * Send the given UTF-16 character to the current input connection. Most
2870 * characters will be delivered simply by calling
2871 * {@link InputConnection#commitText InputConnection.commitText()} with
2872 * the character; some, however, may be handled different. In particular,
2873 * the enter character ('\n') will either be delivered as an action code
Jean Chalard405bc512012-05-29 19:12:34 +09002874 * or a raw key event, as appropriate. Consider this as a convenience
2875 * method for IMEs that do not have a full implementation of actions; a
2876 * fully complying IME will decide of the right action for each event and
2877 * will likely never call this method except maybe to handle events coming
2878 * from an actual hardware keyboard.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002879 *
2880 * @param charCode The UTF-16 character code to send.
2881 */
2882 public void sendKeyChar(char charCode) {
2883 switch (charCode) {
2884 case '\n': // Apps may be listening to an enter key to perform an action
2885 if (!sendDefaultEditorAction(true)) {
2886 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2887 }
2888 break;
2889 default:
2890 // Make sure that digits go through any text watcher on the client side.
2891 if (charCode >= '0' && charCode <= '9') {
2892 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2893 } else {
2894 InputConnection ic = getCurrentInputConnection();
2895 if (ic != null) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002896 ic.commitText(String.valueOf(charCode), 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002897 }
2898 }
2899 break;
2900 }
2901 }
2902
2903 /**
2904 * This is called when the user has moved the cursor in the extracted
2905 * text view, when running in fullsreen mode. The default implementation
2906 * performs the corresponding selection change on the underlying text
2907 * editor.
2908 */
2909 public void onExtractedSelectionChanged(int start, int end) {
2910 InputConnection conn = getCurrentInputConnection();
2911 if (conn != null) {
2912 conn.setSelection(start, end);
2913 }
2914 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002915
2916 /**
2917 * @hide
2918 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002919 @UnsupportedAppUsage
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002920 public void onExtractedDeleteText(int start, int end) {
2921 InputConnection conn = getCurrentInputConnection();
2922 if (conn != null) {
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002923 conn.finishComposingText();
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002924 conn.setSelection(start, start);
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002925 conn.deleteSurroundingText(0, end - start);
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002926 }
2927 }
2928
2929 /**
2930 * @hide
2931 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002932 @UnsupportedAppUsage
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002933 public void onExtractedReplaceText(int start, int end, CharSequence text) {
2934 InputConnection conn = getCurrentInputConnection();
2935 if (conn != null) {
2936 conn.setComposingRegion(start, end);
2937 conn.commitText(text, 1);
2938 }
2939 }
2940
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002941 /**
Gilles Debunnee300be92011-12-06 10:15:56 -08002942 * @hide
2943 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002944 @UnsupportedAppUsage
Gilles Debunnee300be92011-12-06 10:15:56 -08002945 public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2946 InputConnection conn = getCurrentInputConnection();
2947 if (conn != null) {
2948 if (!conn.setSelection(start, end)) return;
2949 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2950 if (text instanceof Spannable) {
2951 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2952 conn.setComposingRegion(start, end);
2953 conn.commitText(text, 1);
2954 }
2955 }
2956 }
2957
2958 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002959 * This is called when the user has clicked on the extracted text view,
2960 * when running in fullscreen mode. The default implementation hides
2961 * the candidates view when this happens, but only if the extracted text
2962 * editor has a vertical scroll bar because its text doesn't fit.
2963 * Re-implement this to provide whatever behavior you want.
2964 */
2965 public void onExtractedTextClicked() {
2966 if (mExtractEditText == null) {
2967 return;
2968 }
2969 if (mExtractEditText.hasVerticalScrollBar()) {
2970 setCandidatesViewShown(false);
2971 }
2972 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002973
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002974 /**
2975 * This is called when the user has performed a cursor movement in the
2976 * extracted text view, when it is running in fullscreen mode. The default
2977 * implementation hides the candidates view when a vertical movement
2978 * happens, but only if the extracted text editor has a vertical scroll bar
2979 * because its text doesn't fit.
2980 * Re-implement this to provide whatever behavior you want.
2981 * @param dx The amount of cursor movement in the x dimension.
2982 * @param dy The amount of cursor movement in the y dimension.
2983 */
2984 public void onExtractedCursorMovement(int dx, int dy) {
2985 if (mExtractEditText == null || dy == 0) {
2986 return;
2987 }
2988 if (mExtractEditText.hasVerticalScrollBar()) {
2989 setCandidatesViewShown(false);
2990 }
2991 }
2992
2993 /**
2994 * This is called when the user has selected a context menu item from the
2995 * extracted text view, when running in fullscreen mode. The default
2996 * implementation sends this action to the current InputConnection's
2997 * {@link InputConnection#performContextMenuAction(int)}, for it
2998 * to be processed in underlying "real" editor. Re-implement this to
2999 * provide whatever behavior you want.
3000 */
3001 public boolean onExtractTextContextMenuItem(int id) {
3002 InputConnection ic = getCurrentInputConnection();
3003 if (ic != null) {
3004 ic.performContextMenuAction(id);
3005 }
3006 return true;
3007 }
Elliot Waite54de7742017-01-11 15:30:35 -08003008
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003009 /**
3010 * Return text that can be used as a button label for the given
3011 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
3012 * if there is no action requested. Note that there is no guarantee that
3013 * the returned text will be relatively short, so you probably do not
3014 * want to use it as text on a soft keyboard key label.
Elliot Waite54de7742017-01-11 15:30:35 -08003015 *
3016 * @param imeOptions The value from {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
3017 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003018 * @return Returns a label to use, or null if there is no action.
3019 */
3020 public CharSequence getTextForImeAction(int imeOptions) {
3021 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
3022 case EditorInfo.IME_ACTION_NONE:
3023 return null;
3024 case EditorInfo.IME_ACTION_GO:
3025 return getText(com.android.internal.R.string.ime_action_go);
3026 case EditorInfo.IME_ACTION_SEARCH:
3027 return getText(com.android.internal.R.string.ime_action_search);
3028 case EditorInfo.IME_ACTION_SEND:
3029 return getText(com.android.internal.R.string.ime_action_send);
3030 case EditorInfo.IME_ACTION_NEXT:
3031 return getText(com.android.internal.R.string.ime_action_next);
The Android Open Source Project4df24232009-03-05 14:34:35 -08003032 case EditorInfo.IME_ACTION_DONE:
3033 return getText(com.android.internal.R.string.ime_action_done);
Dianne Hackborndea3ef72010-10-28 14:24:22 -07003034 case EditorInfo.IME_ACTION_PREVIOUS:
3035 return getText(com.android.internal.R.string.ime_action_previous);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003036 default:
3037 return getText(com.android.internal.R.string.ime_action_default);
3038 }
3039 }
Mark Renouf91eb2652016-04-11 16:03:26 -04003040
3041 /**
3042 * Return a drawable resource id that can be used as a button icon for the given
3043 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
3044 *
3045 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
3046 *
3047 * @return Returns a drawable resource id to use.
3048 */
3049 @DrawableRes
3050 private int getIconForImeAction(int imeOptions) {
3051 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
3052 case EditorInfo.IME_ACTION_GO:
3053 return com.android.internal.R.drawable.ic_input_extract_action_go;
3054 case EditorInfo.IME_ACTION_SEARCH:
3055 return com.android.internal.R.drawable.ic_input_extract_action_search;
3056 case EditorInfo.IME_ACTION_SEND:
3057 return com.android.internal.R.drawable.ic_input_extract_action_send;
3058 case EditorInfo.IME_ACTION_NEXT:
3059 return com.android.internal.R.drawable.ic_input_extract_action_next;
3060 case EditorInfo.IME_ACTION_DONE:
3061 return com.android.internal.R.drawable.ic_input_extract_action_done;
3062 case EditorInfo.IME_ACTION_PREVIOUS:
3063 return com.android.internal.R.drawable.ic_input_extract_action_previous;
3064 default:
3065 return com.android.internal.R.drawable.ic_input_extract_action_return;
3066 }
3067 }
3068
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003069 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07003070 * Called when the fullscreen-mode extracting editor info has changed,
3071 * to determine whether the extracting (extract text and candidates) portion
3072 * of the UI should be shown. The standard implementation hides or shows
3073 * the extract area depending on whether it makes sense for the
3074 * current editor. In particular, a {@link InputType#TYPE_NULL}
3075 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
3076 * turn off the extract area since there is no text to be shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003077 */
The Android Open Source Project10592532009-03-18 17:39:46 -07003078 public void onUpdateExtractingVisibility(EditorInfo ei) {
3079 if (ei.inputType == InputType.TYPE_NULL ||
3080 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
3081 // No reason to show extract UI!
3082 setExtractViewShown(false);
3083 return;
3084 }
3085
3086 setExtractViewShown(true);
3087 }
3088
3089 /**
3090 * Called when the fullscreen-mode extracting editor info has changed,
3091 * to update the state of its UI such as the action buttons shown.
3092 * You do not need to deal with this if you are using the standard
3093 * full screen extract UI. If replacing it, you will need to re-implement
3094 * this to put the appropriate action button in your own UI and handle it,
3095 * and perform any other changes.
3096 *
3097 * <p>The standard implementation turns on or off its accessory area
3098 * depending on whether there is an action button, and hides or shows
3099 * the entire extract area depending on whether it makes sense for the
3100 * current editor. In particular, a {@link InputType#TYPE_NULL} or
3101 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
3102 * extract area since there is no text to be shown.
3103 */
3104 public void onUpdateExtractingViews(EditorInfo ei) {
3105 if (!isExtractViewShown()) {
3106 return;
3107 }
3108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003109 if (mExtractAccessories == null) {
3110 return;
3111 }
3112 final boolean hasAction = ei.actionLabel != null || (
3113 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
The Android Open Source Project10592532009-03-18 17:39:46 -07003114 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
3115 ei.inputType != InputType.TYPE_NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003116 if (hasAction) {
3117 mExtractAccessories.setVisibility(View.VISIBLE);
Steve Kondik59eb6912009-09-07 22:53:34 -04003118 if (mExtractAction != null) {
Mark Renouf91eb2652016-04-11 16:03:26 -04003119 if (mExtractAction instanceof ImageButton) {
3120 ((ImageButton) mExtractAction)
3121 .setImageResource(getIconForImeAction(ei.imeOptions));
3122 if (ei.actionLabel != null) {
3123 mExtractAction.setContentDescription(ei.actionLabel);
3124 } else {
3125 mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
3126 }
Steve Kondik59eb6912009-09-07 22:53:34 -04003127 } else {
Mark Renouf91eb2652016-04-11 16:03:26 -04003128 if (ei.actionLabel != null) {
3129 ((TextView) mExtractAction).setText(ei.actionLabel);
3130 } else {
3131 ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
3132 }
Steve Kondik59eb6912009-09-07 22:53:34 -04003133 }
3134 mExtractAction.setOnClickListener(mActionClickListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003135 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003136 } else {
3137 mExtractAccessories.setVisibility(View.GONE);
Steve Kondik59eb6912009-09-07 22:53:34 -04003138 if (mExtractAction != null) {
3139 mExtractAction.setOnClickListener(null);
3140 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003141 }
3142 }
3143
3144 /**
3145 * This is called when, while currently displayed in extract mode, the
3146 * current input target changes. The default implementation will
3147 * auto-hide the IME if the new target is not a full editor, since this
Ken Wakasaf76a50c2012-03-09 19:56:35 +09003148 * can be a confusing experience for the user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003149 */
3150 public void onExtractingInputChanged(EditorInfo ei) {
3151 if (ei.inputType == InputType.TYPE_NULL) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08003152 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003153 }
3154 }
3155
3156 void startExtractingText(boolean inputChanged) {
3157 final ExtractEditText eet = mExtractEditText;
3158 if (eet != null && getCurrentInputStarted()
3159 && isFullscreenMode()) {
3160 mExtractedToken++;
3161 ExtractedTextRequest req = new ExtractedTextRequest();
3162 req.token = mExtractedToken;
3163 req.flags = InputConnection.GET_TEXT_WITH_STYLES;
3164 req.hintMaxLines = 10;
3165 req.hintMaxChars = 10000;
Amith Yamasaniba4d93f2009-08-19 18:27:56 -07003166 InputConnection ic = getCurrentInputConnection();
3167 mExtractedText = ic == null? null
3168 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
Amith Yamasania8b00c82010-03-05 15:41:31 -08003169 if (mExtractedText == null || ic == null) {
3170 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
3171 + mExtractedText + ", input connection = " + ic);
3172 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003173 final EditorInfo ei = getCurrentInputEditorInfo();
3174
3175 try {
3176 eet.startInternalChanges();
The Android Open Source Project10592532009-03-18 17:39:46 -07003177 onUpdateExtractingVisibility(ei);
3178 onUpdateExtractingViews(ei);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003179 int inputType = ei.inputType;
3180 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
3181 == EditorInfo.TYPE_CLASS_TEXT) {
3182 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
3183 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
3184 }
3185 }
3186 eet.setInputType(inputType);
3187 eet.setHint(ei.hintText);
3188 if (mExtractedText != null) {
3189 eet.setEnabled(true);
3190 eet.setExtractedText(mExtractedText);
3191 } else {
3192 eet.setEnabled(false);
3193 eet.setText("");
3194 }
3195 } finally {
3196 eet.finishInternalChanges();
3197 }
3198
3199 if (inputChanged) {
3200 onExtractingInputChanged(ei);
3201 }
3202 }
3203 }
satokab751aa2010-09-14 19:17:36 +09003204
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003205 private void dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
3206 synchronized (mLock) {
3207 mNotifyUserActionSent = false;
3208 }
3209 onCurrentInputMethodSubtypeChanged(newSubtype);
3210 }
3211
satokab751aa2010-09-14 19:17:36 +09003212 // TODO: Handle the subtype change event
3213 /**
3214 * Called when the subtype was changed.
3215 * @param newSubtype the subtype which is being changed to.
3216 */
3217 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
3218 if (DEBUG) {
3219 int nameResId = newSubtype.getNameResId();
satok9ef02832010-11-04 21:17:48 +09003220 String mode = newSubtype.getMode();
satokab751aa2010-09-14 19:17:36 +09003221 String output = "changeInputMethodSubtype:"
3222 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
satok9ef02832010-11-04 21:17:48 +09003223 + mode + ","
satokab751aa2010-09-14 19:17:36 +09003224 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
3225 Log.v(TAG, "--- " + output);
3226 }
3227 }
3228
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003229 /**
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003230 * Aimed to return the previous input method's {@link Insets#contentTopInsets}, but its actual
3231 * semantics has never been well defined.
3232 *
3233 * <p>Note that the previous document clearly mentioned that this method could return {@code 0}
3234 * at any time for whatever reason. Now this method is just always returning {@code 0}.</p>
3235 *
3236 * @return on Android {@link android.os.Build.VERSION_CODES#Q} and later devices this method
3237 * always returns {@code 0}
3238 * @deprecated the actual behavior of this method has never been well defined. You cannot use
3239 * this method in a reliable and predictable way
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003240 */
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003241 @Deprecated
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003242 public int getInputMethodWindowRecommendedHeight() {
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003243 Log.w(TAG, "getInputMethodWindowRecommendedHeight() is deprecated and now always returns 0."
3244 + " Do not use this method.");
3245 return 0;
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003246 }
3247
3248 /**
Yohei Yukawa25e08132016-06-22 16:31:41 -07003249 * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
3250 * permission to the content.
3251 *
Yohei Yukawa25e08132016-06-22 16:31:41 -07003252 * @param inputContentInfo Content to be temporarily exposed from the input method to the
3253 * application.
3254 * This cannot be {@code null}.
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003255 * @param inputConnection {@link InputConnection} with which
Yohei Yukawab2a0e052018-01-14 16:06:16 -08003256 * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} will be called.
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003257 * @hide
Yohei Yukawa25e08132016-06-22 16:31:41 -07003258 */
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003259 @Override
3260 public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
3261 @NonNull InputConnection inputConnection) {
3262 if (inputConnection == null) {
3263 return;
Yohei Yukawa25e08132016-06-22 16:31:41 -07003264 }
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003265 if (getCurrentInputConnection() != inputConnection) {
3266 return;
Yohei Yukawa25e08132016-06-22 16:31:41 -07003267 }
Yohei Yukawac54c1172018-09-06 11:39:50 -07003268 exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
3269 }
3270
3271 /**
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003272 * {@inheritDoc}
3273 * @hide
3274 */
3275 @AnyThread
3276 @Override
3277 public final void notifyUserActionIfNecessary() {
3278 synchronized (mLock) {
3279 if (mNotifyUserActionSent) {
3280 return;
3281 }
Yohei Yukawa9b60ba02019-01-21 00:06:27 -08003282 mPrivOps.notifyUserAction();
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003283 mNotifyUserActionSent = true;
3284 }
3285 }
3286
3287 /**
Yohei Yukawac54c1172018-09-06 11:39:50 -07003288 * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
3289 * permission to the content.
3290 *
3291 * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
3292 * InputConnection)} for details.</p>
3293 *
3294 * @param inputContentInfo Content to be temporarily exposed from the input method to the
3295 * application.
3296 * This cannot be {@code null}.
3297 * @param editorInfo The editor that receives {@link InputContentInfo}.
3298 */
3299 private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
3300 @NonNull EditorInfo editorInfo) {
Yohei Yukawac54c1172018-09-06 11:39:50 -07003301 final Uri contentUri = inputContentInfo.getContentUri();
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07003302 final IInputContentUriToken uriToken =
3303 mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
3304 if (uriToken == null) {
Yohei Yukawac54c1172018-09-06 11:39:50 -07003305 Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07003306 + " packageName=" + editorInfo.packageName);
Yohei Yukawac54c1172018-09-06 11:39:50 -07003307 return;
3308 }
3309 inputContentInfo.setUriToken(uriToken);
Yohei Yukawa25e08132016-06-22 16:31:41 -07003310 }
3311
Tarandeep Singheadb1392018-11-09 18:15:57 +01003312 private int mapToImeWindowStatus() {
3313 return IME_ACTIVE
3314 | (isInputViewShown()
3315 ? (mCanPreRender ? (mWindowVisible ? IME_VISIBLE : IME_INVISIBLE)
3316 : IME_VISIBLE) : 0);
Tarandeep Singh3fecef12018-01-22 14:33:33 -08003317 }
3318
JianYang Liu7eec3162020-01-23 17:09:26 -08003319 private boolean isAutomotive() {
3320 return getApplicationContext().getPackageManager().hasSystemFeature(
3321 PackageManager.FEATURE_AUTOMOTIVE);
3322 }
3323
Yohei Yukawa25e08132016-06-22 16:31:41 -07003324 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003325 * Performs a dump of the InputMethodService's internal state. Override
3326 * to add your own information to the dump.
3327 */
3328 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
3329 final Printer p = new PrintWriterPrinter(fout);
3330 p.println("Input method service state for " + this + ":");
Tarandeep Singheadb1392018-11-09 18:15:57 +01003331 p.println(" mViewsCreated=" + mViewsCreated);
3332 p.println(" mDecorViewVisible=" + mDecorViewVisible
3333 + " mDecorViewWasVisible=" + mDecorViewWasVisible
3334 + " mWindowVisible=" + mWindowVisible
The Android Open Source Project10592532009-03-18 17:39:46 -07003335 + " mInShowWindow=" + mInShowWindow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003336 p.println(" Configuration=" + getResources().getConfiguration());
3337 p.println(" mToken=" + mToken);
3338 p.println(" mInputBinding=" + mInputBinding);
3339 p.println(" mInputConnection=" + mInputConnection);
3340 p.println(" mStartedInputConnection=" + mStartedInputConnection);
3341 p.println(" mInputStarted=" + mInputStarted
3342 + " mInputViewStarted=" + mInputViewStarted
3343 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08003344
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003345 if (mInputEditorInfo != null) {
3346 p.println(" mInputEditorInfo:");
3347 mInputEditorInfo.dump(p, " ");
3348 } else {
3349 p.println(" mInputEditorInfo: null");
3350 }
3351
3352 p.println(" mShowInputRequested=" + mShowInputRequested
3353 + " mLastShowInputRequested=" + mLastShowInputRequested
Tarandeep Singheadb1392018-11-09 18:15:57 +01003354 + " mCanPreRender=" + mCanPreRender
3355 + " mIsPreRendered=" + mIsPreRendered
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003356 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
3357 p.println(" mCandidatesVisibility=" + mCandidatesVisibility
3358 + " mFullscreenApplied=" + mFullscreenApplied
The Android Open Source Project10592532009-03-18 17:39:46 -07003359 + " mIsFullscreen=" + mIsFullscreen
3360 + " mExtractViewHidden=" + mExtractViewHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003361
3362 if (mExtractedText != null) {
3363 p.println(" mExtractedText:");
3364 p.println(" text=" + mExtractedText.text.length() + " chars"
3365 + " startOffset=" + mExtractedText.startOffset);
3366 p.println(" selectionStart=" + mExtractedText.selectionStart
3367 + " selectionEnd=" + mExtractedText.selectionEnd
3368 + " flags=0x" + Integer.toHexString(mExtractedText.flags));
3369 } else {
3370 p.println(" mExtractedText: null");
3371 }
3372 p.println(" mExtractedToken=" + mExtractedToken);
3373 p.println(" mIsInputViewShown=" + mIsInputViewShown
3374 + " mStatusIcon=" + mStatusIcon);
3375 p.println("Last computed insets:");
3376 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
3377 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
Jeff Brownfbf09772011-01-16 14:06:57 -08003378 + " touchableInsets=" + mTmpInsets.touchableInsets
3379 + " touchableRegion=" + mTmpInsets.touchableRegion);
Yohei Yukawa7b739a82015-12-21 13:30:44 -08003380 p.println(" mSettingsObserver=" + mSettingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003381 }
3382}