blob: 8e52ee944c1cf25ee534b315b2f7cbfa8930378e [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
lumark90120a82018-08-15 00:33:03 +080019import static android.view.Display.DEFAULT_DISPLAY;
Romain Guy980a9382010-01-08 15:06:28 -080020import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070022import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
Yohei Yukawa8f162c62018-01-10 13:18:09 -080023import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
Tiger Huang4a7835f2019-11-06 00:07:56 +080024import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
Adam Hebc67f2e2019-11-13 14:34:56 -080026import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
27
Yohei Yukawa386f50e2018-03-14 13:03:42 -070028import static java.lang.annotation.RetentionPolicy.SOURCE;
29
Yohei Yukawac07fd4c2018-09-11 11:37:13 -070030import android.annotation.AnyThread;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080031import android.annotation.CallSuper;
Tor Norbye7b9c9122013-05-30 16:48:33 -070032import android.annotation.DrawableRes;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080033import android.annotation.IntDef;
34import android.annotation.MainThread;
Yohei Yukawa25e08132016-06-22 16:31:41 -070035import android.annotation.NonNull;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -080036import android.annotation.Nullable;
Dianne Hackborn836531b2012-08-01 19:00:38 -070037import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.app.Dialog;
Artur Satayev26958002019-12-10 17:47:52 +000039import android.compat.annotation.UnsupportedAppUsage;
Adam Hebc67f2e2019-11-13 14:34:56 -080040import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.Context;
42import android.content.res.Configuration;
Dianne Hackbornd922ae02011-01-14 11:43:24 -080043import android.content.res.Resources;
The Android Open Source Project10592532009-03-18 17:39:46 -070044import android.content.res.TypedArray;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080045import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.graphics.Rect;
Jeff Brownfbf09772011-01-16 14:06:57 -080047import android.graphics.Region;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080048import android.net.Uri;
Mathew Inwood31755f92018-12-20 13:53:36 +000049import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Bundle;
Yohei Yukawa7b739a82015-12-21 13:30:44 -080051import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.os.IBinder;
Adam Hebc67f2e2019-11-13 14:34:56 -080053import android.os.Looper;
54import android.os.RemoteException;
The Android Open Source Project4df24232009-03-05 14:34:35 -080055import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.os.SystemClock;
57import android.provider.Settings;
58import android.text.InputType;
59import android.text.Layout;
60import android.text.Spannable;
61import android.text.method.MovementMethod;
62import android.util.Log;
63import android.util.PrintWriterPrinter;
64import android.util.Printer;
Dianne Hackborne30e02f2014-05-27 18:24:45 -070065import android.view.Gravity;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080066import android.view.KeyCharacterMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.view.KeyEvent;
68import android.view.LayoutInflater;
69import android.view.MotionEvent;
70import android.view.View;
71import android.view.ViewGroup;
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070072import android.view.ViewRootImpl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import android.view.ViewTreeObserver;
74import android.view.Window;
Tiger Huang4a7835f2019-11-06 00:07:56 +080075import android.view.WindowInsets;
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;
Adam Hebc67f2e2019-11-13 14:34: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;
103import com.android.internal.view.IInlineSuggestionsResponseCallback;
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;
Adam Hebc67f2e2019-11-13 14:34:56 -0800109import java.lang.ref.WeakReference;
Vinit Nayak31595bc2019-09-30 15:04:19 -0700110import java.util.Collections;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
112/**
113 * InputMethodService provides a standard implementation of an InputMethod,
114 * which final implementations can derive from and customize. See the
115 * base class {@link AbstractInputMethodService} and the {@link InputMethod}
116 * interface for more information on the basics of writing input methods.
117 *
118 * <p>In addition to the normal Service lifecycle methods, this class
119 * introduces some new specific callbacks that most subclasses will want
120 * to make use of:</p>
121 * <ul>
122 * <li> {@link #onInitializeInterface()} for user-interface initialization,
123 * in particular to deal with configuration changes while the service is
124 * running.
125 * <li> {@link #onBindInput} to find out about switching to a new client.
126 * <li> {@link #onStartInput} to deal with an input session starting with
127 * the client.
128 * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
129 * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
130 * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
131 * starting within the input area of the IME.
132 * </ul>
133 *
134 * <p>An input method has significant discretion in how it goes about its
135 * work: the {@link android.inputmethodservice.InputMethodService} provides
136 * a basic framework for standard UI elements (input view, candidates view,
137 * and running in fullscreen mode), but it is up to a particular implementor
138 * to decide how to use them. For example, one input method could implement
139 * an input area with a keyboard, another could allow the user to draw text,
140 * while a third could have no input area (and thus not be visible to the
141 * user) but instead listen to audio and perform text to speech conversion.</p>
142 *
143 * <p>In the implementation provided here, all of these elements are placed
144 * together in a single window managed by the InputMethodService. It will
145 * execute callbacks as it needs information about them, and provides APIs for
146 * programmatic control over them. They layout of these elements is explicitly
147 * defined:</p>
148 *
149 * <ul>
150 * <li>The soft input view, if available, is placed at the bottom of the
151 * screen.
152 * <li>The candidates view, if currently shown, is placed above the soft
153 * input view.
154 * <li>If not running fullscreen, the application is moved or resized to be
155 * above these views; if running fullscreen, the window will completely cover
156 * the application and its top part will contain the extract text of what is
157 * currently being edited by the application.
158 * </ul>
159 *
160 *
161 * <a name="SoftInputView"></a>
162 * <h3>Soft Input View</h3>
163 *
164 * <p>Central to most input methods is the soft input view. This is where most
165 * user interaction occurs: pressing on soft keys, drawing characters, or
166 * however else your input method wants to generate text. Most implementations
167 * will simply have their own view doing all of this work, and return a new
168 * instance of it when {@link #onCreateInputView()} is called. At that point,
169 * as long as the input view is visible, you will see user interaction in
170 * that view and can call back on the InputMethodService to interact with the
171 * application as appropriate.</p>
172 *
173 * <p>There are some situations where you want to decide whether or not your
174 * soft input view should be shown to the user. This is done by implementing
175 * the {@link #onEvaluateInputViewShown()} to return true or false based on
176 * whether it should be shown in the current environment. If any of your
177 * state has changed that may impact this, call
178 * {@link #updateInputViewShown()} to have it re-evaluated. The default
179 * implementation always shows the input view unless there is a hard
180 * keyboard available, which is the appropriate behavior for most input
181 * methods.</p>
182 *
183 *
184 * <a name="CandidatesView"></a>
185 * <h3>Candidates View</h3>
186 *
187 * <p>Often while the user is generating raw text, an input method wants to
188 * provide them with a list of possible interpretations of that text that can
189 * be selected for use. This is accomplished with the candidates view, and
190 * like the soft input view you implement {@link #onCreateCandidatesView()}
191 * to instantiate your own view implementing your candidates UI.</p>
192 *
193 * <p>Management of the candidates view is a little different than the input
194 * view, because the candidates view tends to be more transient, being shown
195 * only when there are possible candidates for the current text being entered
196 * by the user. To control whether the candidates view is shown, you use
197 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate
198 * view tends to be shown and hidden a lot, it does not impact the application
199 * UI in the same way as the soft input view: it will never cause application
200 * windows to resize, only cause them to be panned if needed for the user to
201 * see the current focus.</p>
202 *
203 *
204 * <a name="FullscreenMode"></a>
205 * <h3>Fullscreen Mode</h3>
206 *
207 * <p>Sometimes your input method UI is too large to integrate with the
208 * application UI, so you just want to take over the screen. This is
209 * accomplished by switching to full-screen mode, causing the input method
210 * window to fill the entire screen and add its own "extracted text" editor
211 * showing the user the text that is being typed. Unlike the other UI elements,
212 * there is a standard implementation for the extract editor that you should
213 * not need to change. The editor is placed at the top of the IME, above the
214 * input and candidates views.</p>
215 *
216 * <p>Similar to the input view, you control whether the IME is running in
217 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
218 * to return true or false based on
219 * whether it should be fullscreen in the current environment. If any of your
220 * state has changed that may impact this, call
221 * {@link #updateFullscreenMode()} to have it re-evaluated. The default
222 * implementation selects fullscreen mode when the screen is in a landscape
223 * orientation, which is appropriate behavior for most input methods that have
224 * a significant input area.</p>
225 *
226 * <p>When in fullscreen mode, you have some special requirements because the
227 * user can not see the application UI. In particular, you should implement
228 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
229 * generated by your application, typically in your candidates view like you
230 * would normally show candidates.
231 *
232 *
233 * <a name="GeneratingText"></a>
234 * <h3>Generating Text</h3>
235 *
236 * <p>The key part of an IME is of course generating text for the application.
237 * This is done through calls to the
238 * {@link android.view.inputmethod.InputConnection} interface to the
239 * application, which can be retrieved from {@link #getCurrentInputConnection()}.
240 * This interface allows you to generate raw key events or, if the target
241 * supports it, directly edit in strings of candidates and committed text.</p>
242 *
243 * <p>Information about what the target is expected and supports can be found
244 * through the {@link android.view.inputmethod.EditorInfo} class, which is
245 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most
246 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
247 * EditorInfo.inputType}; in particular, if this is
248 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
249 * then the target does not support complex edits and you need to only deliver
250 * raw key events to it. An input method will also want to look at other
251 * values here, to for example detect password mode, auto complete text views,
252 * phone number entry, etc.</p>
253 *
254 * <p>When the user switches between input targets, you will receive calls to
255 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
256 * You can use these to reset and initialize your input state for the current
257 * target. For example, you will often want to clear any input state, and
258 * update a soft keyboard to be appropriate for the new inputType.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -0700259 *
260 * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
261 * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
262 * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 */
264public class InputMethodService extends AbstractInputMethodService {
265 static final String TAG = "InputMethodService";
266 static final boolean DEBUG = false;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800267
268 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700269 * Allows the system to optimize the back button affordance based on the presence of software
270 * keyboard.
271 *
272 * <p>For instance, on devices that have navigation bar and software-rendered back button, the
273 * system may use a different icon while {@link #isInputViewShown()} returns {@code true}, to
274 * indicate that the back button has "dismiss" affordance.</p>
275 *
276 * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
277 * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
278 * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
279 * not take this mode into account.</p>
280 *
281 * <p>For API level {@link android.os.Build.VERSION_CODES#O_MR1} and lower devices, this is the
282 * only mode you can safely specify without worrying about the compatibility.</p>
283 *
284 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800285 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700286 public static final int BACK_DISPOSITION_DEFAULT = 0;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800287
288 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700289 * Deprecated flag.
290 *
291 * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
292 *
293 * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
294 * handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
295 * {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
296 * of this mode had not been well defined. Most likely the end result would be the
297 * same as {@link #BACK_DISPOSITION_DEFAULT}. Either way it is not recommended to
298 * use this mode
299 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800300 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700301 @Deprecated
302 public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800303
304 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700305 * Deprecated flag.
306 *
307 * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
308 *
309 * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
310 * handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
311 * {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
312 * of this mode had not been well defined. In AOSP implementation running on devices
313 * that have navigation bar, specifying this flag could change the software back
314 * button to "Dismiss" icon no matter whether the software keyboard is shown or not,
315 * but there would be no easy way to restore the icon state even after IME lost the
316 * connection to the application. To avoid user confusions, do not specify this mode
317 * anyway
318 * @see #setBackDisposition(int)
Joe Onorato857fd9b2011-01-27 15:08:35 -0800319 */
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700320 @Deprecated
321 public static final int BACK_DISPOSITION_WILL_DISMISS = 2;
322
323 /**
324 * Asks the system to not adjust the back button affordance even when the software keyboard is
325 * shown.
326 *
327 * <p>This mode is useful for UI modes where IME's main soft input window is used for some
328 * supplemental UI, such as floating candidate window for languages such as Chinese and
329 * Japanese, where users expect the back button is, or at least looks to be, handled by the
330 * target application rather than the UI shown by the IME even while {@link #isInputViewShown()}
331 * returns {@code true}.</p>
332 *
333 * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
334 * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
335 * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
336 * not take this mode into account.</p>
337 *
338 * @see #setBackDisposition(int)
339 */
340 public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
341
342 /**
343 * Enum flag to be used for {@link #setBackDisposition(int)}.
344 *
345 * @hide
346 */
347 @Retention(SOURCE)
348 @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
349 BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
350 prefix = "BACK_DISPOSITION_")
351 public @interface BackDispositionMode {}
Joe Onorato857fd9b2011-01-27 15:08:35 -0800352
353 /**
354 * @hide
355 * The IME is active. It may or may not be visible.
356 */
357 public static final int IME_ACTIVE = 0x1;
358
359 /**
360 * @hide
361 * The IME is visible.
362 */
363 public static final int IME_VISIBLE = 0x2;
364
Tarandeep Singheadb1392018-11-09 18:15:57 +0100365 /**
366 * @hide
367 * The IME is active and ready with views but set invisible.
368 * This flag cannot be combined with {@link #IME_VISIBLE}.
369 */
370 public static final int IME_INVISIBLE = 0x4;
371
Tarandeep Singh3fecef12018-01-22 14:33:33 -0800372 // Min and max values for back disposition.
373 private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700374 private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
Tarandeep Singh3fecef12018-01-22 14:33:33 -0800375
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 InputMethodManager mImm;
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700377 private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
Yohei Yukawac54c1172018-09-06 11:39:50 -0700378
Mathew Inwood31755f92018-12-20 13:53:36 +0000379 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Dianne Hackbornd922ae02011-01-14 11:43:24 -0800380 int mTheme = 0;
The Android Open Source Project10592532009-03-18 17:39:46 -0700381
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 LayoutInflater mInflater;
The Android Open Source Project10592532009-03-18 17:39:46 -0700383 TypedArray mThemeAttrs;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100384 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 View mRootView;
386 SoftInputWindow mWindow;
387 boolean mInitialized;
Tarandeep Singheadb1392018-11-09 18:15:57 +0100388 boolean mViewsCreated;
389 // IME views visibility.
390 boolean mDecorViewVisible;
391 boolean mDecorViewWasVisible;
The Android Open Source Project10592532009-03-18 17:39:46 -0700392 boolean mInShowWindow;
Tarandeep Singheadb1392018-11-09 18:15:57 +0100393 // True if pre-rendering of IME views/window is supported.
394 boolean mCanPreRender;
395 // If IME is pre-rendered.
396 boolean mIsPreRendered;
397 // IME window visibility.
398 // Use (mDecorViewVisible && mWindowVisible) to check if IME is visible to the user.
399 boolean mWindowVisible;
400
The Android Open Source Project10592532009-03-18 17:39:46 -0700401 ViewGroup mFullscreenArea;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 FrameLayout mExtractFrame;
403 FrameLayout mCandidatesFrame;
404 FrameLayout mInputFrame;
405
406 IBinder mToken;
407
408 InputBinding mInputBinding;
409 InputConnection mInputConnection;
410 boolean mInputStarted;
411 boolean mInputViewStarted;
412 boolean mCandidatesViewStarted;
413 InputConnection mStartedInputConnection;
414 EditorInfo mInputEditorInfo;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 int mShowInputFlags;
417 boolean mShowInputRequested;
418 boolean mLastShowInputRequested;
419 int mCandidatesVisibility;
420 CompletionInfo[] mCurCompletions;
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 boolean mFullscreenApplied;
423 boolean mIsFullscreen;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100424 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 View mExtractView;
The Android Open Source Project10592532009-03-18 17:39:46 -0700426 boolean mExtractViewHidden;
Mathew Inwood1dd7d112018-07-31 14:53:29 +0100427 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 ExtractEditText mExtractEditText;
429 ViewGroup mExtractAccessories;
Mark Renouf91eb2652016-04-11 16:03:26 -0400430 View mExtractAction;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 ExtractedText mExtractedText;
432 int mExtractedToken;
433
434 View mInputView;
435 boolean mIsInputViewShown;
436
437 int mStatusIcon;
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700438
439 @BackDispositionMode
Joe Onorato857fd9b2011-01-27 15:08:35 -0800440 int mBackDisposition;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441
Yohei Yukawac07fd4c2018-09-11 11:37:13 -0700442 private Object mLock = new Object();
443 @GuardedBy("mLock")
444 private boolean mNotifyUserActionSent;
445
Mathew Inwood31755f92018-12-20 13:53:36 +0000446 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 final Insets mTmpInsets = new Insets();
448 final int[] mTmpLocation = new int[2];
satokab751aa2010-09-14 19:17:36 +0900449
Adam Hebc67f2e2019-11-13 14:34:56 -0800450 @Nullable
451 private InlineSuggestionsRequestInfo mInlineSuggestionsRequestInfo = null;
452
Adam Hebc67f2e2019-11-13 14:34:56 -0800453 private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
454
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800455 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
Vinit Nayak31595bc2019-09-30 15:04:19 -0700456 onComputeInsets(mTmpInsets);
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800457 if (isExtractViewShown()) {
458 // In true fullscreen mode, we just say the window isn't covering
459 // any content so we don't impact whatever is behind.
460 View decor = getWindow().getWindow().getDecorView();
461 info.contentInsets.top = info.visibleInsets.top = decor.getHeight();
462 info.touchableRegion.setEmpty();
463 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
464 } else {
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800465 info.contentInsets.top = mTmpInsets.contentTopInsets;
466 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
467 info.touchableRegion.set(mTmpInsets.touchableRegion);
468 info.setTouchableInsets(mTmpInsets.touchableInsets);
469 }
Vinit Nayak31595bc2019-09-30 15:04:19 -0700470
471 if (mInputFrame != null) {
472 setImeExclusionRect(mTmpInsets.visibleTopInsets);
473 }
Yohei Yukawa623e94b2018-01-14 16:30:59 -0800474 };
475
476 final View.OnClickListener mActionClickListener = v -> {
477 final EditorInfo ei = getCurrentInputEditorInfo();
478 final InputConnection ic = getCurrentInputConnection();
479 if (ei != null && ic != null) {
480 if (ei.actionId != 0) {
481 ic.performEditorAction(ei.actionId);
482 } else if ((ei.imeOptions & EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE) {
483 ic.performEditorAction(ei.imeOptions & EditorInfo.IME_MASK_ACTION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 }
485 }
486 };
487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 /**
489 * Concrete implementation of
490 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
491 * all of the standard behavior for an input method.
492 */
493 public class InputMethodImpl extends AbstractInputMethodImpl {
494 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700495 * {@inheritDoc}
Yohei Yukawac54c1172018-09-06 11:39:50 -0700496 * @hide
497 */
498 @MainThread
499 @Override
lumarke0af3942019-05-29 17:11:57 +0800500 public final void initializeInternal(@NonNull IBinder token, int displayId,
Yohei Yukawac54c1172018-09-06 11:39:50 -0700501 IInputMethodPrivilegedOperations privilegedOperations) {
lumarke0af3942019-05-29 17:11:57 +0800502 if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
503 Log.w(TAG, "The token has already registered, ignore this initialization.");
504 return;
505 }
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700506 mPrivOps.set(privilegedOperations);
Yohei Yukawad746a7e2018-09-18 18:55:02 -0700507 InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
lumark90120a82018-08-15 00:33:03 +0800508 updateInputMethodDisplay(displayId);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700509 attachToken(token);
510 }
511
512 /**
513 * {@inheritDoc}
Adam Hebc67f2e2019-11-13 14:34:56 -0800514 * @hide
515 */
516 @MainThread
517 @Override
518 public void onCreateInlineSuggestionsRequest(ComponentName componentName,
519 AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
Joanne Chungd9a916f2020-01-02 19:03:16 +0800520 if (DEBUG) {
521 Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
522 }
Adam Hebc67f2e2019-11-13 14:34:56 -0800523 handleOnCreateInlineSuggestionsRequest(componentName, autofillId, cb);
524 }
525
526 /**
527 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700529 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700530 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 public void attachToken(IBinder token) {
Yohei Yukawa674cc4b2018-09-06 10:47:15 -0700532 if (mToken != null) {
533 throw new IllegalStateException(
534 "attachToken() must be called at most once. token=" + token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 }
Yohei Yukawa674cc4b2018-09-06 10:47:15 -0700536 mToken = token;
537 mWindow.setToken(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 }
Tarandeep Singhd8d03a82017-11-28 13:35:32 -0800539
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700541 * {@inheritDoc}
lumark90120a82018-08-15 00:33:03 +0800542 * @hide
543 */
544 @MainThread
545 @Override
546 public void updateInputMethodDisplay(int displayId) {
547 // Update display for adding IME window to the right display.
548 if (displayId != DEFAULT_DISPLAY) {
549 // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
550 // for update resources & configuration correctly when show soft input
551 // in non-default display.
552 updateDisplay(displayId);
553 }
554 }
555
556 /**
557 * {@inheritDoc}
Yohei Yukawa16f04072017-10-18 20:19:43 -0700558 *
559 * <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700561 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700562 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 public void bindInput(InputBinding binding) {
564 mInputBinding = binding;
565 mInputConnection = binding.getConnection();
566 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
567 + " ic=" + mInputConnection);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700568 reportFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 initialize();
570 onBindInput();
571 }
572
573 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700574 * {@inheritDoc}
575 *
576 * <p>Calls {@link InputMethodService#onUnbindInput()} when done.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700578 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700579 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 public void unbindInput() {
581 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
582 + " ic=" + mInputConnection);
583 onUnbindInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 mInputBinding = null;
585 mInputConnection = null;
586 }
587
Yohei Yukawa16f04072017-10-18 20:19:43 -0700588 /**
589 * {@inheritDoc}
590 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700591 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700592 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 public void startInput(InputConnection ic, EditorInfo attribute) {
594 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
595 doStartInput(ic, attribute, false);
596 }
597
Yohei Yukawa16f04072017-10-18 20:19:43 -0700598 /**
599 * {@inheritDoc}
600 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700601 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700602 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 public void restartInput(InputConnection ic, EditorInfo attribute) {
604 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
605 doStartInput(ic, attribute, true);
606 }
607
608 /**
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800609 * {@inheritDoc}
610 * @hide
611 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700612 @MainThread
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800613 @Override
Tarandeep Singheadb1392018-11-09 18:15:57 +0100614 public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800615 @NonNull EditorInfo editorInfo, boolean restarting,
Tarandeep Singheadb1392018-11-09 18:15:57 +0100616 @NonNull IBinder startInputToken, boolean shouldPreRenderIme) {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700617 mPrivOps.reportStartInput(startInputToken);
Tarandeep Singheadb1392018-11-09 18:15:57 +0100618 mCanPreRender = shouldPreRenderIme;
619 if (DEBUG) Log.v(TAG, "Will Pre-render IME: " + mCanPreRender);
620
621 if (restarting) {
622 restartInput(inputConnection, editorInfo);
623 } else {
624 startInput(inputConnection, editorInfo);
625 }
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800626 }
627
628 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700629 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700631 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700632 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -0800633 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 if (DEBUG) Log.v(TAG, "hideSoftInput()");
Tarandeep Singheadb1392018-11-09 18:15:57 +0100635 final boolean wasVisible = mIsPreRendered
636 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700637 applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */);
Tarandeep Singheadb1392018-11-09 18:15:57 +0100638 if (mIsPreRendered) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100639 if (DEBUG) {
640 Log.v(TAG, "Making IME window invisible");
641 }
642 setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
643 onPreRenderedWindowVisibilityChanged(false /* setVisible */);
644 } else {
645 mShowInputFlags = 0;
646 mShowInputRequested = false;
647 doHideWindow();
648 }
649 final boolean isVisible = mIsPreRendered
650 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
651 final boolean visibilityChanged = isVisible != wasVisible;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800652 if (resultReceiver != null) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100653 resultReceiver.send(visibilityChanged
The Android Open Source Project4df24232009-03-05 14:34:35 -0800654 ? InputMethodManager.RESULT_HIDDEN
Tarandeep Singheadb1392018-11-09 18:15:57 +0100655 : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
The Android Open Source Project4df24232009-03-05 14:34:35 -0800656 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
657 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659
660 /**
Yohei Yukawa16f04072017-10-18 20:19:43 -0700661 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700663 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700664 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -0800665 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700666 if (DEBUG) Log.v(TAG, "showSoftInput()");
Tarandeep Singheadb1392018-11-09 18:15:57 +0100667 final boolean wasVisible = mIsPreRendered
668 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
Yohei Yukawaef5b4652016-04-03 22:50:11 -0700669 if (dispatchOnShowInputRequested(flags, false)) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100670 if (mIsPreRendered) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100671 if (DEBUG) {
672 Log.v(TAG, "Making IME window visible");
673 }
674 onPreRenderedWindowVisibilityChanged(true /* setVisible */);
675 } else {
676 showWindow(true);
677 }
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700678 applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 }
satok865b9772011-01-21 02:45:06 +0900680 // If user uses hard keyboard, IME button should always be shown.
Tarandeep Singheadb1392018-11-09 18:15:57 +0100681 setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
682 final boolean isVisible = mIsPreRendered
683 ? mDecorViewVisible && mWindowVisible : isInputViewShown();
684 final boolean visibilityChanged = isVisible != wasVisible;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800685 if (resultReceiver != null) {
Tarandeep Singheadb1392018-11-09 18:15:57 +0100686 resultReceiver.send(visibilityChanged
The Android Open Source Project4df24232009-03-05 14:34:35 -0800687 ? InputMethodManager.RESULT_SHOWN
Tarandeep Singheadb1392018-11-09 18:15:57 +0100688 : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
The Android Open Source Project4df24232009-03-05 14:34:35 -0800689 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
690 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 }
satokab751aa2010-09-14 19:17:36 +0900692
Yohei Yukawa16f04072017-10-18 20:19:43 -0700693 /**
694 * {@inheritDoc}
695 */
Yohei Yukawa930328c2017-10-18 20:19:53 -0700696 @MainThread
Yohei Yukawa16f04072017-10-18 20:19:43 -0700697 @Override
satokab751aa2010-09-14 19:17:36 +0900698 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
Yohei Yukawac07fd4c2018-09-11 11:37:13 -0700699 dispatchOnCurrentInputMethodSubtypeChanged(subtype);
satokab751aa2010-09-14 19:17:36 +0900700 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 }
satokab751aa2010-09-14 19:17:36 +0900702
Adam Hebc67f2e2019-11-13 14:34:56 -0800703 // TODO(b/137800469): Add detailed docs explaining the inline suggestions process.
704 /**
705 * Returns an {@link InlineSuggestionsRequest} to be sent to Autofill.
706 *
707 * <p>Should be implemented by subclasses.</p>
708 */
709 public @Nullable InlineSuggestionsRequest onCreateInlineSuggestionsRequest() {
710 return null;
711 }
712
713 /**
714 * Called when Autofill responds back with {@link InlineSuggestionsResponse} containing
715 * inline suggestions.
716 *
717 * <p>Should be implemented by subclasses.</p>
718 *
719 * @param response {@link InlineSuggestionsResponse} passed back by Autofill.
720 * @return Whether the IME will use and render the inline suggestions.
721 */
722 public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
723 return false;
724 }
725
726 /**
Adam Hebc67f2e2019-11-13 14:34:56 -0800727 * Sends an {@link InlineSuggestionsRequest} obtained from
728 * {@link #onCreateInlineSuggestionsRequest()} to the current Autofill Session through
729 * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
730 */
731 private void makeInlineSuggestionsRequest() {
732 if (mInlineSuggestionsRequestInfo == null) {
733 Log.w(TAG, "makeInlineSuggestionsRequest() called with null requestInfo cache");
734 return;
735 }
736
737 final IInlineSuggestionsRequestCallback requestCallback =
738 mInlineSuggestionsRequestInfo.mCallback;
739 try {
740 final InlineSuggestionsRequest request = onCreateInlineSuggestionsRequest();
741 if (request == null) {
742 Log.w(TAG, "onCreateInlineSuggestionsRequest() returned null request");
743 requestCallback.onInlineSuggestionsUnsupported();
744 } else {
Feng Caoec3f84a2019-12-23 12:46:58 -0800745 final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
746 new InlineSuggestionsResponseCallbackImpl(this,
747 mInlineSuggestionsRequestInfo.mComponentName,
748 mInlineSuggestionsRequestInfo.mFocusedId);
Adam Hebc67f2e2019-11-13 14:34:56 -0800749 requestCallback.onInlineSuggestionsRequest(request,
Feng Caoec3f84a2019-12-23 12:46:58 -0800750 inlineSuggestionsResponseCallback);
Adam Hebc67f2e2019-11-13 14:34:56 -0800751 }
752 } catch (RemoteException e) {
753 Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
754 }
755 }
756
757 private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
758 @NonNull AutofillId autofillId, @NonNull IInlineSuggestionsRequestCallback callback) {
Adam Hebc67f2e2019-11-13 14:34:56 -0800759 if (!mInputStarted) {
Joanne Chungd9a916f2020-01-02 19:03:16 +0800760 try {
761 Log.w(TAG, "onStartInput() not called yet");
762 callback.onInlineSuggestionsUnsupported();
763 } catch (RemoteException e) {
764 Log.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
765 }
Adam Hebc67f2e2019-11-13 14:34:56 -0800766 return;
767 }
768
Adam He7bc8f602019-12-12 17:00:34 -0800769 mInlineSuggestionsRequestInfo = new InlineSuggestionsRequestInfo(componentName, autofillId,
770 callback);
771
Adam Hebc67f2e2019-11-13 14:34:56 -0800772 makeInlineSuggestionsRequest();
773 }
774
775 private void handleOnInlineSuggestionsResponse(@NonNull ComponentName componentName,
776 @NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse response) {
777 if (!mInlineSuggestionsRequestInfo.validate(componentName)) {
Joanne Chungd9a916f2020-01-02 19:03:16 +0800778 if (DEBUG) {
779 Log.d(TAG,
780 "Response component=" + componentName + " differs from request component="
781 + mInlineSuggestionsRequestInfo.mComponentName
782 + ", ignoring response");
783 }
Adam Hebc67f2e2019-11-13 14:34:56 -0800784 return;
785 }
786 onInlineSuggestionsResponse(response);
787 }
788
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800789 private void notifyImeHidden() {
790 setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
791 onPreRenderedWindowVisibilityChanged(false /* setVisible */);
792 }
793
Yohei Yukawac54c1172018-09-06 11:39:50 -0700794 private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -0700795 mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
Yohei Yukawac54c1172018-09-06 11:39:50 -0700796 }
797
Vinit Nayak31595bc2019-09-30 15:04:19 -0700798 /** Set region of the keyboard to be avoided from back gesture */
799 private void setImeExclusionRect(int visibleTopInsets) {
800 View inputFrameRootView = mInputFrame.getRootView();
801 Rect r = new Rect(0, visibleTopInsets, inputFrameRootView.getWidth(),
802 inputFrameRootView.getHeight());
803 inputFrameRootView.setSystemGestureExclusionRects(Collections.singletonList(r));
804 }
805
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 /**
Adam Hebc67f2e2019-11-13 14:34:56 -0800807 * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
808 */
809 private static final class InlineSuggestionsResponseCallbackImpl
810 extends IInlineSuggestionsResponseCallback.Stub {
811 private final WeakReference<InputMethodService> mInputMethodService;
812
813 private final ComponentName mRequestComponentName;
814 private final AutofillId mRequestAutofillId;
815
816 private InlineSuggestionsResponseCallbackImpl(InputMethodService inputMethodService,
817 ComponentName componentName, AutofillId autofillId) {
818 mInputMethodService = new WeakReference<>(inputMethodService);
819 mRequestComponentName = componentName;
820 mRequestAutofillId = autofillId;
821 }
822
823 @Override
824 public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
825 throws RemoteException {
826 final InputMethodService service = mInputMethodService.get();
827 if (service != null) {
828 service.mHandler.sendMessage(obtainMessage(
829 InputMethodService::handleOnInlineSuggestionsResponse, service,
830 mRequestComponentName, mRequestAutofillId, response));
831 }
832 }
833 }
834
835 /**
836 * Information about incoming requests from Autofill Frameworks for inline suggestions.
837 */
838 private static final class InlineSuggestionsRequestInfo {
839 final ComponentName mComponentName;
840 final AutofillId mFocusedId;
841 final IInlineSuggestionsRequestCallback mCallback;
842
843 InlineSuggestionsRequestInfo(ComponentName componentName, AutofillId focusedId,
844 IInlineSuggestionsRequestCallback callback) {
845 this.mComponentName = componentName;
846 this.mFocusedId = focusedId;
847 this.mCallback = callback;
848 }
849
850 /**
851 * Returns whether the cached {@link ComponentName} matches the passed in activity.
852 */
853 public boolean validate(ComponentName componentName) {
854 final boolean result = componentName.equals(mComponentName);
Joanne Chungd9a916f2020-01-02 19:03:16 +0800855 if (DEBUG && !result) {
Adam Hebc67f2e2019-11-13 14:34:56 -0800856 Log.d(TAG, "Cached request info ComponentName=" + mComponentName
857 + " differs from received ComponentName=" + componentName);
858 }
859 return result;
860 }
861 }
862
863 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 * Concrete implementation of
865 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
866 * all of the standard behavior for an input method session.
867 */
868 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
869 public void finishInput() {
870 if (!isEnabled()) {
871 return;
872 }
873 if (DEBUG) Log.v(TAG, "finishInput() in " + this);
874 doFinishInput();
875 }
876
877 /**
878 * Call {@link InputMethodService#onDisplayCompletions
879 * InputMethodService.onDisplayCompletions()}.
880 */
881 public void displayCompletions(CompletionInfo[] completions) {
882 if (!isEnabled()) {
883 return;
884 }
885 mCurCompletions = completions;
886 onDisplayCompletions(completions);
887 }
888
889 /**
890 * Call {@link InputMethodService#onUpdateExtractedText
891 * InputMethodService.onUpdateExtractedText()}.
892 */
893 public void updateExtractedText(int token, ExtractedText text) {
894 if (!isEnabled()) {
895 return;
896 }
897 onUpdateExtractedText(token, text);
898 }
899
900 /**
901 * Call {@link InputMethodService#onUpdateSelection
902 * InputMethodService.onUpdateSelection()}.
903 */
904 public void updateSelection(int oldSelStart, int oldSelEnd,
905 int newSelStart, int newSelEnd,
906 int candidatesStart, int candidatesEnd) {
907 if (!isEnabled()) {
908 return;
909 }
910 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
911 newSelStart, newSelEnd, candidatesStart, candidatesEnd);
912 }
satok863fcd62011-06-21 17:38:02 +0900913
914 @Override
915 public void viewClicked(boolean focusChanged) {
916 if (!isEnabled()) {
917 return;
918 }
919 InputMethodService.this.onViewClicked(focusChanged);
920 }
921
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 /**
923 * Call {@link InputMethodService#onUpdateCursor
924 * InputMethodService.onUpdateCursor()}.
925 */
926 public void updateCursor(Rect newCursor) {
927 if (!isEnabled()) {
928 return;
929 }
930 InputMethodService.this.onUpdateCursor(newCursor);
931 }
932
933 /**
934 * Call {@link InputMethodService#onAppPrivateCommand
935 * InputMethodService.onAppPrivateCommand()}.
936 */
937 public void appPrivateCommand(String action, Bundle data) {
938 if (!isEnabled()) {
939 return;
940 }
941 InputMethodService.this.onAppPrivateCommand(action, data);
942 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800943
944 /**
945 *
946 */
947 public void toggleSoftInput(int showFlags, int hideFlags) {
948 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
949 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900950
951 /**
952 * Call {@link InputMethodService#onUpdateCursorAnchorInfo
953 * InputMethodService.onUpdateCursorAnchorInfo()}.
954 */
955 public void updateCursorAnchorInfo(CursorAnchorInfo info) {
956 if (!isEnabled()) {
957 return;
958 }
959 InputMethodService.this.onUpdateCursorAnchorInfo(info);
960 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800961
962 /**
963 * Notify IME that window is hidden.
964 * @hide
965 */
966 public final void notifyImeHidden() {
967 InputMethodService.this.notifyImeHidden();
968 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 }
970
971 /**
972 * Information about where interesting parts of the input method UI appear.
973 */
974 public static final class Insets {
975 /**
976 * This is the top part of the UI that is the main content. It is
977 * used to determine the basic space needed, to resize/pan the
978 * application behind. It is assumed that this inset does not
979 * change very much, since any change will cause a full resize/pan
980 * of the application behind. This value is relative to the top edge
981 * of the input method window.
982 */
983 public int contentTopInsets;
984
985 /**
986 * This is the top part of the UI that is visibly covering the
987 * application behind it. This provides finer-grained control over
988 * visibility, allowing you to change it relatively frequently (such
989 * as hiding or showing candidates) without disrupting the underlying
990 * UI too much. For example, this will never resize the application
991 * UI, will only pan if needed to make the current focus visible, and
992 * will not aggressively move the pan position when this changes unless
993 * needed to make the focus visible. This value is relative to the top edge
994 * of the input method window.
995 */
996 public int visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -0800997
998 /**
999 * This is the region of the UI that is touchable. It is used when
1000 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
1001 * The region should be specified relative to the origin of the window frame.
1002 */
1003 public final Region touchableRegion = new Region();
1004
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 /**
1006 * Option for {@link #touchableInsets}: the entire window frame
1007 * can be touched.
1008 */
1009 public static final int TOUCHABLE_INSETS_FRAME
1010 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
1011
1012 /**
1013 * Option for {@link #touchableInsets}: the area inside of
1014 * the content insets can be touched.
1015 */
1016 public static final int TOUCHABLE_INSETS_CONTENT
1017 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
1018
1019 /**
1020 * Option for {@link #touchableInsets}: the area inside of
1021 * the visible insets can be touched.
1022 */
1023 public static final int TOUCHABLE_INSETS_VISIBLE
1024 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001025
1026 /**
1027 * Option for {@link #touchableInsets}: the region specified by
1028 * {@link #touchableRegion} can be touched.
1029 */
1030 public static final int TOUCHABLE_INSETS_REGION
1031 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
1032
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 /**
1034 * Determine which area of the window is touchable by the user. May
1035 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
Jeff Brownfbf09772011-01-16 14:06:57 -08001036 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
1037 * or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 */
1039 public int touchableInsets;
1040 }
satok865b9772011-01-21 02:45:06 +09001041
The Android Open Source Project10592532009-03-18 17:39:46 -07001042 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001043 * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
1044 *
1045 * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
1046 * Basically this functionality still needs to be considered as implementation details.</p>
1047 */
1048 @MainThread
1049 private static final class SettingsObserver extends ContentObserver {
1050 @Retention(RetentionPolicy.SOURCE)
1051 @IntDef({
1052 ShowImeWithHardKeyboardType.UNKNOWN,
1053 ShowImeWithHardKeyboardType.FALSE,
1054 ShowImeWithHardKeyboardType.TRUE,
1055 })
1056 private @interface ShowImeWithHardKeyboardType {
1057 int UNKNOWN = 0;
1058 int FALSE = 1;
1059 int TRUE = 2;
1060 }
1061 @ShowImeWithHardKeyboardType
1062 private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
1063
1064 private final InputMethodService mService;
1065
1066 private SettingsObserver(InputMethodService service) {
1067 super(new Handler(service.getMainLooper()));
1068 mService = service;
1069 }
1070
1071 /**
1072 * A factory method that internally enforces two-phase initialization to make sure that the
1073 * object reference will not be escaped until the object is properly constructed.
1074 *
1075 * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread. Hence
1076 * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
1077 *
1078 * @param service {@link InputMethodService} that needs to receive the callback.
1079 * @return {@link SettingsObserver} that is already registered to
1080 * {@link android.content.ContentResolver}. The caller must call
1081 * {@link SettingsObserver#unregister()}.
1082 */
1083 public static SettingsObserver createAndRegister(InputMethodService service) {
1084 final SettingsObserver observer = new SettingsObserver(service);
1085 // The observer is properly constructed. Let's start accepting the event.
1086 service.getContentResolver().registerContentObserver(
1087 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
1088 false, observer);
1089 return observer;
1090 }
1091
1092 void unregister() {
1093 mService.getContentResolver().unregisterContentObserver(this);
1094 }
1095
Mathew Inwood1dd7d112018-07-31 14:53:29 +01001096 @UnsupportedAppUsage
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001097 private boolean shouldShowImeWithHardKeyboard() {
1098 // Lazily initialize as needed.
1099 if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
1100 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
1101 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
1102 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
1103 }
1104 switch (mShowImeWithHardKeyboard) {
1105 case ShowImeWithHardKeyboardType.TRUE:
1106 return true;
1107 case ShowImeWithHardKeyboardType.FALSE:
1108 return false;
1109 default:
1110 Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
1111 return false;
1112 }
1113 }
1114
1115 @Override
1116 public void onChange(boolean selfChange, Uri uri) {
1117 final Uri showImeWithHardKeyboardUri =
1118 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
1119 if (showImeWithHardKeyboardUri.equals(uri)) {
1120 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
1121 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
1122 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001123 // In Android M and prior, state change of
1124 // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered
1125 // #onConfigurationChanged(). For compatibility reasons, we reset the internal
1126 // state as if configuration was changed.
1127 mService.resetStateForNewConfiguration();
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001128 }
1129 }
1130
1131 @Override
1132 public String toString() {
1133 return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}";
1134 }
1135 }
Mathew Inwood1dd7d112018-07-31 14:53:29 +01001136 @UnsupportedAppUsage
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001137 private SettingsObserver mSettingsObserver;
1138
1139 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001140 * You can call this to customize the theme used by your IME's window.
1141 * This theme should typically be one that derives from
1142 * {@link android.R.style#Theme_InputMethod}, which is the default theme
1143 * you will get. This must be set before {@link #onCreate}, so you
1144 * will typically call it in your constructor with the resource ID
1145 * of your custom theme.
1146 */
satokab751aa2010-09-14 19:17:36 +09001147 @Override
The Android Open Source Project10592532009-03-18 17:39:46 -07001148 public void setTheme(int theme) {
1149 if (mWindow != null) {
1150 throw new IllegalStateException("Must be called before onCreate()");
1151 }
1152 mTheme = theme;
1153 }
satok865b9772011-01-21 02:45:06 +09001154
Dianne Hackborn836531b2012-08-01 19:00:38 -07001155 /**
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001156 * You can call this to try to enable accelerated drawing for your IME. This must be set before
1157 * {@link #onCreate()}, so you will typically call it in your constructor. It is not always
1158 * possible to use hardware accelerated drawing in an IME (for example on low-end devices that
1159 * do not have the resources to support this), so the call {@code true} if it succeeds otherwise
1160 * {@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 -07001161 *
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001162 * <p>In API 21 and later, system may automatically enable hardware accelerated drawing for your
1163 * IME on capable devices even if this method is not explicitly called. Make sure that your IME
1164 * is able to handle either case.</p>
1165 *
1166 * @return {@code true} if accelerated drawing is successfully enabled otherwise {@code false}.
1167 * On API 21 and later devices the return value is basically just a hint and your IME
1168 * does not need to change the behavior based on the it
1169 * @deprecated Starting in API 21, hardware acceleration is always enabled on capable devices
Dianne Hackborn836531b2012-08-01 19:00:38 -07001170 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001171 @Deprecated
Dianne Hackborn836531b2012-08-01 19:00:38 -07001172 public boolean enableHardwareAcceleration() {
1173 if (mWindow != null) {
1174 throw new IllegalStateException("Must be called before onCreate()");
1175 }
Yohei Yukawaac0211a2016-09-01 12:41:58 -07001176 return ActivityManager.isHighEndGfx();
Dianne Hackborn836531b2012-08-01 19:00:38 -07001177 }
1178
Alan Viverette5effd7e2014-05-05 12:25:33 -07001179 @Override public void onCreate() {
1180 mTheme = Resources.selectSystemTheme(mTheme,
1181 getApplicationInfo().targetSdkVersion,
1182 android.R.style.Theme_InputMethod,
1183 android.R.style.Theme_Holo_InputMethod,
1184 android.R.style.Theme_DeviceDefault_InputMethod,
1185 android.R.style.Theme_DeviceDefault_InputMethod);
The Android Open Source Project10592532009-03-18 17:39:46 -07001186 super.setTheme(mTheme);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 super.onCreate();
1188 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001189 mSettingsObserver = SettingsObserver.createAndRegister(this);
lumark90120a82018-08-15 00:33:03 +08001190 // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
1191 // for update resources & configuration correctly when show soft input
1192 // in non-default display.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 mInflater = (LayoutInflater)getSystemService(
1194 Context.LAYOUT_INFLATER_SERVICE);
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001195 mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
Dianne Hackborne30e02f2014-05-27 18:24:45 -07001196 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
Tiger Huang4a7835f2019-11-06 00:07:56 +08001197 mWindow.getWindow().setFitWindowInsetsTypes(WindowInsets.Type.systemBars());
1198 mWindow.getWindow().addPrivateFlags(PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND);
1199 mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
1200 (v, insets) -> v.onApplyWindowInsets(
1201 new WindowInsets.Builder(insets).setSystemWindowInsets(
1202 android.graphics.Insets.of(
1203 insets.getSystemWindowInsetLeft(),
1204 insets.getSystemWindowInsetTop(),
1205 insets.getSystemWindowInsetRight(),
1206 insets.getStableInsetBottom())).build()));
Yohei Yukawa8f162c62018-01-10 13:18:09 -08001207 // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
1208 // by default (but IME developers can opt this out later if they want a new behavior).
1209 mWindow.getWindow().setFlags(
1210 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
1211
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 initViews();
Romain Guy980a9382010-01-08 15:06:28 -08001213 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 }
satokab751aa2010-09-14 19:17:36 +09001215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 /**
1217 * This is a hook that subclasses can use to perform initialization of
1218 * their interface. It is called for you prior to any of your UI objects
1219 * being created, both after the service is first created and after a
1220 * configuration change happens.
1221 */
1222 public void onInitializeInterface() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001223 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 }
satokab751aa2010-09-14 19:17:36 +09001225
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 void initialize() {
1227 if (!mInitialized) {
1228 mInitialized = true;
1229 onInitializeInterface();
1230 }
1231 }
satokab751aa2010-09-14 19:17:36 +09001232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 void initViews() {
1234 mInitialized = false;
Tarandeep Singheadb1392018-11-09 18:15:57 +01001235 mViewsCreated = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 mShowInputRequested = false;
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001237 mShowInputFlags = 0;
1238
The Android Open Source Project10592532009-03-18 17:39:46 -07001239 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001240 mRootView = mInflater.inflate(
1241 com.android.internal.R.layout.input_method, null);
1242 mWindow.setContentView(mRootView);
Seonggoo Kang72745ff2014-12-24 13:55:50 +09001243 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
Jeff Sharkey6e2bee72012-10-01 13:39:08 -07001245 if (Settings.Global.getInt(getContentResolver(),
1246 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 mWindow.getWindow().setWindowAnimations(
1248 com.android.internal.R.style.Animation_InputMethodFancy);
1249 }
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001250 mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
The Android Open Source Project10592532009-03-18 17:39:46 -07001251 mExtractViewHidden = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001252 mExtractFrame = mRootView.findViewById(android.R.id.extractArea);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 mExtractView = null;
1254 mExtractEditText = null;
1255 mExtractAccessories = null;
1256 mExtractAction = null;
1257 mFullscreenApplied = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001258
1259 mCandidatesFrame = mRootView.findViewById(android.R.id.candidatesArea);
1260 mInputFrame = mRootView.findViewById(android.R.id.inputArea);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 mInputView = null;
1262 mIsInputViewShown = false;
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 mExtractFrame.setVisibility(View.GONE);
1265 mCandidatesVisibility = getCandidatesHiddenVisibility();
1266 mCandidatesFrame.setVisibility(mCandidatesVisibility);
1267 mInputFrame.setVisibility(View.GONE);
1268 }
satokab751aa2010-09-14 19:17:36 +09001269
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 @Override public void onDestroy() {
1271 super.onDestroy();
1272 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
1273 mInsetsComputer);
Satoshi Kataokac56191f2013-05-30 13:14:47 +09001274 doFinishInput();
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07001275 mWindow.dismissForDestroyIfNecessary();
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001276 if (mSettingsObserver != null) {
1277 mSettingsObserver.unregister();
1278 mSettingsObserver = null;
1279 }
Yohei Yukawaeec552e2018-09-09 20:48:41 -07001280 if (mToken != null) {
1281 // This is completely optional, but allows us to show more explicit error messages
1282 // when IME developers are doing something unsupported.
Yohei Yukawad746a7e2018-09-18 18:55:02 -07001283 InputMethodPrivilegedOperationsRegistry.remove(mToken);
Yohei Yukawaeec552e2018-09-09 20:48:41 -07001284 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 }
satokf17db9f2011-09-14 18:55:58 +09001286
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 /**
1288 * Take care of handling configuration changes. Subclasses of
1289 * InputMethodService generally don't need to deal directly with
1290 * this on their own; the standard implementation here takes care of
1291 * regenerating the input method UI as a result of the configuration
1292 * change, so you can rely on your {@link #onCreateInputView} and
1293 * other methods being called as appropriate due to a configuration change.
1294 *
1295 * <p>When a configuration change does happen,
1296 * {@link #onInitializeInterface()} is guaranteed to be called the next
1297 * time prior to any of the other input or UI creation callbacks. The
1298 * following will be called immediately depending if appropriate for current
1299 * state: {@link #onStartInput} if input is active, and
1300 * {@link #onCreateInputView} and {@link #onStartInputView} and related
1301 * appropriate functions if the UI is displayed.
1302 */
1303 @Override public void onConfigurationChanged(Configuration newConfig) {
1304 super.onConfigurationChanged(newConfig);
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001305 resetStateForNewConfiguration();
1306 }
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001307
Yohei Yukawa2dbc5322016-04-03 22:50:18 -07001308 private void resetStateForNewConfiguration() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01001309 boolean visible = mDecorViewVisible;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 int showFlags = mShowInputFlags;
1311 boolean showingInput = mShowInputRequested;
1312 CompletionInfo[] completions = mCurCompletions;
1313 initViews();
1314 mInputViewStarted = false;
1315 mCandidatesViewStarted = false;
1316 if (mInputStarted) {
1317 doStartInput(getCurrentInputConnection(),
1318 getCurrentInputEditorInfo(), true);
1319 }
1320 if (visible) {
1321 if (showingInput) {
1322 // If we were last showing the soft keyboard, try to do so again.
Yohei Yukawaef5b4652016-04-03 22:50:11 -07001323 if (dispatchOnShowInputRequested(showFlags, true)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 showWindow(true);
1325 if (completions != null) {
1326 mCurCompletions = completions;
1327 onDisplayCompletions(completions);
1328 }
1329 } else {
satok2f913d92012-05-10 01:48:03 +09001330 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001331 }
1332 } else if (mCandidatesVisibility == View.VISIBLE) {
1333 // If the candidates are currently visible, make sure the
1334 // window is shown for them.
1335 showWindow(false);
1336 } else {
1337 // Otherwise hide the window.
satok2f913d92012-05-10 01:48:03 +09001338 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 }
satok865b9772011-01-21 02:45:06 +09001340 // If user uses hard keyboard, IME button should always be shown.
Joe Onorato857fd9b2011-01-27 15:08:35 -08001341 boolean showing = onEvaluateInputViewShown();
Yohei Yukawac54c1172018-09-06 11:39:50 -07001342 setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 }
1344 }
1345
1346 /**
1347 * Implement to return our standard {@link InputMethodImpl}. Subclasses
1348 * can override to provide their own customized version.
1349 */
satokab751aa2010-09-14 19:17:36 +09001350 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 public AbstractInputMethodImpl onCreateInputMethodInterface() {
1352 return new InputMethodImpl();
1353 }
1354
1355 /**
1356 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
1357 * can override to provide their own customized version.
1358 */
satokab751aa2010-09-14 19:17:36 +09001359 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001360 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
1361 return new InputMethodSessionImpl();
1362 }
1363
1364 public LayoutInflater getLayoutInflater() {
1365 return mInflater;
1366 }
1367
1368 public Dialog getWindow() {
1369 return mWindow;
1370 }
Yohei Yukawa386f50e2018-03-14 13:03:42 -07001371
1372 /**
1373 * Sets the disposition mode that indicates the expected affordance for the back button.
1374 *
1375 * <p>Keep in mind that specifying this flag does not change the the default behavior of
1376 * {@link #onKeyDown(int, KeyEvent)}. It is IME developers' responsibility for making sure that
1377 * their custom implementation of {@link #onKeyDown(int, KeyEvent)} is consistent with the mode
1378 * specified to this API.</p>
1379 *
1380 * @see #getBackDisposition()
1381 * @param disposition disposition mode to be set
1382 */
1383 public void setBackDisposition(@BackDispositionMode int disposition) {
Tarandeep Singh3fecef12018-01-22 14:33:33 -08001384 if (disposition == mBackDisposition) {
1385 return;
1386 }
1387 if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
1388 Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
1389 return;
1390 }
Joe Onorato857fd9b2011-01-27 15:08:35 -08001391 mBackDisposition = disposition;
Tarandeep Singheadb1392018-11-09 18:15:57 +01001392 setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
Joe Onorato857fd9b2011-01-27 15:08:35 -08001393 }
1394
Yohei Yukawa386f50e2018-03-14 13:03:42 -07001395 /**
1396 * Retrieves the current disposition mode that indicates the expected back button affordance.
1397 *
1398 * @see #setBackDisposition(int)
1399 * @return currently selected disposition mode
1400 */
1401 @BackDispositionMode
Joe Onorato857fd9b2011-01-27 15:08:35 -08001402 public int getBackDisposition() {
1403 return mBackDisposition;
1404 }
1405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 /**
1407 * Return the maximum width, in pixels, available the input method.
1408 * Input methods are positioned at the bottom of the screen and, unless
1409 * running in fullscreen, will generally want to be as short as possible
1410 * so should compute their height based on their contents. However, they
1411 * can stretch as much as needed horizontally. The function returns to
1412 * you the maximum amount of space available horizontally, which you can
1413 * use if needed for UI placement.
1414 *
1415 * <p>In many cases this is not needed, you can just rely on the normal
1416 * view layout mechanisms to position your views within the full horizontal
1417 * space given to the input method.
1418 *
1419 * <p>Note that this value can change dynamically, in particular when the
1420 * screen orientation changes.
1421 */
1422 public int getMaxWidth() {
1423 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
1424 return wm.getDefaultDisplay().getWidth();
1425 }
1426
1427 /**
1428 * Return the currently active InputBinding for the input method, or
1429 * null if there is none.
1430 */
1431 public InputBinding getCurrentInputBinding() {
1432 return mInputBinding;
1433 }
1434
1435 /**
1436 * Retrieve the currently active InputConnection that is bound to
1437 * the input method, or null if there is none.
1438 */
1439 public InputConnection getCurrentInputConnection() {
1440 InputConnection ic = mStartedInputConnection;
1441 if (ic != null) {
1442 return ic;
1443 }
1444 return mInputConnection;
1445 }
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001446
1447 /**
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001448 * Force switch to the last used input method and subtype. If the last input method didn't have
1449 * any subtypes, the framework will simply switch to the last input method with no subtype
1450 * specified.
1451 * @return true if the current input method and subtype was successfully switched to the last
1452 * used input method and subtype.
1453 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001454 public final boolean switchToPreviousInputMethod() {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001455 return mPrivOps.switchToPreviousInputMethod();
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001456 }
1457
1458 /**
1459 * Force switch to the next input method and subtype. If there is no IME enabled except
1460 * current IME and subtype, do nothing.
1461 * @param onlyCurrentIme if true, the framework will find the next subtype which
1462 * belongs to the current IME
1463 * @return true if the current input method and subtype was successfully switched to the next
1464 * input method and subtype.
1465 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001466 public final boolean switchToNextInputMethod(boolean onlyCurrentIme) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001467 return mPrivOps.switchToNextInputMethod(onlyCurrentIme);
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001468 }
1469
1470 /**
1471 * Returns true if the current IME needs to offer the users ways to switch to a next input
1472 * method (e.g. a globe key.).
1473 * When an IME sets supportsSwitchingToNextInputMethod and this method returns true,
1474 * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly.
1475 * <p> Note that the system determines the most appropriate next input method
1476 * and subtype in order to provide the consistent user experience in switching
1477 * between IMEs and subtypes.
1478 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001479 public final boolean shouldOfferSwitchingToNextInputMethod() {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001480 return mPrivOps.shouldOfferSwitchingToNextInputMethod();
Tarandeep Singhd8d03a82017-11-28 13:35:32 -08001481 }
1482
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 public boolean getCurrentInputStarted() {
1484 return mInputStarted;
1485 }
1486
1487 public EditorInfo getCurrentInputEditorInfo() {
1488 return mInputEditorInfo;
1489 }
Yohei Yukawac54c1172018-09-06 11:39:50 -07001490
1491 private void reportFullscreenMode() {
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07001492 mPrivOps.reportFullscreenMode(mIsFullscreen);
Yohei Yukawac54c1172018-09-06 11:39:50 -07001493 }
1494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495 /**
1496 * Re-evaluate whether the input method should be running in fullscreen
1497 * mode, and update its UI if this has changed since the last time it
1498 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to
1499 * determine whether it should currently run in fullscreen mode. You
1500 * can use {@link #isFullscreenMode()} to determine if the input method
1501 * is currently running in fullscreen mode.
1502 */
1503 public void updateFullscreenMode() {
1504 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
1505 boolean changed = mLastShowInputRequested != mShowInputRequested;
1506 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
1507 changed = true;
1508 mIsFullscreen = isFullscreen;
Yohei Yukawac54c1172018-09-06 11:39:50 -07001509 reportFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 mFullscreenApplied = true;
1511 initialize();
The Android Open Source Project10592532009-03-18 17:39:46 -07001512 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1513 mFullscreenArea.getLayoutParams();
1514 if (isFullscreen) {
1515 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
1516 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
1517 lp.height = 0;
1518 lp.weight = 1;
1519 } else {
1520 mFullscreenArea.setBackgroundDrawable(null);
1521 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
1522 lp.weight = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001523 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001524 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
1525 mFullscreenArea, lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 if (isFullscreen) {
1527 if (mExtractView == null) {
1528 View v = onCreateExtractTextView();
1529 if (v != null) {
1530 setExtractView(v);
1531 }
1532 }
1533 startExtractingText(false);
1534 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001535 updateExtractFrameVisibility();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 }
1537
1538 if (changed) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001539 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 mLastShowInputRequested = mShowInputRequested;
1541 }
1542 }
1543
1544 /**
1545 * Update the given window's parameters for the given mode. This is called
1546 * when the window is first displayed and each time the fullscreen or
1547 * candidates only mode changes.
1548 *
1549 * <p>The default implementation makes the layout for the window
Romain Guy980a9382010-01-08 15:06:28 -08001550 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
1551 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 *
1553 * @param win The input method's window.
1554 * @param isFullscreen If true, the window is running in fullscreen mode
1555 * and intended to cover the entire application display.
1556 * @param isCandidatesOnly If true, the window is only showing the
1557 * candidates view and none of the rest of its UI. This is mutually
1558 * exclusive with fullscreen mode.
1559 */
1560 public void onConfigureWindow(Window win, boolean isFullscreen,
1561 boolean isCandidatesOnly) {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001562 final int currentHeight = mWindow.getWindow().getAttributes().height;
1563 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
1564 if (mIsInputViewShown && currentHeight != newHeight) {
Yohei Yukawad0332832017-02-01 13:59:43 -08001565 if (DEBUG) {
1566 Log.w(TAG,"Window size has been changed. This may cause jankiness of resizing "
1567 + "window: " + currentHeight + " -> " + newHeight);
1568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569 }
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001570 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001571 }
1572
1573 /**
1574 * Return whether the input method is <em>currently</em> running in
1575 * fullscreen mode. This is the mode that was last determined and
1576 * applied by {@link #updateFullscreenMode()}.
1577 */
1578 public boolean isFullscreenMode() {
1579 return mIsFullscreen;
1580 }
1581
1582 /**
1583 * Override this to control when the input method should run in
1584 * fullscreen mode. The default implementation runs in fullsceen only
1585 * when the screen is in landscape mode. If you change what
1586 * this returns, you will need to call {@link #updateFullscreenMode()}
1587 * yourself whenever the returned value may have changed to have it
1588 * re-evaluated and applied.
1589 */
1590 public boolean onEvaluateFullscreenMode() {
1591 Configuration config = getResources().getConfiguration();
Leon Scroggins2edd6822010-01-12 11:36:13 -05001592 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
1593 return false;
1594 }
1595 if (mInputEditorInfo != null
1596 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
1597 return false;
1598 }
1599 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001600 }
Gilles Debunne34703b62011-09-08 11:16:25 -07001601
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001602 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001603 * Controls the visibility of the extracted text area. This only applies
1604 * when the input method is in fullscreen mode, and thus showing extracted
1605 * text. When false, the extracted text will not be shown, allowing some
1606 * of the application to be seen behind. This is normally set for you
1607 * by {@link #onUpdateExtractingVisibility}. This controls the visibility
1608 * of both the extracted text and candidate view; the latter since it is
1609 * not useful if there is no text to see.
1610 */
1611 public void setExtractViewShown(boolean shown) {
1612 if (mExtractViewHidden == shown) {
1613 mExtractViewHidden = !shown;
1614 updateExtractFrameVisibility();
1615 }
1616 }
1617
1618 /**
1619 * Return whether the fullscreen extract view is shown. This will only
1620 * return true if {@link #isFullscreenMode()} returns true, and in that
1621 * case its value depends on the last call to
1622 * {@link #setExtractViewShown(boolean)}. This effectively lets you
1623 * determine if the application window is entirely covered (when this
1624 * returns true) or if some part of it may be shown (if this returns
1625 * false, though if {@link #isFullscreenMode()} returns true in that case
1626 * then it is probably only a sliver of the application).
1627 */
1628 public boolean isExtractViewShown() {
1629 return mIsFullscreen && !mExtractViewHidden;
1630 }
1631
1632 void updateExtractFrameVisibility() {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001633 final int vis;
The Android Open Source Project10592532009-03-18 17:39:46 -07001634 if (isFullscreenMode()) {
1635 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001636 // "vis" should be applied for the extract frame as well in the fullscreen mode.
1637 mExtractFrame.setVisibility(vis);
The Android Open Source Project10592532009-03-18 17:39:46 -07001638 } else {
1639 vis = View.VISIBLE;
1640 mExtractFrame.setVisibility(View.GONE);
1641 }
1642 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
Tarandeep Singheadb1392018-11-09 18:15:57 +01001643 if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001644 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1645 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1646 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1647 0);
1648 if (animRes != 0) {
1649 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1650 this, animRes));
1651 }
1652 }
1653 mFullscreenArea.setVisibility(vis);
1654 }
1655
1656 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 * Compute the interesting insets into your UI. The default implementation
1658 * uses the top of the candidates frame for the visible insets, and the
1659 * top of the input frame for the content insets. The default touchable
1660 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1661 *
The Android Open Source Project10592532009-03-18 17:39:46 -07001662 * <p>Note that this method is not called when
1663 * {@link #isExtractViewShown} returns true, since
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 * in that case the application is left as-is behind the input method and
1665 * not impacted by anything in its UI.
1666 *
1667 * @param outInsets Fill in with the current UI insets.
1668 */
1669 public void onComputeInsets(Insets outInsets) {
1670 int[] loc = mTmpLocation;
1671 if (mInputFrame.getVisibility() == View.VISIBLE) {
1672 mInputFrame.getLocationInWindow(loc);
1673 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001674 View decor = getWindow().getWindow().getDecorView();
1675 loc[1] = decor.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001677 if (isFullscreenMode()) {
1678 // In fullscreen mode, we never resize the underlying window.
1679 View decor = getWindow().getWindow().getDecorView();
1680 outInsets.contentTopInsets = decor.getHeight();
1681 } else {
1682 outInsets.contentTopInsets = loc[1];
1683 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1685 mCandidatesFrame.getLocationInWindow(loc);
1686 }
1687 outInsets.visibleTopInsets = loc[1];
1688 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001689 outInsets.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690 }
1691
1692 /**
1693 * Re-evaluate whether the soft input area should currently be shown, and
1694 * update its UI if this has changed since the last time it
1695 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to
1696 * determine whether the input view should currently be shown. You
1697 * can use {@link #isInputViewShown()} to determine if the input view
1698 * is currently shown.
1699 */
1700 public void updateInputViewShown() {
1701 boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
Tarandeep Singheadb1392018-11-09 18:15:57 +01001702 if (mIsInputViewShown != isShown && mDecorViewVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 mIsInputViewShown = isShown;
1704 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1705 if (mInputView == null) {
1706 initialize();
1707 View v = onCreateInputView();
1708 if (v != null) {
1709 setInputView(v);
1710 }
1711 }
1712 }
1713 }
1714
1715 /**
1716 * Returns true if we have been asked to show our input view.
1717 */
1718 public boolean isShowInputRequested() {
1719 return mShowInputRequested;
1720 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01001721
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 /**
1723 * Return whether the soft input view is <em>currently</em> shown to the
1724 * user. This is the state that was last determined and
1725 * applied by {@link #updateInputViewShown()}.
1726 */
1727 public boolean isInputViewShown() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01001728 return mCanPreRender ? mWindowVisible : mIsInputViewShown && mDecorViewVisible;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001730
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001731 /**
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001732 * Override this to control when the soft input area should be shown to the user. The default
1733 * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
1734 * unless the user shows an intention to use software keyboard. If you change what this
1735 * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
1736 * value may have changed to have it re-evaluated and applied.
1737 *
1738 * <p>When you override this method, it is recommended to call
1739 * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
1740 * returned.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 */
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001742 @CallSuper
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 public boolean onEvaluateInputViewShown() {
Yohei Yukawacf8403b2016-01-12 11:54:58 -08001744 if (mSettingsObserver == null) {
1745 Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
1746 return false;
1747 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001748 if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
1749 return true;
1750 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 Configuration config = getResources().getConfiguration();
1752 return config.keyboard == Configuration.KEYBOARD_NOKEYS
Ken Wakasa8710e762011-01-30 11:02:09 +09001753 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 }
Yohei Yukawa7b739a82015-12-21 13:30:44 -08001755
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001756 /**
1757 * Controls the visibility of the candidates display area. By default
1758 * it is hidden.
1759 */
1760 public void setCandidatesViewShown(boolean shown) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001761 updateCandidatesVisibility(shown);
Tarandeep Singheadb1392018-11-09 18:15:57 +01001762 if (!mShowInputRequested && mDecorViewVisible != shown) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 // If we are being asked to show the candidates view while the app
1764 // has not asked for the input view to be shown, then we need
1765 // to update whether the window is shown.
1766 if (shown) {
1767 showWindow(false);
1768 } else {
satok2f913d92012-05-10 01:48:03 +09001769 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 }
1771 }
1772 }
1773
The Android Open Source Project10592532009-03-18 17:39:46 -07001774 void updateCandidatesVisibility(boolean shown) {
1775 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1776 if (mCandidatesVisibility != vis) {
1777 mCandidatesFrame.setVisibility(vis);
1778 mCandidatesVisibility = vis;
1779 }
1780 }
1781
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 /**
1783 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1784 * or {@link View#GONE View.GONE}) of the candidates view when it is not
The Android Open Source Project10592532009-03-18 17:39:46 -07001785 * shown. The default implementation returns GONE when
1786 * {@link #isExtractViewShown} returns true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 * otherwise VISIBLE. Be careful if you change this to return GONE in
1788 * other situations -- if showing or hiding the candidates view causes
1789 * your window to resize, this can cause temporary drawing artifacts as
1790 * the resize takes place.
1791 */
1792 public int getCandidatesHiddenVisibility() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001793 return isExtractViewShown() ? View.GONE : View.INVISIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001795
Tor Norbye7b9c9122013-05-30 16:48:33 -07001796 public void showStatusIcon(@DrawableRes int iconResId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 mStatusIcon = iconResId;
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001798 mPrivOps.updateStatusIcon(getPackageName(), iconResId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001799 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001800
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 public void hideStatusIcon() {
1802 mStatusIcon = 0;
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001803 mPrivOps.updateStatusIcon(null, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001805
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001806 /**
1807 * Force switch to a new input method, as identified by <var>id</var>. This
1808 * input method will be destroyed, and the requested one started on the
1809 * current input field.
1810 *
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001811 * @param id Unique identifier of the new input method to start.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 */
1813 public void switchInputMethod(String id) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001814 mPrivOps.setInputMethod(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 }
Yohei Yukawa2b634342018-01-14 16:15:31 -08001816
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001817 /**
1818 * Force switch to a new input method, as identified by {@code id}. This
1819 * input method will be destroyed, and the requested one started on the
1820 * current input field.
1821 *
1822 * @param id Unique identifier of the new input method to start.
1823 * @param subtype The new subtype of the new input method to be switched to.
1824 */
Tarandeep Singh45136992018-03-08 10:52:03 -08001825 public final void switchInputMethod(String id, InputMethodSubtype subtype) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07001826 mPrivOps.setInputMethodAndSubtype(id, subtype);
Tarandeep Singh164cfba2018-02-28 14:17:43 -08001827 }
1828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 public void setExtractView(View view) {
1830 mExtractFrame.removeAllViews();
1831 mExtractFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001832 ViewGroup.LayoutParams.MATCH_PARENT,
1833 ViewGroup.LayoutParams.MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001834 mExtractView = view;
1835 if (view != null) {
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001836 mExtractEditText = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001837 com.android.internal.R.id.inputExtractEditText);
1838 mExtractEditText.setIME(this);
Mark Renouf91eb2652016-04-11 16:03:26 -04001839 mExtractAction = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 com.android.internal.R.id.inputExtractAction);
1841 if (mExtractAction != null) {
Yohei Yukawa31a260f2018-01-14 16:24:26 -08001842 mExtractAccessories = view.findViewById(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 com.android.internal.R.id.inputExtractAccessories);
1844 }
1845 startExtractingText(false);
1846 } else {
1847 mExtractEditText = null;
1848 mExtractAccessories = null;
1849 mExtractAction = null;
1850 }
1851 }
1852
1853 /**
1854 * Replaces the current candidates view with a new one. You only need to
1855 * call this when dynamically changing the view; normally, you should
1856 * implement {@link #onCreateCandidatesView()} and create your view when
1857 * first needed by the input method.
1858 */
1859 public void setCandidatesView(View view) {
1860 mCandidatesFrame.removeAllViews();
1861 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001862 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863 ViewGroup.LayoutParams.WRAP_CONTENT));
1864 }
1865
1866 /**
1867 * Replaces the current input view with a new one. You only need to
1868 * call this when dynamically changing the view; normally, you should
1869 * implement {@link #onCreateInputView()} and create your view when
1870 * first needed by the input method.
1871 */
1872 public void setInputView(View view) {
1873 mInputFrame.removeAllViews();
1874 mInputFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001875 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001876 ViewGroup.LayoutParams.WRAP_CONTENT));
1877 mInputView = view;
1878 }
1879
1880 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001881 * Called by the framework to create the layout for showing extacted text.
1882 * Only called when in fullscreen mode. The returned view hierarchy must
1883 * have an {@link ExtractEditText} whose ID is
1884 * {@link android.R.id#inputExtractEditText}.
1885 */
1886 public View onCreateExtractTextView() {
1887 return mInflater.inflate(
1888 com.android.internal.R.layout.input_method_extract_view, null);
1889 }
1890
1891 /**
1892 * Create and return the view hierarchy used to show candidates. This will
1893 * be called once, when the candidates are first displayed. You can return
1894 * null to have no candidates view; the default implementation returns null.
1895 *
1896 * <p>To control when the candidates view is displayed, use
1897 * {@link #setCandidatesViewShown(boolean)}.
1898 * To change the candidates view after the first one is created by this
1899 * function, use {@link #setCandidatesView(View)}.
1900 */
1901 public View onCreateCandidatesView() {
1902 return null;
1903 }
1904
1905 /**
1906 * Create and return the view hierarchy used for the input area (such as
1907 * a soft keyboard). This will be called once, when the input area is
1908 * first displayed. You can return null to have no input area; the default
1909 * implementation returns null.
1910 *
1911 * <p>To control when the input view is displayed, implement
1912 * {@link #onEvaluateInputViewShown()}.
1913 * To change the input view after the first one is created by this
1914 * function, use {@link #setInputView(View)}.
1915 */
1916 public View onCreateInputView() {
1917 return null;
1918 }
1919
1920 /**
1921 * Called when the input view is being shown and input has started on
1922 * a new editor. This will always be called after {@link #onStartInput},
1923 * allowing you to do your general setup there and just view-specific
1924 * setup here. You are guaranteed that {@link #onCreateInputView()} will
1925 * have been called some time before this function is called.
1926 *
1927 * @param info Description of the type of text being edited.
1928 * @param restarting Set to true if we are restarting input on the
1929 * same text field as before.
1930 */
1931 public void onStartInputView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001932 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001933 }
1934
1935 /**
1936 * Called when the input view is being hidden from the user. This will
1937 * be called either prior to hiding the window, or prior to switching to
1938 * another target for editing.
1939 *
1940 * <p>The default
1941 * implementation uses the InputConnection to clear any active composing
1942 * text; you can override this (not calling the base class implementation)
1943 * to perform whatever behavior you would like.
1944 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001945 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001946 * called immediately after.
1947 */
1948 public void onFinishInputView(boolean finishingInput) {
1949 if (!finishingInput) {
1950 InputConnection ic = getCurrentInputConnection();
1951 if (ic != null) {
1952 ic.finishComposingText();
1953 }
1954 }
1955 }
1956
1957 /**
1958 * Called when only the candidates view has been shown for showing
1959 * processing as the user enters text through a hard keyboard.
1960 * This will always be called after {@link #onStartInput},
1961 * allowing you to do your general setup there and just view-specific
1962 * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
1963 * will have been called some time before this function is called.
1964 *
1965 * <p>Note that this will <em>not</em> be called when the input method
1966 * is running in full editing mode, and thus receiving
1967 * {@link #onStartInputView} to initiate that operation. This is only
1968 * for the case when candidates are being shown while the input method
1969 * editor is hidden but wants to show its candidates UI as text is
1970 * entered through some other mechanism.
1971 *
1972 * @param info Description of the type of text being edited.
1973 * @param restarting Set to true if we are restarting input on the
1974 * same text field as before.
1975 */
1976 public void onStartCandidatesView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001977 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001978 }
1979
1980 /**
1981 * Called when the candidates view is being hidden from the user. This will
1982 * be called either prior to hiding the window, or prior to switching to
1983 * another target for editing.
1984 *
1985 * <p>The default
1986 * implementation uses the InputConnection to clear any active composing
1987 * text; you can override this (not calling the base class implementation)
1988 * to perform whatever behavior you would like.
1989 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001990 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001991 * called immediately after.
1992 */
1993 public void onFinishCandidatesView(boolean finishingInput) {
1994 if (!finishingInput) {
1995 InputConnection ic = getCurrentInputConnection();
1996 if (ic != null) {
1997 ic.finishComposingText();
1998 }
1999 }
2000 }
2001
2002 /**
2003 * The system has decided that it may be time to show your input method.
2004 * This is called due to a corresponding call to your
The Android Open Source Project4df24232009-03-05 14:34:35 -08002005 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 * method. The default implementation uses
2007 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
2008 * and the current configuration to decide whether the input view should
2009 * be shown at this point.
2010 *
2011 * @param flags Provides additional information about the show request,
The Android Open Source Project4df24232009-03-05 14:34:35 -08002012 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 * @param configChange This is true if we are re-showing due to a
2014 * configuration change.
2015 * @return Returns true to indicate that the window should be shown.
2016 */
2017 public boolean onShowInputRequested(int flags, boolean configChange) {
2018 if (!onEvaluateInputViewShown()) {
2019 return false;
2020 }
2021 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
2022 if (!configChange && onEvaluateFullscreenMode()) {
2023 // Don't show if this is not explicitly requested by the user and
2024 // the input method is fullscreen. That would be too disruptive.
2025 // However, we skip this change for a config change, since if
2026 // the IME is already shown we do want to go into fullscreen
2027 // mode at this point.
2028 return false;
2029 }
Yohei Yukawad0d07972016-05-04 11:56:35 -07002030 if (!mSettingsObserver.shouldShowImeWithHardKeyboard() &&
2031 getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002032 // And if the device has a hard keyboard, even if it is
2033 // currently hidden, don't show the input method implicitly.
2034 // These kinds of devices don't need it that much.
2035 return false;
2036 }
2037 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 return true;
2039 }
Yohei Yukawaef5b4652016-04-03 22:50:11 -07002040
2041 /**
2042 * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal
2043 * states depending on its result. Since {@link #onShowInputRequested(int, boolean)} is
2044 * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
2045 * to have this method to ensure that those internal states are always updated no matter how
2046 * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
2047 * @param flags Provides additional information about the show request,
2048 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
2049 * @param configChange This is true if we are re-showing due to a
2050 * configuration change.
2051 * @return Returns true to indicate that the window should be shown.
2052 * @see #onShowInputRequested(int, boolean)
2053 */
2054 private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
2055 final boolean result = onShowInputRequested(flags, configChange);
2056 if (result) {
2057 mShowInputFlags = flags;
2058 } else {
2059 mShowInputFlags = 0;
2060 }
2061 return result;
2062 }
2063
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002064 public void showWindow(boolean showInput) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07002065 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002066 + " mShowInputRequested=" + mShowInputRequested
Tarandeep Singheadb1392018-11-09 18:15:57 +01002067 + " mViewsCreated=" + mViewsCreated
2068 + " mDecorViewVisible=" + mDecorViewVisible
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002069 + " mWindowVisible=" + mWindowVisible
Yohei Yukawaef5b4652016-04-03 22:50:11 -07002070 + " mInputStarted=" + mInputStarted
2071 + " mShowInputFlags=" + mShowInputFlags);
2072
The Android Open Source Project10592532009-03-18 17:39:46 -07002073 if (mInShowWindow) {
2074 Log.w(TAG, "Re-entrance in to showWindow");
2075 return;
2076 }
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002077
Tarandeep Singheadb1392018-11-09 18:15:57 +01002078 mDecorViewWasVisible = mDecorViewVisible;
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002079 mInShowWindow = true;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002080 boolean isPreRenderedAndInvisible = mIsPreRendered && !mWindowVisible;
2081 final int previousImeWindowStatus =
2082 (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
2083 ? (isPreRenderedAndInvisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
2084 startViews(prepareWindow(showInput));
2085 final int nextImeWindowStatus = mapToImeWindowStatus();
2086 if (previousImeWindowStatus != nextImeWindowStatus) {
2087 setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
2088 }
2089
2090 // compute visibility
2091 onWindowShown();
2092 mIsPreRendered = mCanPreRender;
2093 if (mIsPreRendered) {
2094 onPreRenderedWindowVisibilityChanged(true /* setVisible */);
2095 } else {
2096 // Pre-rendering not supported.
2097 if (DEBUG) Log.d(TAG, "No pre-rendering supported");
2098 mWindowVisible = true;
2099 }
2100
2101 // request draw for the IME surface.
2102 // When IME is not pre-rendered, this will actually show the IME.
2103 if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
2104 if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
2105 mWindow.show();
2106 }
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002107 maybeNotifyPreRendered();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002108 mDecorViewWasVisible = true;
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002109 mInShowWindow = false;
The Android Open Source Project10592532009-03-18 17:39:46 -07002110 }
satok06487a52010-10-29 11:37:18 +09002111
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002112 /**
2113 * Notify {@link android.view.ImeInsetsSourceConsumer} if IME has been pre-rendered
2114 * for current EditorInfo, when pre-rendering is enabled.
2115 */
2116 private void maybeNotifyPreRendered() {
2117 if (!mCanPreRender || !mIsPreRendered) {
2118 return;
2119 }
2120 mPrivOps.reportPreRendered(getCurrentInputEditorInfo());
2121 }
2122
2123
Tarandeep Singheadb1392018-11-09 18:15:57 +01002124 private boolean prepareWindow(boolean showInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002125 boolean doShowInput = false;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002126 mDecorViewVisible = true;
Yohei Yukawaac8bdd22015-09-11 18:17:11 -07002127 if (!mShowInputRequested && mInputStarted && showInput) {
2128 doShowInput = true;
2129 mShowInputRequested = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002130 }
satok06487a52010-10-29 11:37:18 +09002131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132 if (DEBUG) Log.v(TAG, "showWindow: updating UI");
2133 initialize();
2134 updateFullscreenMode();
2135 updateInputViewShown();
Yohei Yukawa13a9ffb2018-08-29 19:56:02 -07002136
Tarandeep Singheadb1392018-11-09 18:15:57 +01002137 if (!mViewsCreated) {
2138 mViewsCreated = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002139 initialize();
2140 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
2141 View v = onCreateCandidatesView();
2142 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
2143 if (v != null) {
2144 setCandidatesView(v);
2145 }
2146 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002147 return doShowInput;
2148 }
2149
2150 private void startViews(boolean doShowInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002151 if (mShowInputRequested) {
2152 if (!mInputViewStarted) {
2153 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
2154 mInputViewStarted = true;
2155 onStartInputView(mInputEditorInfo, false);
2156 }
2157 } else if (!mCandidatesViewStarted) {
2158 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
2159 mCandidatesViewStarted = true;
2160 onStartCandidatesView(mInputEditorInfo, false);
2161 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002162 if (doShowInput) startExtractingText(false);
2163 }
satok06487a52010-10-29 11:37:18 +09002164
Tarandeep Singheadb1392018-11-09 18:15:57 +01002165 private void onPreRenderedWindowVisibilityChanged(boolean setVisible) {
2166 mWindowVisible = setVisible;
2167 mShowInputFlags = setVisible ? mShowInputFlags : 0;
2168 mShowInputRequested = setVisible;
2169 mDecorViewVisible = setVisible;
2170 if (setVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002171 onWindowShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002172 }
2173 }
satok06487a52010-10-29 11:37:18 +09002174
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002175 /**
2176 * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002177 * {@link ViewRootImpl.sNewInsetsMode} is enabled.
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002178 * @param setVisible {@code true} to make it visible, false to hide it.
2179 */
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002180 private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
2181 if (!isVisibilityAppliedUsingInsetsConsumer()) {
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002182 return;
2183 }
2184 mPrivOps.applyImeVisibility(setVisible);
2185 }
2186
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002187 private boolean isVisibilityAppliedUsingInsetsConsumer() {
2188 return ViewRootImpl.sNewInsetsMode > NEW_INSETS_MODE_NONE;
2189 }
2190
Tarandeep Singheadb1392018-11-09 18:15:57 +01002191 private void finishViews(boolean finishingInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002192 if (mInputViewStarted) {
2193 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
Tarandeep Singheadb1392018-11-09 18:15:57 +01002194 onFinishInputView(finishingInput);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002195 } else if (mCandidatesViewStarted) {
2196 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
Tarandeep Singheadb1392018-11-09 18:15:57 +01002197 onFinishCandidatesView(finishingInput);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002198 }
2199 mInputViewStarted = false;
2200 mCandidatesViewStarted = false;
satokf17db9f2011-09-14 18:55:58 +09002201 }
2202
satok2f913d92012-05-10 01:48:03 +09002203 private void doHideWindow() {
Yohei Yukawac54c1172018-09-06 11:39:50 -07002204 setImeWindowStatus(0, mBackDisposition);
satok2f913d92012-05-10 01:48:03 +09002205 hideWindow();
2206 }
2207
satokf17db9f2011-09-14 18:55:58 +09002208 public void hideWindow() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01002209 if (DEBUG) Log.v(TAG, "CALL: hideWindow");
2210 mIsPreRendered = false;
2211 mWindowVisible = false;
2212 finishViews(false /* finishingInput */);
2213 if (mDecorViewVisible) {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -07002214 // When insets API is enabled, it is responsible for client and server side
2215 // visibility of IME window.
2216 if (!isVisibilityAppliedUsingInsetsConsumer()) {
2217 mWindow.hide();
2218 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002219 mDecorViewVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002220 onWindowHidden();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002221 mDecorViewWasVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002222 }
Seigo Nonaka93c47ea2015-07-14 15:05:04 +09002223 updateFullscreenMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002224 }
satok06487a52010-10-29 11:37:18 +09002225
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002226 /**
tiansiming [田思明]b9025932018-02-05 18:28:28 +08002227 * Called immediately before the input method window is shown to the user.
2228 * You could override this to prepare for the window to be shown
2229 * (update view structure etc).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002230 */
2231 public void onWindowShown() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002232 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002233 }
2234
2235 /**
2236 * Called when the input method window has been hidden from the user,
2237 * after previously being visible.
2238 */
2239 public void onWindowHidden() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002240 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241 }
Yohei Yukawa2977eb72015-05-27 18:54:18 -07002242
2243 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002244 * Called when a new client has bound to the input method. This
2245 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
2246 * and {@link #onFinishInput()} calls as the user navigates through its
2247 * UI. Upon this call you know that {@link #getCurrentInputBinding}
2248 * and {@link #getCurrentInputConnection} return valid objects.
2249 */
2250 public void onBindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002251 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002252 }
2253
2254 /**
2255 * Called when the previous bound client is no longer associated
2256 * with the input method. After returning {@link #getCurrentInputBinding}
2257 * and {@link #getCurrentInputConnection} will no longer return
2258 * valid objects.
2259 */
2260 public void onUnbindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07002261 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002262 }
2263
2264 /**
2265 * Called to inform the input method that text input has started in an
2266 * editor. You should use this callback to initialize the state of your
2267 * input to match the state of the editor given to it.
2268 *
2269 * @param attribute The attributes of the editor that input is starting
2270 * in.
2271 * @param restarting Set to true if input is restarting in the same
2272 * editor such as because the application has changed the text in
2273 * the editor. Otherwise will be false, indicating this is a new
2274 * session with the editor.
2275 */
2276 public void onStartInput(EditorInfo attribute, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002277 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278 }
2279
2280 void doFinishInput() {
Tarandeep Singheadb1392018-11-09 18:15:57 +01002281 if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
2282 finishViews(true /* finishingInput */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002283 if (mInputStarted) {
2284 if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
2285 onFinishInput();
2286 }
2287 mInputStarted = false;
2288 mStartedInputConnection = null;
2289 mCurCompletions = null;
2290 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08002291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002292 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
2293 if (!restarting) {
2294 doFinishInput();
2295 }
2296 mInputStarted = true;
2297 mStartedInputConnection = ic;
2298 mInputEditorInfo = attribute;
2299 initialize();
2300 if (DEBUG) Log.v(TAG, "CALL: onStartInput");
2301 onStartInput(attribute, restarting);
Tarandeep Singheadb1392018-11-09 18:15:57 +01002302 if (mDecorViewVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002303 if (mShowInputRequested) {
2304 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
2305 mInputViewStarted = true;
2306 onStartInputView(mInputEditorInfo, restarting);
2307 startExtractingText(true);
2308 } else if (mCandidatesVisibility == View.VISIBLE) {
2309 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
2310 mCandidatesViewStarted = true;
2311 onStartCandidatesView(mInputEditorInfo, restarting);
2312 }
Tarandeep Singheadb1392018-11-09 18:15:57 +01002313 } else if (mCanPreRender && mInputEditorInfo != null && mStartedInputConnection != null) {
2314 // Pre-render IME views and window when real EditorInfo is available.
2315 // pre-render IME window and keep it invisible.
2316 if (DEBUG) Log.v(TAG, "Pre-Render IME for " + mInputEditorInfo.fieldName);
2317 if (mInShowWindow) {
2318 Log.w(TAG, "Re-entrance in to showWindow");
2319 return;
2320 }
2321
2322 mDecorViewWasVisible = mDecorViewVisible;
2323 mInShowWindow = true;
2324 startViews(prepareWindow(true /* showInput */));
2325
2326 // compute visibility
2327 mIsPreRendered = true;
2328 onPreRenderedWindowVisibilityChanged(false /* setVisible */);
2329
2330 // request draw for the IME surface.
2331 // When IME is not pre-rendered, this will actually show the IME.
2332 if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
2333 mWindow.show();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08002334 maybeNotifyPreRendered();
Tarandeep Singheadb1392018-11-09 18:15:57 +01002335 mDecorViewWasVisible = true;
2336 mInShowWindow = false;
2337 } else {
2338 mIsPreRendered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002339 }
2340 }
2341
2342 /**
2343 * Called to inform the input method that text input has finished in
2344 * the last editor. At this point there may be a call to
2345 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
2346 * new editor, or the input method may be left idle. This method is
2347 * <em>not</em> called when input restarts in the same editor.
2348 *
2349 * <p>The default
2350 * implementation uses the InputConnection to clear any active composing
2351 * text; you can override this (not calling the base class implementation)
2352 * to perform whatever behavior you would like.
2353 */
2354 public void onFinishInput() {
2355 InputConnection ic = getCurrentInputConnection();
2356 if (ic != null) {
2357 ic.finishComposingText();
2358 }
2359 }
2360
2361 /**
2362 * Called when the application has reported auto-completion candidates that
2363 * it would like to have the input method displayed. Typically these are
2364 * only used when an input method is running in full-screen mode, since
2365 * otherwise the user can see and interact with the pop-up window of
2366 * completions shown by the application.
2367 *
2368 * <p>The default implementation here does nothing.
2369 */
2370 public void onDisplayCompletions(CompletionInfo[] completions) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002371 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002372 }
2373
2374 /**
2375 * Called when the application has reported new extracted text to be shown
2376 * due to changes in its current text state. The default implementation
2377 * here places the new text in the extract edit text, when the input
2378 * method is running in fullscreen mode.
2379 */
2380 public void onUpdateExtractedText(int token, ExtractedText text) {
2381 if (mExtractedToken != token) {
2382 return;
2383 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002384 if (text != null) {
2385 if (mExtractEditText != null) {
2386 mExtractedText = text;
2387 mExtractEditText.setExtractedText(text);
2388 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002389 }
2390 }
2391
2392 /**
2393 * Called when the application has reported a new selection region of
2394 * the text. This is called whether or not the input method has requested
2395 * extracted text updates, although if so it will not receive this call
2396 * if the extracted text has changed as well.
Jean Chalardc743cb92013-09-12 16:28:45 +09002397 *
2398 * <p>Be careful about changing the text in reaction to this call with
2399 * methods such as setComposingText, commitText or
2400 * deleteSurroundingText. If the cursor moves as a result, this method
2401 * will be called again, which may result in an infinite loop.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002402 *
2403 * <p>The default implementation takes care of updating the cursor in
2404 * the extract text, if it is being shown.
2405 */
2406 public void onUpdateSelection(int oldSelStart, int oldSelEnd,
2407 int newSelStart, int newSelEnd,
2408 int candidatesStart, int candidatesEnd) {
2409 final ExtractEditText eet = mExtractEditText;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07002410 if (eet != null && isFullscreenMode() && mExtractedText != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002411 final int off = mExtractedText.startOffset;
2412 eet.startInternalChanges();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07002413 newSelStart -= off;
2414 newSelEnd -= off;
2415 final int len = eet.getText().length();
2416 if (newSelStart < 0) newSelStart = 0;
2417 else if (newSelStart > len) newSelStart = len;
2418 if (newSelEnd < 0) newSelEnd = 0;
2419 else if (newSelEnd > len) newSelEnd = len;
2420 eet.setSelection(newSelStart, newSelEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002421 eet.finishInternalChanges();
2422 }
2423 }
2424
2425 /**
satok863fcd62011-06-21 17:38:02 +09002426 * Called when the user tapped or clicked a text view.
2427 * IMEs can't rely on this method being called because this was not part of the original IME
2428 * protocol, so applications with custom text editing written before this method appeared will
2429 * not call to inform the IME of this interaction.
2430 * @param focusChanged true if the user changed the focused view by this click.
Yohei Yukawa0eb8d162019-01-22 21:47:57 -08002431 * @see InputMethodManager#viewClicked(View)
2432 * @deprecated The method may not be called for composite {@link View} that works as a giant
2433 * "Canvas", which can host its own UI hierarchy and sub focus state.
2434 * {@link android.webkit.WebView} is a good example. Application / IME developers
2435 * should not rely on this method. If your goal is just being notified when an
2436 * on-going input is interrupted, simply monitor {@link #onFinishInput()}.
satok863fcd62011-06-21 17:38:02 +09002437 */
Yohei Yukawa0eb8d162019-01-22 21:47:57 -08002438 @Deprecated
satok863fcd62011-06-21 17:38:02 +09002439 public void onViewClicked(boolean focusChanged) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002440 // Intentionally empty
satok863fcd62011-06-21 17:38:02 +09002441 }
2442
2443 /**
Yohei Yukawaa277db22014-08-21 18:38:44 -07002444 * Called when the application has reported a new location of its text
2445 * cursor. This is only called if explicitly requested by the input method.
2446 * The default implementation does nothing.
Andrew Solovay5c05ded2018-10-02 14:14:42 -07002447 * @deprecated Use {@link #onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002448 */
Yohei Yukawaa277db22014-08-21 18:38:44 -07002449 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002450 public void onUpdateCursor(Rect newCursor) {
Gilles Debunne34703b62011-09-08 11:16:25 -07002451 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002452 }
2453
2454 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +09002455 * Called when the application has reported a new location of its text insertion point and
2456 * characters in the composition string. This is only called if explicitly requested by the
2457 * input method. The default implementation does nothing.
2458 * @param cursorAnchorInfo The positional information of the text insertion point and the
2459 * composition string.
2460 */
2461 public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
2462 // Intentionally empty
2463 }
2464
2465 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002466 * Close this input method's soft input area, removing it from the display.
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002467 *
2468 * The input method will continue running, but the user can no longer use it to generate input
2469 * by touching the screen.
2470 *
2471 * @see InputMethodManager#HIDE_IMPLICIT_ONLY
2472 * @see InputMethodManager#HIDE_NOT_ALWAYS
2473 * @param flags Provides additional operating flags.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002474 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08002475 public void requestHideSelf(int flags) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07002476 mPrivOps.hideMySoftInput(flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002477 }
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002478
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002479 /**
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002480 * Show the input method's soft input area, so the user sees the input method window and can
2481 * interact with it.
2482 *
2483 * @see InputMethodManager#SHOW_IMPLICIT
2484 * @see InputMethodManager#SHOW_FORCED
2485 * @param flags Provides additional operating flags.
The Android Open Source Project4df24232009-03-05 14:34:35 -08002486 */
Tarandeep Singh45136992018-03-08 10:52:03 -08002487 public final void requestShowSelf(int flags) {
Yohei Yukawac7ca3682018-09-09 20:48:38 -07002488 mPrivOps.showMySoftInput(flags);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002489 }
Yohei Yukawafbc2f7a2018-01-16 08:09:11 -08002490
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002491 private boolean handleBack(boolean doIt) {
2492 if (mShowInputRequested) {
2493 // If the soft input area is shown, back closes it and we
2494 // consume the back key.
2495 if (doIt) requestHideSelf(0);
2496 return true;
Tarandeep Singheadb1392018-11-09 18:15:57 +01002497 } else if (mDecorViewVisible) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002498 if (mCandidatesVisibility == View.VISIBLE) {
2499 // If we are showing candidates even if no input area, then
2500 // hide them.
2501 if (doIt) setCandidatesViewShown(false);
2502 } else {
2503 // If we have the window visible for some other reason --
2504 // most likely to show candidates -- then just get rid
2505 // of it. This really shouldn't happen, but just in case...
satok2f913d92012-05-10 01:48:03 +09002506 if (doIt) doHideWindow();
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002507 }
2508 return true;
2509 }
2510 return false;
2511 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002512
2513 /**
Andrew Solovay5c05ded2018-10-02 14:14:42 -07002514 * @return {@link ExtractEditText} if it is considered to be visible and active. Otherwise
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002515 * {@code null} is returned.
2516 */
2517 private ExtractEditText getExtractEditTextIfVisible() {
2518 if (!isExtractViewShown() || !isInputViewShown()) {
2519 return null;
2520 }
2521 return mExtractEditText;
2522 }
2523
The Android Open Source Project4df24232009-03-05 14:34:35 -08002524 /**
Yohei Yukawa386f50e2018-03-14 13:03:42 -07002525 * Called back when a {@link KeyEvent} is forwarded from the target application.
2526 *
2527 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK} if the IME is
2528 * currently shown , to possibly hide it when the key goes up (if not canceled or long pressed).
2529 * In addition, in fullscreen mode only, it will consume DPAD movement events to move the cursor
2530 * in the extracted text view, not allowing them to perform navigation in the underlying
2531 * application.</p>
2532 *
2533 * <p>The default implementation does not take flags specified to
2534 * {@link #setBackDisposition(int)} into account, even on API version
2535 * {@link android.os.Build.VERSION_CODES#P} and later devices. IME developers are responsible
2536 * for making sure that their special handling for {@link KeyEvent#KEYCODE_BACK} are consistent
2537 * with the flag they specified to {@link #setBackDisposition(int)}.</p>
2538 *
2539 * @param keyCode The value in {@code event.getKeyCode()}
2540 * @param event Description of the key event
2541 *
2542 * @return {@code true} if the event is consumed by the IME and the application no longer needs
2543 * to consume it. Return {@code false} when the event should be handled as if the IME
2544 * had not seen the event at all.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002545 */
2546 public boolean onKeyDown(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002547 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002548 final ExtractEditText eet = getExtractEditTextIfVisible();
2549 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2550 return true;
2551 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002552 if (handleBack(false)) {
2553 event.startTracking();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002554 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002555 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002556 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002557 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002558 return doMovementKey(keyCode, event, MOVEMENT_DOWN);
2559 }
2560
2561 /**
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002562 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
2563 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
2564 * the event).
2565 */
2566 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
2567 return false;
2568 }
2569
2570 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002571 * Override this to intercept special key multiple events before they are
2572 * processed by the
2573 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002574 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002575 * will occur as if the IME had not seen the event at all.
2576 *
2577 * <p>The default implementation always returns false, except when
2578 * in fullscreen mode, where it will consume DPAD movement
2579 * events to move the cursor in the extracted text view, not allowing
2580 * them to perform navigation in the underlying application.
2581 */
2582 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
2583 return doMovementKey(keyCode, event, count);
2584 }
2585
2586 /**
2587 * Override this to intercept key up events before they are processed by the
2588 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07002589 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002590 * will occur as if the IME had not seen the event at all.
2591 *
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002592 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
2593 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
2594 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002595 * events to move the cursor in the extracted text view, not allowing
2596 * them to perform navigation in the underlying application.
2597 */
2598 public boolean onKeyUp(int keyCode, KeyEvent event) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002599 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
2600 final ExtractEditText eet = getExtractEditTextIfVisible();
2601 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
2602 return true;
2603 }
2604 if (event.isTracking() && !event.isCanceled()) {
2605 return handleBack(true);
2606 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07002607 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002608 return doMovementKey(keyCode, event, MOVEMENT_UP);
2609 }
2610
Victoria Leaseb38070c2012-08-24 13:46:02 -07002611 /**
2612 * Override this to intercept trackball motion events before they are
2613 * processed by the application.
2614 * If you return true, the application will not itself process the event.
2615 * If you return false, the normal application processing will occur as if
2616 * the IME had not seen the event at all.
2617 */
satokab751aa2010-09-14 19:17:36 +09002618 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002619 public boolean onTrackballEvent(MotionEvent event) {
Victoria Leaseb38070c2012-08-24 13:46:02 -07002620 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
2621 return false;
2622 }
2623
2624 /**
2625 * Override this to intercept generic motion events before they are
2626 * processed by the application.
2627 * If you return true, the application will not itself process the event.
2628 * If you return false, the normal application processing will occur as if
2629 * the IME had not seen the event at all.
2630 */
2631 @Override
2632 public boolean onGenericMotionEvent(MotionEvent event) {
2633 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002634 return false;
2635 }
2636
2637 public void onAppPrivateCommand(String action, Bundle data) {
2638 }
2639
The Android Open Source Project4df24232009-03-05 14:34:35 -08002640 /**
2641 * Handle a request by the system to toggle the soft input area.
2642 */
2643 private void onToggleSoftInput(int showFlags, int hideFlags) {
2644 if (DEBUG) Log.v(TAG, "toggleSoftInput()");
2645 if (isInputViewShown()) {
2646 requestHideSelf(hideFlags);
2647 } else {
2648 requestShowSelf(showFlags);
2649 }
2650 }
2651
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002652 static final int MOVEMENT_DOWN = -1;
2653 static final int MOVEMENT_UP = -2;
2654
2655 void reportExtractedMovement(int keyCode, int count) {
2656 int dx = 0, dy = 0;
2657 switch (keyCode) {
2658 case KeyEvent.KEYCODE_DPAD_LEFT:
2659 dx = -count;
2660 break;
2661 case KeyEvent.KEYCODE_DPAD_RIGHT:
2662 dx = count;
2663 break;
2664 case KeyEvent.KEYCODE_DPAD_UP:
2665 dy = -count;
2666 break;
2667 case KeyEvent.KEYCODE_DPAD_DOWN:
2668 dy = count;
2669 break;
2670 }
satokab751aa2010-09-14 19:17:36 +09002671 onExtractedCursorMovement(dx, dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002672 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002674 boolean doMovementKey(int keyCode, KeyEvent event, int count) {
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002675 final ExtractEditText eet = getExtractEditTextIfVisible();
2676 if (eet != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002677 // If we are in fullscreen mode, the cursor will move around
2678 // the extract edit text, but should NOT cause focus to move
2679 // to other fields.
2680 MovementMethod movement = eet.getMovementMethod();
2681 Layout layout = eet.getLayout();
2682 if (movement != null && layout != null) {
2683 // We want our own movement method to handle the key, so the
2684 // cursor will properly move in our own word wrapping.
2685 if (count == MOVEMENT_DOWN) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002686 if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002687 reportExtractedMovement(keyCode, 1);
2688 return true;
2689 }
2690 } else if (count == MOVEMENT_UP) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002691 if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002692 return true;
2693 }
2694 } else {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002695 if (movement.onKeyOther(eet, eet.getText(), event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002696 reportExtractedMovement(keyCode, count);
2697 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07002698 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002699 if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002700 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
Yohei Yukawa24182f32015-09-11 18:33:33 -07002701 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002702 while (--count > 0) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002703 movement.onKeyDown(eet, eet.getText(), keyCode, down);
2704 movement.onKeyUp(eet, eet.getText(), keyCode, up);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002705 }
2706 reportExtractedMovement(keyCode, count);
2707 }
2708 }
2709 }
2710 }
2711 // Regardless of whether the movement method handled the key,
2712 // we never allow DPAD navigation to the application.
2713 switch (keyCode) {
2714 case KeyEvent.KEYCODE_DPAD_LEFT:
2715 case KeyEvent.KEYCODE_DPAD_RIGHT:
2716 case KeyEvent.KEYCODE_DPAD_UP:
2717 case KeyEvent.KEYCODE_DPAD_DOWN:
2718 return true;
2719 }
2720 }
Yohei Yukawa38940aa2015-06-24 00:20:24 -07002721
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002722 return false;
2723 }
2724
2725 /**
2726 * Send the given key event code (as defined by {@link KeyEvent}) to the
2727 * current input connection is a key down + key up event pair. The sent
2728 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
2729 * set, so that the recipient can identify them as coming from a software
2730 * input method, and
2731 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
2732 * that they don't impact the current touch mode of the UI.
2733 *
Jean Chalard405bc512012-05-29 19:12:34 +09002734 * <p>Note that it's discouraged to send such key events in normal operation;
2735 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
2736 * text fields, or for non-rich input methods. A reasonably capable software
2737 * input method should use the
2738 * {@link android.view.inputmethod.InputConnection#commitText} family of methods
2739 * to send text to an application, rather than sending key events.</p>
2740 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002741 * @param keyEventCode The raw key code to send, as defined by
2742 * {@link KeyEvent}.
2743 */
2744 public void sendDownUpKeyEvents(int keyEventCode) {
2745 InputConnection ic = getCurrentInputConnection();
2746 if (ic == null) return;
2747 long eventTime = SystemClock.uptimeMillis();
2748 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002749 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002750 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
Tadashi G. Takaokacb95cd62012-10-26 17:20:59 +09002751 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -08002752 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002753 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
2754 }
2755
2756 /**
2757 * Ask the input target to execute its default action via
2758 * {@link InputConnection#performEditorAction
2759 * InputConnection.performEditorAction()}.
2760 *
2761 * @param fromEnterKey If true, this will be executed as if the user had
2762 * pressed an enter key on the keyboard, that is it will <em>not</em>
2763 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
2764 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
2765 * sent regardless of how the editor has set that flag.
2766 *
2767 * @return Returns a boolean indicating whether an action has been sent.
2768 * If false, either the editor did not specify a default action or it
2769 * does not want an action from the enter key. If true, the action was
2770 * sent (or there was no input connection at all).
2771 */
2772 public boolean sendDefaultEditorAction(boolean fromEnterKey) {
2773 EditorInfo ei = getCurrentInputEditorInfo();
2774 if (ei != null &&
2775 (!fromEnterKey || (ei.imeOptions &
2776 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
2777 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
2778 EditorInfo.IME_ACTION_NONE) {
2779 // If the enter key was pressed, and the editor has a default
2780 // action associated with pressing enter, then send it that
2781 // explicit action instead of the key event.
2782 InputConnection ic = getCurrentInputConnection();
2783 if (ic != null) {
2784 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
2785 }
2786 return true;
2787 }
2788
2789 return false;
2790 }
2791
2792 /**
2793 * Send the given UTF-16 character to the current input connection. Most
2794 * characters will be delivered simply by calling
2795 * {@link InputConnection#commitText InputConnection.commitText()} with
2796 * the character; some, however, may be handled different. In particular,
2797 * the enter character ('\n') will either be delivered as an action code
Jean Chalard405bc512012-05-29 19:12:34 +09002798 * or a raw key event, as appropriate. Consider this as a convenience
2799 * method for IMEs that do not have a full implementation of actions; a
2800 * fully complying IME will decide of the right action for each event and
2801 * will likely never call this method except maybe to handle events coming
2802 * from an actual hardware keyboard.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002803 *
2804 * @param charCode The UTF-16 character code to send.
2805 */
2806 public void sendKeyChar(char charCode) {
2807 switch (charCode) {
2808 case '\n': // Apps may be listening to an enter key to perform an action
2809 if (!sendDefaultEditorAction(true)) {
2810 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2811 }
2812 break;
2813 default:
2814 // Make sure that digits go through any text watcher on the client side.
2815 if (charCode >= '0' && charCode <= '9') {
2816 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2817 } else {
2818 InputConnection ic = getCurrentInputConnection();
2819 if (ic != null) {
Yohei Yukawa24182f32015-09-11 18:33:33 -07002820 ic.commitText(String.valueOf(charCode), 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002821 }
2822 }
2823 break;
2824 }
2825 }
2826
2827 /**
2828 * This is called when the user has moved the cursor in the extracted
2829 * text view, when running in fullsreen mode. The default implementation
2830 * performs the corresponding selection change on the underlying text
2831 * editor.
2832 */
2833 public void onExtractedSelectionChanged(int start, int end) {
2834 InputConnection conn = getCurrentInputConnection();
2835 if (conn != null) {
2836 conn.setSelection(start, end);
2837 }
2838 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002839
2840 /**
2841 * @hide
2842 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002843 @UnsupportedAppUsage
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002844 public void onExtractedDeleteText(int start, int end) {
2845 InputConnection conn = getCurrentInputConnection();
2846 if (conn != null) {
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002847 conn.finishComposingText();
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002848 conn.setSelection(start, start);
Keisuke Kuroyanagi755c0092016-03-14 19:09:20 +09002849 conn.deleteSurroundingText(0, end - start);
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002850 }
2851 }
2852
2853 /**
2854 * @hide
2855 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002856 @UnsupportedAppUsage
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002857 public void onExtractedReplaceText(int start, int end, CharSequence text) {
2858 InputConnection conn = getCurrentInputConnection();
2859 if (conn != null) {
2860 conn.setComposingRegion(start, end);
2861 conn.commitText(text, 1);
2862 }
2863 }
2864
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002865 /**
Gilles Debunnee300be92011-12-06 10:15:56 -08002866 * @hide
2867 */
Mathew Inwood1dd7d112018-07-31 14:53:29 +01002868 @UnsupportedAppUsage
Gilles Debunnee300be92011-12-06 10:15:56 -08002869 public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2870 InputConnection conn = getCurrentInputConnection();
2871 if (conn != null) {
2872 if (!conn.setSelection(start, end)) return;
2873 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2874 if (text instanceof Spannable) {
2875 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2876 conn.setComposingRegion(start, end);
2877 conn.commitText(text, 1);
2878 }
2879 }
2880 }
2881
2882 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002883 * This is called when the user has clicked on the extracted text view,
2884 * when running in fullscreen mode. The default implementation hides
2885 * the candidates view when this happens, but only if the extracted text
2886 * editor has a vertical scroll bar because its text doesn't fit.
2887 * Re-implement this to provide whatever behavior you want.
2888 */
2889 public void onExtractedTextClicked() {
2890 if (mExtractEditText == null) {
2891 return;
2892 }
2893 if (mExtractEditText.hasVerticalScrollBar()) {
2894 setCandidatesViewShown(false);
2895 }
2896 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002897
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002898 /**
2899 * This is called when the user has performed a cursor movement in the
2900 * extracted text view, when it is running in fullscreen mode. The default
2901 * implementation hides the candidates view when a vertical movement
2902 * happens, but only if the extracted text editor has a vertical scroll bar
2903 * because its text doesn't fit.
2904 * Re-implement this to provide whatever behavior you want.
2905 * @param dx The amount of cursor movement in the x dimension.
2906 * @param dy The amount of cursor movement in the y dimension.
2907 */
2908 public void onExtractedCursorMovement(int dx, int dy) {
2909 if (mExtractEditText == null || dy == 0) {
2910 return;
2911 }
2912 if (mExtractEditText.hasVerticalScrollBar()) {
2913 setCandidatesViewShown(false);
2914 }
2915 }
2916
2917 /**
2918 * This is called when the user has selected a context menu item from the
2919 * extracted text view, when running in fullscreen mode. The default
2920 * implementation sends this action to the current InputConnection's
2921 * {@link InputConnection#performContextMenuAction(int)}, for it
2922 * to be processed in underlying "real" editor. Re-implement this to
2923 * provide whatever behavior you want.
2924 */
2925 public boolean onExtractTextContextMenuItem(int id) {
2926 InputConnection ic = getCurrentInputConnection();
2927 if (ic != null) {
2928 ic.performContextMenuAction(id);
2929 }
2930 return true;
2931 }
Elliot Waite54de7742017-01-11 15:30:35 -08002932
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002933 /**
2934 * Return text that can be used as a button label for the given
2935 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
2936 * if there is no action requested. Note that there is no guarantee that
2937 * the returned text will be relatively short, so you probably do not
2938 * want to use it as text on a soft keyboard key label.
Elliot Waite54de7742017-01-11 15:30:35 -08002939 *
2940 * @param imeOptions The value from {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
2941 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002942 * @return Returns a label to use, or null if there is no action.
2943 */
2944 public CharSequence getTextForImeAction(int imeOptions) {
2945 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2946 case EditorInfo.IME_ACTION_NONE:
2947 return null;
2948 case EditorInfo.IME_ACTION_GO:
2949 return getText(com.android.internal.R.string.ime_action_go);
2950 case EditorInfo.IME_ACTION_SEARCH:
2951 return getText(com.android.internal.R.string.ime_action_search);
2952 case EditorInfo.IME_ACTION_SEND:
2953 return getText(com.android.internal.R.string.ime_action_send);
2954 case EditorInfo.IME_ACTION_NEXT:
2955 return getText(com.android.internal.R.string.ime_action_next);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002956 case EditorInfo.IME_ACTION_DONE:
2957 return getText(com.android.internal.R.string.ime_action_done);
Dianne Hackborndea3ef72010-10-28 14:24:22 -07002958 case EditorInfo.IME_ACTION_PREVIOUS:
2959 return getText(com.android.internal.R.string.ime_action_previous);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002960 default:
2961 return getText(com.android.internal.R.string.ime_action_default);
2962 }
2963 }
Mark Renouf91eb2652016-04-11 16:03:26 -04002964
2965 /**
2966 * Return a drawable resource id that can be used as a button icon for the given
2967 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
2968 *
2969 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
2970 *
2971 * @return Returns a drawable resource id to use.
2972 */
2973 @DrawableRes
2974 private int getIconForImeAction(int imeOptions) {
2975 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2976 case EditorInfo.IME_ACTION_GO:
2977 return com.android.internal.R.drawable.ic_input_extract_action_go;
2978 case EditorInfo.IME_ACTION_SEARCH:
2979 return com.android.internal.R.drawable.ic_input_extract_action_search;
2980 case EditorInfo.IME_ACTION_SEND:
2981 return com.android.internal.R.drawable.ic_input_extract_action_send;
2982 case EditorInfo.IME_ACTION_NEXT:
2983 return com.android.internal.R.drawable.ic_input_extract_action_next;
2984 case EditorInfo.IME_ACTION_DONE:
2985 return com.android.internal.R.drawable.ic_input_extract_action_done;
2986 case EditorInfo.IME_ACTION_PREVIOUS:
2987 return com.android.internal.R.drawable.ic_input_extract_action_previous;
2988 default:
2989 return com.android.internal.R.drawable.ic_input_extract_action_return;
2990 }
2991 }
2992
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002993 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07002994 * Called when the fullscreen-mode extracting editor info has changed,
2995 * to determine whether the extracting (extract text and candidates) portion
2996 * of the UI should be shown. The standard implementation hides or shows
2997 * the extract area depending on whether it makes sense for the
2998 * current editor. In particular, a {@link InputType#TYPE_NULL}
2999 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
3000 * turn off the extract area since there is no text to be shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003001 */
The Android Open Source Project10592532009-03-18 17:39:46 -07003002 public void onUpdateExtractingVisibility(EditorInfo ei) {
3003 if (ei.inputType == InputType.TYPE_NULL ||
3004 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
3005 // No reason to show extract UI!
3006 setExtractViewShown(false);
3007 return;
3008 }
3009
3010 setExtractViewShown(true);
3011 }
3012
3013 /**
3014 * Called when the fullscreen-mode extracting editor info has changed,
3015 * to update the state of its UI such as the action buttons shown.
3016 * You do not need to deal with this if you are using the standard
3017 * full screen extract UI. If replacing it, you will need to re-implement
3018 * this to put the appropriate action button in your own UI and handle it,
3019 * and perform any other changes.
3020 *
3021 * <p>The standard implementation turns on or off its accessory area
3022 * depending on whether there is an action button, and hides or shows
3023 * the entire extract area depending on whether it makes sense for the
3024 * current editor. In particular, a {@link InputType#TYPE_NULL} or
3025 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
3026 * extract area since there is no text to be shown.
3027 */
3028 public void onUpdateExtractingViews(EditorInfo ei) {
3029 if (!isExtractViewShown()) {
3030 return;
3031 }
3032
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003033 if (mExtractAccessories == null) {
3034 return;
3035 }
3036 final boolean hasAction = ei.actionLabel != null || (
3037 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
The Android Open Source Project10592532009-03-18 17:39:46 -07003038 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
3039 ei.inputType != InputType.TYPE_NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003040 if (hasAction) {
3041 mExtractAccessories.setVisibility(View.VISIBLE);
Steve Kondik59eb6912009-09-07 22:53:34 -04003042 if (mExtractAction != null) {
Mark Renouf91eb2652016-04-11 16:03:26 -04003043 if (mExtractAction instanceof ImageButton) {
3044 ((ImageButton) mExtractAction)
3045 .setImageResource(getIconForImeAction(ei.imeOptions));
3046 if (ei.actionLabel != null) {
3047 mExtractAction.setContentDescription(ei.actionLabel);
3048 } else {
3049 mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
3050 }
Steve Kondik59eb6912009-09-07 22:53:34 -04003051 } else {
Mark Renouf91eb2652016-04-11 16:03:26 -04003052 if (ei.actionLabel != null) {
3053 ((TextView) mExtractAction).setText(ei.actionLabel);
3054 } else {
3055 ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
3056 }
Steve Kondik59eb6912009-09-07 22:53:34 -04003057 }
3058 mExtractAction.setOnClickListener(mActionClickListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003059 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003060 } else {
3061 mExtractAccessories.setVisibility(View.GONE);
Steve Kondik59eb6912009-09-07 22:53:34 -04003062 if (mExtractAction != null) {
3063 mExtractAction.setOnClickListener(null);
3064 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003065 }
3066 }
3067
3068 /**
3069 * This is called when, while currently displayed in extract mode, the
3070 * current input target changes. The default implementation will
3071 * auto-hide the IME if the new target is not a full editor, since this
Ken Wakasaf76a50c2012-03-09 19:56:35 +09003072 * can be a confusing experience for the user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003073 */
3074 public void onExtractingInputChanged(EditorInfo ei) {
3075 if (ei.inputType == InputType.TYPE_NULL) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08003076 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003077 }
3078 }
3079
3080 void startExtractingText(boolean inputChanged) {
3081 final ExtractEditText eet = mExtractEditText;
3082 if (eet != null && getCurrentInputStarted()
3083 && isFullscreenMode()) {
3084 mExtractedToken++;
3085 ExtractedTextRequest req = new ExtractedTextRequest();
3086 req.token = mExtractedToken;
3087 req.flags = InputConnection.GET_TEXT_WITH_STYLES;
3088 req.hintMaxLines = 10;
3089 req.hintMaxChars = 10000;
Amith Yamasaniba4d93f2009-08-19 18:27:56 -07003090 InputConnection ic = getCurrentInputConnection();
3091 mExtractedText = ic == null? null
3092 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
Amith Yamasania8b00c82010-03-05 15:41:31 -08003093 if (mExtractedText == null || ic == null) {
3094 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
3095 + mExtractedText + ", input connection = " + ic);
3096 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003097 final EditorInfo ei = getCurrentInputEditorInfo();
3098
3099 try {
3100 eet.startInternalChanges();
The Android Open Source Project10592532009-03-18 17:39:46 -07003101 onUpdateExtractingVisibility(ei);
3102 onUpdateExtractingViews(ei);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003103 int inputType = ei.inputType;
3104 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
3105 == EditorInfo.TYPE_CLASS_TEXT) {
3106 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
3107 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
3108 }
3109 }
3110 eet.setInputType(inputType);
3111 eet.setHint(ei.hintText);
3112 if (mExtractedText != null) {
3113 eet.setEnabled(true);
3114 eet.setExtractedText(mExtractedText);
3115 } else {
3116 eet.setEnabled(false);
3117 eet.setText("");
3118 }
3119 } finally {
3120 eet.finishInternalChanges();
3121 }
3122
3123 if (inputChanged) {
3124 onExtractingInputChanged(ei);
3125 }
3126 }
3127 }
satokab751aa2010-09-14 19:17:36 +09003128
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003129 private void dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
3130 synchronized (mLock) {
3131 mNotifyUserActionSent = false;
3132 }
3133 onCurrentInputMethodSubtypeChanged(newSubtype);
3134 }
3135
satokab751aa2010-09-14 19:17:36 +09003136 // TODO: Handle the subtype change event
3137 /**
3138 * Called when the subtype was changed.
3139 * @param newSubtype the subtype which is being changed to.
3140 */
3141 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
3142 if (DEBUG) {
3143 int nameResId = newSubtype.getNameResId();
satok9ef02832010-11-04 21:17:48 +09003144 String mode = newSubtype.getMode();
satokab751aa2010-09-14 19:17:36 +09003145 String output = "changeInputMethodSubtype:"
3146 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
satok9ef02832010-11-04 21:17:48 +09003147 + mode + ","
satokab751aa2010-09-14 19:17:36 +09003148 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
3149 Log.v(TAG, "--- " + output);
3150 }
3151 }
3152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003153 /**
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003154 * Aimed to return the previous input method's {@link Insets#contentTopInsets}, but its actual
3155 * semantics has never been well defined.
3156 *
3157 * <p>Note that the previous document clearly mentioned that this method could return {@code 0}
3158 * at any time for whatever reason. Now this method is just always returning {@code 0}.</p>
3159 *
3160 * @return on Android {@link android.os.Build.VERSION_CODES#Q} and later devices this method
3161 * always returns {@code 0}
3162 * @deprecated the actual behavior of this method has never been well defined. You cannot use
3163 * this method in a reliable and predictable way
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003164 */
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003165 @Deprecated
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003166 public int getInputMethodWindowRecommendedHeight() {
Yohei Yukawa9d73f2e2018-09-26 18:19:21 -07003167 Log.w(TAG, "getInputMethodWindowRecommendedHeight() is deprecated and now always returns 0."
3168 + " Do not use this method.");
3169 return 0;
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003170 }
3171
3172 /**
Yohei Yukawa25e08132016-06-22 16:31:41 -07003173 * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
3174 * permission to the content.
3175 *
Yohei Yukawa25e08132016-06-22 16:31:41 -07003176 * @param inputContentInfo Content to be temporarily exposed from the input method to the
3177 * application.
3178 * This cannot be {@code null}.
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003179 * @param inputConnection {@link InputConnection} with which
Yohei Yukawab2a0e052018-01-14 16:06:16 -08003180 * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} will be called.
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003181 * @hide
Yohei Yukawa25e08132016-06-22 16:31:41 -07003182 */
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003183 @Override
3184 public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
3185 @NonNull InputConnection inputConnection) {
3186 if (inputConnection == null) {
3187 return;
Yohei Yukawa25e08132016-06-22 16:31:41 -07003188 }
Yohei Yukawa45700fa2016-06-23 17:12:59 -07003189 if (getCurrentInputConnection() != inputConnection) {
3190 return;
Yohei Yukawa25e08132016-06-22 16:31:41 -07003191 }
Yohei Yukawac54c1172018-09-06 11:39:50 -07003192 exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
3193 }
3194
3195 /**
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003196 * {@inheritDoc}
3197 * @hide
3198 */
3199 @AnyThread
3200 @Override
3201 public final void notifyUserActionIfNecessary() {
3202 synchronized (mLock) {
3203 if (mNotifyUserActionSent) {
3204 return;
3205 }
Yohei Yukawa9b60ba02019-01-21 00:06:27 -08003206 mPrivOps.notifyUserAction();
Yohei Yukawac07fd4c2018-09-11 11:37:13 -07003207 mNotifyUserActionSent = true;
3208 }
3209 }
3210
3211 /**
Yohei Yukawac54c1172018-09-06 11:39:50 -07003212 * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
3213 * permission to the content.
3214 *
3215 * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
3216 * InputConnection)} for details.</p>
3217 *
3218 * @param inputContentInfo Content to be temporarily exposed from the input method to the
3219 * application.
3220 * This cannot be {@code null}.
3221 * @param editorInfo The editor that receives {@link InputContentInfo}.
3222 */
3223 private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
3224 @NonNull EditorInfo editorInfo) {
Yohei Yukawac54c1172018-09-06 11:39:50 -07003225 final Uri contentUri = inputContentInfo.getContentUri();
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07003226 final IInputContentUriToken uriToken =
3227 mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
3228 if (uriToken == null) {
Yohei Yukawac54c1172018-09-06 11:39:50 -07003229 Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
Yohei Yukawa2bc3d6f2018-09-09 20:48:34 -07003230 + " packageName=" + editorInfo.packageName);
Yohei Yukawac54c1172018-09-06 11:39:50 -07003231 return;
3232 }
3233 inputContentInfo.setUriToken(uriToken);
Yohei Yukawa25e08132016-06-22 16:31:41 -07003234 }
3235
Tarandeep Singheadb1392018-11-09 18:15:57 +01003236 private int mapToImeWindowStatus() {
3237 return IME_ACTIVE
3238 | (isInputViewShown()
3239 ? (mCanPreRender ? (mWindowVisible ? IME_VISIBLE : IME_INVISIBLE)
3240 : IME_VISIBLE) : 0);
Tarandeep Singh3fecef12018-01-22 14:33:33 -08003241 }
3242
Yohei Yukawa25e08132016-06-22 16:31:41 -07003243 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003244 * Performs a dump of the InputMethodService's internal state. Override
3245 * to add your own information to the dump.
3246 */
3247 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
3248 final Printer p = new PrintWriterPrinter(fout);
3249 p.println("Input method service state for " + this + ":");
Tarandeep Singheadb1392018-11-09 18:15:57 +01003250 p.println(" mViewsCreated=" + mViewsCreated);
3251 p.println(" mDecorViewVisible=" + mDecorViewVisible
3252 + " mDecorViewWasVisible=" + mDecorViewWasVisible
3253 + " mWindowVisible=" + mWindowVisible
The Android Open Source Project10592532009-03-18 17:39:46 -07003254 + " mInShowWindow=" + mInShowWindow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003255 p.println(" Configuration=" + getResources().getConfiguration());
3256 p.println(" mToken=" + mToken);
3257 p.println(" mInputBinding=" + mInputBinding);
3258 p.println(" mInputConnection=" + mInputConnection);
3259 p.println(" mStartedInputConnection=" + mStartedInputConnection);
3260 p.println(" mInputStarted=" + mInputStarted
3261 + " mInputViewStarted=" + mInputViewStarted
3262 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08003263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003264 if (mInputEditorInfo != null) {
3265 p.println(" mInputEditorInfo:");
3266 mInputEditorInfo.dump(p, " ");
3267 } else {
3268 p.println(" mInputEditorInfo: null");
3269 }
3270
3271 p.println(" mShowInputRequested=" + mShowInputRequested
3272 + " mLastShowInputRequested=" + mLastShowInputRequested
Tarandeep Singheadb1392018-11-09 18:15:57 +01003273 + " mCanPreRender=" + mCanPreRender
3274 + " mIsPreRendered=" + mIsPreRendered
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003275 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
3276 p.println(" mCandidatesVisibility=" + mCandidatesVisibility
3277 + " mFullscreenApplied=" + mFullscreenApplied
The Android Open Source Project10592532009-03-18 17:39:46 -07003278 + " mIsFullscreen=" + mIsFullscreen
3279 + " mExtractViewHidden=" + mExtractViewHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003280
3281 if (mExtractedText != null) {
3282 p.println(" mExtractedText:");
3283 p.println(" text=" + mExtractedText.text.length() + " chars"
3284 + " startOffset=" + mExtractedText.startOffset);
3285 p.println(" selectionStart=" + mExtractedText.selectionStart
3286 + " selectionEnd=" + mExtractedText.selectionEnd
3287 + " flags=0x" + Integer.toHexString(mExtractedText.flags));
3288 } else {
3289 p.println(" mExtractedText: null");
3290 }
3291 p.println(" mExtractedToken=" + mExtractedToken);
3292 p.println(" mIsInputViewShown=" + mIsInputViewShown
3293 + " mStatusIcon=" + mStatusIcon);
3294 p.println("Last computed insets:");
3295 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
3296 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
Jeff Brownfbf09772011-01-16 14:06:57 -08003297 + " touchableInsets=" + mTmpInsets.touchableInsets
3298 + " touchableRegion=" + mTmpInsets.touchableRegion);
Yohei Yukawa7b739a82015-12-21 13:30:44 -08003299 p.println(" mSettingsObserver=" + mSettingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003300 }
3301}