blob: 4c015ba9e2d318d4c8924597db3b665c1002a952 [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.view.inputmethod;
18
Svetoslav Ganov758143e2012-08-06 16:40:27 -070019import com.android.internal.os.SomeArgs;
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080020import com.android.internal.view.IInputConnectionWrapper;
21import com.android.internal.view.IInputContext;
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080022import com.android.internal.view.IInputMethodClient;
23import com.android.internal.view.IInputMethodManager;
24import com.android.internal.view.IInputMethodSession;
25import com.android.internal.view.InputBindResult;
Yohei Yukawa33e81792015-11-17 21:14:42 -080026import com.android.internal.view.InputMethodClient;
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080027
Yohei Yukawa2afe2aa2016-01-07 18:09:44 -080028import android.annotation.NonNull;
29import android.annotation.Nullable;
Yoshiki Iguchi00d51222015-05-29 15:36:22 +090030import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.Context;
32import android.graphics.Rect;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.IBinder;
36import android.os.Looper;
37import android.os.Message;
38import android.os.RemoteException;
The Android Open Source Project4df24232009-03-05 14:34:35 -080039import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.ServiceManager;
Jeff Brownf9e989d2013-04-04 23:04:03 -070041import android.os.Trace;
satokf9f01002011-05-19 21:31:50 +090042import android.text.style.SuggestionSpan;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.util.Log;
Jeff Brownf9e989d2013-04-04 23:04:03 -070044import android.util.Pools.Pool;
45import android.util.Pools.SimplePool;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.util.PrintWriterPrinter;
47import android.util.Printer;
Yohei Yukawab7b79072014-03-25 11:02:00 +090048import android.util.SparseArray;
Jeff Brownc28867a2013-03-26 15:42:39 -070049import android.view.InputChannel;
50import android.view.InputEvent;
51import android.view.InputEventSender;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.view.View;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -070054import android.view.ViewRootImpl;
Gilles Debunned4723bb2010-09-02 15:27:32 -070055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import java.io.FileDescriptor;
57import java.io.PrintWriter;
satok4e4569d2010-11-19 18:45:53 +090058import java.util.ArrayList;
Andreas Gampee6748ce2015-12-11 18:00:38 -080059import java.util.Arrays;
satokf3db1af2010-11-23 13:34:33 +090060import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import java.util.List;
satokf3db1af2010-11-23 13:34:33 +090062import java.util.Map;
Yohei Yukawac941fed2014-05-14 19:33:35 +090063import java.util.Objects;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import java.util.concurrent.CountDownLatch;
65import java.util.concurrent.TimeUnit;
66
Yoshiki Iguchi00d51222015-05-29 15:36:22 +090067import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
68
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069/**
70 * Central system API to the overall input method framework (IMF) architecture,
71 * which arbitrates interaction between applications and the current input method.
72 * You can retrieve an instance of this interface with
73 * {@link Context#getSystemService(String) Context.getSystemService()}.
74 *
75 * <p>Topics covered here:
76 * <ol>
77 * <li><a href="#ArchitectureOverview">Architecture Overview</a>
Ken Wakasa384f8ba2012-03-10 09:59:31 +090078 * <li><a href="#Applications">Applications</a>
79 * <li><a href="#InputMethods">Input Methods</a>
80 * <li><a href="#Security">Security</a>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 * </ol>
82 *
83 * <a name="ArchitectureOverview"></a>
84 * <h3>Architecture Overview</h3>
85 *
86 * <p>There are three primary parties involved in the input method
87 * framework (IMF) architecture:</p>
88 *
89 * <ul>
90 * <li> The <strong>input method manager</strong> as expressed by this class
91 * is the central point of the system that manages interaction between all
92 * other parts. It is expressed as the client-side API here which exists
93 * in each application context and communicates with a global system service
94 * that manages the interaction across all processes.
95 * <li> An <strong>input method (IME)</strong> implements a particular
96 * interaction model allowing the user to generate text. The system binds
97 * to the current input method that is use, causing it to be created and run,
98 * and tells it when to hide and show its UI. Only one IME is running at a time.
99 * <li> Multiple <strong>client applications</strong> arbitrate with the input
100 * method manager for input focus and control over the state of the IME. Only
101 * one such client is ever active (working with the IME) at a time.
102 * </ul>
103 *
104 *
105 * <a name="Applications"></a>
106 * <h3>Applications</h3>
107 *
108 * <p>In most cases, applications that are using the standard
109 * {@link android.widget.TextView} or its subclasses will have little they need
110 * to do to work well with soft input methods. The main things you need to
111 * be aware of are:</p>
112 *
113 * <ul>
Gilles Debunne8cbb4c62011-01-24 12:33:56 -0800114 * <li> Properly set the {@link android.R.attr#inputType} in your editable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 * text views, so that the input method will have enough context to help the
116 * user in entering text into them.
117 * <li> Deal well with losing screen space when the input method is
118 * displayed. Ideally an application should handle its window being resized
119 * smaller, but it can rely on the system performing panning of the window
120 * if needed. You should set the {@link android.R.attr#windowSoftInputMode}
121 * attribute on your activity or the corresponding values on windows you
122 * create to help the system determine whether to pan or resize (it will
123 * try to determine this automatically but may get it wrong).
124 * <li> You can also control the preferred soft input state (open, closed, etc)
125 * for your window using the same {@link android.R.attr#windowSoftInputMode}
126 * attribute.
127 * </ul>
128 *
129 * <p>More finer-grained control is available through the APIs here to directly
130 * interact with the IMF and its IME -- either showing or hiding the input
131 * area, letting the user pick an input method, etc.</p>
132 *
133 * <p>For the rare people amongst us writing their own text editors, you
134 * will need to implement {@link android.view.View#onCreateInputConnection}
135 * to return a new instance of your own {@link InputConnection} interface
136 * allowing the IME to interact with your editor.</p>
137 *
138 *
139 * <a name="InputMethods"></a>
140 * <h3>Input Methods</h3>
141 *
142 * <p>An input method (IME) is implemented
143 * as a {@link android.app.Service}, typically deriving from
144 * {@link android.inputmethodservice.InputMethodService}. It must provide
145 * the core {@link InputMethod} interface, though this is normally handled by
146 * {@link android.inputmethodservice.InputMethodService} and implementors will
147 * only need to deal with the higher-level API there.</p>
148 *
149 * See the {@link android.inputmethodservice.InputMethodService} class for
150 * more information on implementing IMEs.
151 *
152 *
153 * <a name="Security"></a>
154 * <h3>Security</h3>
155 *
156 * <p>There are a lot of security issues associated with input methods,
157 * since they essentially have freedom to completely drive the UI and monitor
158 * everything the user enters. The Android input method framework also allows
159 * arbitrary third party IMEs, so care must be taken to restrict their
160 * selection and interactions.</p>
161 *
162 * <p>Here are some key points about the security architecture behind the
163 * IMF:</p>
164 *
165 * <ul>
166 * <li> <p>Only the system is allowed to directly access an IME's
167 * {@link InputMethod} interface, via the
168 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission. This is
169 * enforced in the system by not binding to an input method service that does
170 * not require this permission, so the system can guarantee no other untrusted
171 * clients are accessing the current input method outside of its control.</p>
172 *
173 * <li> <p>There may be many client processes of the IMF, but only one may
174 * be active at a time. The inactive clients can not interact with key
175 * parts of the IMF through the mechanisms described below.</p>
176 *
177 * <li> <p>Clients of an input method are only given access to its
178 * {@link InputMethodSession} interface. One instance of this interface is
179 * created for each client, and only calls from the session associated with
180 * the active client will be processed by the current IME. This is enforced
181 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
182 * IMEs, but must be explicitly handled by an IME that is customizing the
183 * raw {@link InputMethodSession} implementation.</p>
184 *
185 * <li> <p>Only the active client's {@link InputConnection} will accept
186 * operations. The IMF tells each client process whether it is active, and
187 * the framework enforces that in inactive processes calls on to the current
188 * InputConnection will be ignored. This ensures that the current IME can
189 * only deliver events and text edits to the UI that the user sees as
190 * being in focus.</p>
191 *
192 * <li> <p>An IME can never interact with an {@link InputConnection} while
193 * the screen is off. This is enforced by making all clients inactive while
194 * the screen is off, and prevents bad IMEs from driving the UI when the user
195 * can not be aware of its behavior.</p>
196 *
197 * <li> <p>A client application can ask that the system let the user pick a
198 * new IME, but can not programmatically switch to one itself. This avoids
199 * malicious applications from switching the user to their own IME, which
200 * remains running when the user navigates away to another application. An
201 * IME, on the other hand, <em>is</em> allowed to programmatically switch
202 * the system to another IME, since it already has full control of user
203 * input.</p>
204 *
205 * <li> <p>The user must explicitly enable a new IME in settings before
206 * they can switch to it, to confirm with the system that they know about it
207 * and want to make it available for use.</p>
208 * </ul>
209 */
210public final class InputMethodManager {
Dianne Hackborn4eba2712009-03-24 19:20:06 -0700211 static final boolean DEBUG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 static final String TAG = "InputMethodManager";
213
Jeff Brownf9e989d2013-04-04 23:04:03 -0700214 static final String PENDING_EVENT_COUNTER = "aq:imm";
215
216 static InputMethodManager sInstance;
Dianne Hackborn7663d802012-02-24 13:08:49 -0800217
218 /**
219 * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
220 * the window has input focus.
221 */
222 public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0;
223
224 /**
225 * @hide Flag for IInputMethodManager.windowGainedFocus: the focus
226 * is a text editor.
227 */
228 public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1;
229
230 /**
231 * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first
232 * time the window has gotten focus.
233 */
234 public static final int CONTROL_WINDOW_FIRST = 1<<2;
235
236 /**
237 * @hide Flag for IInputMethodManager.startInput: this is the first
238 * time the window has gotten focus.
239 */
240 public static final int CONTROL_START_INITIAL = 1<<8;
241
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700242 /**
243 * Timeout in milliseconds for delivering a key to an IME.
244 */
245 static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
246
Jeff Brownf9e989d2013-04-04 23:04:03 -0700247 /** @hide */
248 public static final int DISPATCH_IN_PROGRESS = -1;
249
250 /** @hide */
251 public static final int DISPATCH_NOT_HANDLED = 0;
252
253 /** @hide */
254 public static final int DISPATCH_HANDLED = 1;
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700255
Seigo Nonaka14e13912015-05-06 21:04:13 -0700256 /** @hide */
257 public static final int SHOW_IM_PICKER_MODE_AUTO = 0;
258 /** @hide */
259 public static final int SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES = 1;
260 /** @hide */
261 public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2;
262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 final IInputMethodManager mService;
264 final Looper mMainLooper;
265
266 // For scheduling work on the main thread. This also serves as our
267 // global lock.
268 final H mH;
269
270 // Our generic input connection if the current target does not have its own.
271 final IInputContext mIInputContext;
272
273 /**
274 * True if this input method client is active, initially false.
275 */
276 boolean mActive = false;
277
278 /**
279 * Set whenever this client becomes inactive, to know we need to reset
Dianne Hackborn7663d802012-02-24 13:08:49 -0800280 * state with the IME the next time we receive focus.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 */
282 boolean mHasBeenInactive = true;
283
284 /**
285 * As reported by IME through InputConnection.
286 */
287 boolean mFullscreenMode;
288
289 // -----------------------------------------------------------
Wale Ogunwale159c3d82015-05-14 12:20:53 -0700290
291 /**
292 * This is the root view of the overall window that currently has input
293 * method focus.
294 */
295 View mCurRootView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 /**
297 * This is the view that should currently be served by an input method,
298 * regardless of the state of setting that up.
299 */
300 View mServedView;
301 /**
302 * This is then next view that will be served by the input method, when
303 * we get around to updating things.
304 */
305 View mNextServedView;
306 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 * This is set when we are in the process of connecting, to determine
308 * when we have actually finished.
309 */
310 boolean mServedConnecting;
311 /**
312 * This is non-null when we have connected the served view; it holds
313 * the attributes that were last retrieved from the served view and given
314 * to the input connection.
315 */
316 EditorInfo mCurrentTextBoxAttribute;
317 /**
318 * The InputConnection that was last retrieved from the served view.
319 */
320 InputConnection mServedInputConnection;
Dianne Hackbornac920872012-05-22 11:49:49 -0700321 ControlledInputConnectionWrapper mServedInputConnectionWrapper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 /**
323 * The completions that were last provided by the served view.
324 */
325 CompletionInfo[] mCompletions;
326
327 // Cursor position on the screen.
Yohei Yukawaa277db22014-08-21 18:38:44 -0700328 Rect mTmpCursorRect = new Rect();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 Rect mCursorRect = new Rect();
330 int mCursorSelStart;
331 int mCursorSelEnd;
332 int mCursorCandStart;
333 int mCursorCandEnd;
Yohei Yukawa056ffe62014-05-13 14:26:09 +0900334
335 /**
Yohei Yukawa3d1e8122014-06-06 19:12:47 +0900336 * Represents an invalid action notification sequence number. {@link InputMethodManagerService}
337 * always issues a positive integer for action notification sequence numbers. Thus -1 is
338 * guaranteed to be different from any valid sequence number.
339 */
340 private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1;
341 /**
342 * The next sequence number that is to be sent to {@link InputMethodManagerService} via
343 * {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed.
344 */
345 private int mNextUserActionNotificationSequenceNumber =
346 NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
347
348 /**
349 * The last sequence number that is already sent to {@link InputMethodManagerService}.
350 */
351 private int mLastSentUserActionNotificationSequenceNumber =
352 NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
353
354 /**
Yohei Yukawa056ffe62014-05-13 14:26:09 +0900355 * The instance that has previously been sent to the input method.
356 */
357 private CursorAnchorInfo mCursorAnchorInfo = null;
358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 // -----------------------------------------------------------
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900360
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 /**
362 * Sequence number of this binding, as returned by the server.
363 */
364 int mBindSequence = -1;
365 /**
366 * ID of the method we are bound to.
367 */
368 String mCurId;
369 /**
370 * The actual instance of the method to make calls on it.
371 */
372 IInputMethodSession mCurMethod;
Jeff Brownc28867a2013-03-26 15:42:39 -0700373 InputChannel mCurChannel;
374 ImeInputEventSender mCurSender;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375
Yohei Yukawaa277db22014-08-21 18:38:44 -0700376 private static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE = 0x0;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900377
378 /**
379 * The monitor mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}.
380 */
Yohei Yukawaa277db22014-08-21 18:38:44 -0700381 private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
Yohei Yukawab7b79072014-03-25 11:02:00 +0900382
Yohei Yukawab0377bb2015-08-10 21:06:30 -0700383 final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
384 final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700385
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 // -----------------------------------------------------------
387
388 static final int MSG_DUMP = 1;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800389 static final int MSG_BIND = 2;
390 static final int MSG_UNBIND = 3;
391 static final int MSG_SET_ACTIVE = 4;
Jeff Brownf9e989d2013-04-04 23:04:03 -0700392 static final int MSG_SEND_INPUT_EVENT = 5;
393 static final int MSG_TIMEOUT_INPUT_EVENT = 6;
394 static final int MSG_FLUSH_INPUT_EVENT = 7;
Yohei Yukawa3d1e8122014-06-06 19:12:47 +0900395 static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9;
Jeff Brownf9e989d2013-04-04 23:04:03 -0700396
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 class H extends Handler {
398 H(Looper looper) {
Jeff Brown29c0ed22013-01-14 13:50:37 -0800399 super(looper, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 }
401
402 @Override
403 public void handleMessage(Message msg) {
404 switch (msg.what) {
405 case MSG_DUMP: {
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700406 SomeArgs args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 try {
408 doDump((FileDescriptor)args.arg1,
409 (PrintWriter)args.arg2, (String[])args.arg3);
410 } catch (RuntimeException e) {
411 ((PrintWriter)args.arg2).println("Exception: " + e);
412 }
413 synchronized (args.arg4) {
414 ((CountDownLatch)args.arg4).countDown();
415 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700416 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 return;
418 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800419 case MSG_BIND: {
420 final InputBindResult res = (InputBindResult)msg.obj;
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +0900421 if (DEBUG) {
422 Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
423 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800424 synchronized (mH) {
425 if (mBindSequence < 0 || mBindSequence != res.sequence) {
426 Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
427 + ", given seq=" + res.sequence);
Jeff Brown4d656882013-04-03 14:39:19 -0700428 if (res.channel != null && res.channel != mCurChannel) {
Jeff Brownc28867a2013-03-26 15:42:39 -0700429 res.channel.dispose();
430 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800431 return;
432 }
Jeff Brown4d656882013-04-03 14:39:19 -0700433
Yohei Yukawaa277db22014-08-21 18:38:44 -0700434 mRequestUpdateCursorAnchorInfoMonitorMode =
435 REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900436
Jeff Brown4d656882013-04-03 14:39:19 -0700437 setInputChannelLocked(res.channel);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800438 mCurMethod = res.method;
439 mCurId = res.id;
440 mBindSequence = res.sequence;
441 }
Yohei Yukawa35d3f372015-11-25 11:07:19 -0800442 startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
443 null, 0, 0, 0);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800444 return;
445 }
446 case MSG_UNBIND: {
447 final int sequence = msg.arg1;
Yohei Yukawa33e81792015-11-17 21:14:42 -0800448 @InputMethodClient.UnbindReason
449 final int reason = msg.arg2;
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +0900450 if (DEBUG) {
Yohei Yukawa33e81792015-11-17 21:14:42 -0800451 Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence +
452 " reason=" + InputMethodClient.getUnbindReason(reason));
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +0900453 }
Yohei Yukawa678e38e2015-11-13 18:36:21 -0800454 final boolean startInput;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800455 synchronized (mH) {
Yohei Yukawa678e38e2015-11-13 18:36:21 -0800456 if (mBindSequence != sequence) {
457 return;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800458 }
Yohei Yukawa678e38e2015-11-13 18:36:21 -0800459 clearBindingLocked();
460 // If we were actively using the last input method, then
461 // we would like to re-connect to the next input method.
462 if (mServedView != null && mServedView.isFocused()) {
463 mServedConnecting = true;
464 }
465 startInput = mActive;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800466 }
Dianne Hackborn06a591c2012-02-16 10:25:26 -0800467 if (startInput) {
Yohei Yukawa35d3f372015-11-25 11:07:19 -0800468 startInputInner(
469 InputMethodClient.START_INPUT_REASON_UNBOUND_FROM_IMMS, null, 0, 0,
470 0);
Dianne Hackborn06a591c2012-02-16 10:25:26 -0800471 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800472 return;
473 }
474 case MSG_SET_ACTIVE: {
475 final boolean active = msg.arg1 != 0;
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +0900476 if (DEBUG) {
477 Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive);
478 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800479 synchronized (mH) {
480 mActive = active;
481 mFullscreenMode = false;
482 if (!active) {
483 // Some other client has starting using the IME, so note
484 // that this happened and make sure our own editor's
485 // state is reset.
486 mHasBeenInactive = true;
487 try {
488 // Note that finishComposingText() is allowed to run
489 // even when we are not active.
490 mIInputContext.finishComposingText();
491 } catch (RemoteException e) {
492 }
Mikael Gullstrand82ae3ff2014-11-25 12:41:53 +0100493 }
494 // Check focus again in case that "onWindowFocus" is called before
495 // handling this message.
496 if (mServedView != null && mServedView.hasWindowFocus()) {
497 // Please note that this handler thread could be different
498 // from a thread that created mServedView. That could happen
499 // the current activity is running in the system process.
500 // In that case, we really should not call
501 // mServedInputConnection.finishComposingText.
502 if (checkFocusNoStartInput(mHasBeenInactive, false)) {
Yohei Yukawa35d3f372015-11-25 11:07:19 -0800503 final int reason = active ?
504 InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
505 InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
506 startInputInner(reason, null, 0, 0, 0);
satok05a6cbe2012-04-05 23:04:08 +0900507 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800508 }
509 }
510 return;
511 }
Jeff Brownf9e989d2013-04-04 23:04:03 -0700512 case MSG_SEND_INPUT_EVENT: {
513 sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj);
514 return;
515 }
516 case MSG_TIMEOUT_INPUT_EVENT: {
517 finishedInputEvent(msg.arg1, false, true);
518 return;
519 }
520 case MSG_FLUSH_INPUT_EVENT: {
521 finishedInputEvent(msg.arg1, false, false);
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700522 return;
523 }
Yohei Yukawa3d1e8122014-06-06 19:12:47 +0900524 case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
525 synchronized (mH) {
526 mNextUserActionNotificationSequenceNumber = msg.arg1;
527 }
528 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 }
530 }
531 }
Yohei Yukawa159dd472016-01-07 16:52:33 -0800532
Jean Chalardde9dbb02011-10-20 19:50:45 +0900533 private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
534 private final InputMethodManager mParentInputMethodManager;
Dianne Hackbornac920872012-05-22 11:49:49 -0700535 private boolean mActive;
Jean Chalardde9dbb02011-10-20 19:50:45 +0900536
537 public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
538 final InputMethodManager inputMethodManager) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 super(mainLooper, conn);
Jean Chalardde9dbb02011-10-20 19:50:45 +0900540 mParentInputMethodManager = inputMethodManager;
Dianne Hackbornac920872012-05-22 11:49:49 -0700541 mActive = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 }
543
Gilles Debunne8cbb4c62011-01-24 12:33:56 -0800544 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 public boolean isActive() {
Dianne Hackbornac920872012-05-22 11:49:49 -0700546 return mParentInputMethodManager.mActive && mActive;
547 }
548
549 void deactivate() {
550 mActive = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 }
Yohei Yukawa12d66c22015-11-18 13:44:14 -0800552
553 @Override
Yohei Yukawa159dd472016-01-07 16:52:33 -0800554 protected void onUserAction() {
555 mParentInputMethodManager.notifyUserAction();
556 }
557
558 @Override
559 protected void onReportFullscreenMode(boolean enabled) {
560 mParentInputMethodManager.setFullscreenMode(enabled);
561 }
562
563 @Override
Yohei Yukawa12d66c22015-11-18 13:44:14 -0800564 public String toString() {
565 return "ControlledInputConnectionWrapper{mActive=" + mActive
566 + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
567 + "}";
568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 }
Yohei Yukawa159dd472016-01-07 16:52:33 -0800570
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
Jeff Brownc28867a2013-03-26 15:42:39 -0700572 @Override
573 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 // No need to check for dump permission, since we only give this
575 // interface to the system.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 CountDownLatch latch = new CountDownLatch(1);
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700577 SomeArgs sargs = SomeArgs.obtain();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 sargs.arg1 = fd;
579 sargs.arg2 = fout;
580 sargs.arg3 = args;
581 sargs.arg4 = latch;
582 mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
583 try {
584 if (!latch.await(5, TimeUnit.SECONDS)) {
585 fout.println("Timeout waiting for dump");
586 }
587 } catch (InterruptedException e) {
588 fout.println("Interrupted waiting for dump");
589 }
590 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700591
592 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 public void setUsingInputMethod(boolean state) {
594 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700595
596 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 public void onBindMethod(InputBindResult res) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800598 mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700600
601 @Override
Yohei Yukawa33e81792015-11-17 21:14:42 -0800602 public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) {
603 mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, unbindReason));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700605
606 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 public void setActive(boolean active) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800608 mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 }
Yohei Yukawab7b79072014-03-25 11:02:00 +0900610
611 @Override
Yohei Yukawa3d1e8122014-06-06 19:12:47 +0900612 public void setUserActionNotificationSequenceNumber(int sequenceNumber) {
613 mH.sendMessage(mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
614 sequenceNumber, 0));
615 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700616 };
617
Dianne Hackborn51bf0772009-03-24 19:11:41 -0700618 final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700619
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 InputMethodManager(IInputMethodManager service, Looper looper) {
621 mService = service;
622 mMainLooper = looper;
623 mH = new H(looper);
624 mIInputContext = new ControlledInputConnectionWrapper(looper,
Jean Chalardde9dbb02011-10-20 19:50:45 +0900625 mDummyInputConnection, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 }
627
628 /**
629 * Retrieve the global InputMethodManager instance, creating it if it
630 * doesn't already exist.
631 * @hide
632 */
Jeff Brownf9e989d2013-04-04 23:04:03 -0700633 public static InputMethodManager getInstance() {
634 synchronized (InputMethodManager.class) {
635 if (sInstance == null) {
636 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
637 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
638 sInstance = new InputMethodManager(service, Looper.getMainLooper());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 }
Jeff Brownf9e989d2013-04-04 23:04:03 -0700640 return sInstance;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 }
643
644 /**
645 * Private optimization: retrieve the global InputMethodManager instance,
646 * if it exists.
647 * @hide
648 */
Jeff Brownf9e989d2013-04-04 23:04:03 -0700649 public static InputMethodManager peekInstance() {
650 return sInstance;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 }
652
653 /** @hide */
654 public IInputMethodClient getClient() {
655 return mClient;
656 }
657
658 /** @hide */
659 public IInputContext getInputContext() {
660 return mIInputContext;
661 }
662
663 public List<InputMethodInfo> getInputMethodList() {
664 try {
665 return mService.getInputMethodList();
666 } catch (RemoteException e) {
667 throw new RuntimeException(e);
668 }
669 }
670
671 public List<InputMethodInfo> getEnabledInputMethodList() {
672 try {
673 return mService.getEnabledInputMethodList();
674 } catch (RemoteException e) {
675 throw new RuntimeException(e);
676 }
677 }
678
satokd4fce2b2011-04-11 12:07:13 +0900679 /**
680 * Returns a list of enabled input method subtypes for the specified input method info.
681 * @param imi An input method info whose subtypes list will be returned.
682 * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly
683 * selected subtypes. If an input method info doesn't have enabled subtypes, the framework
684 * will implicitly enable subtypes according to the current system language.
685 */
satok16331c82010-12-20 23:48:46 +0900686 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
687 boolean allowsImplicitlySelectedSubtypes) {
satok67ddf9c2010-11-17 09:45:54 +0900688 try {
Satoshi Kataokab3c21ac2013-08-07 15:43:29 +0900689 return mService.getEnabledInputMethodSubtypeList(
690 imi == null ? null : imi.getId(), allowsImplicitlySelectedSubtypes);
satok67ddf9c2010-11-17 09:45:54 +0900691 } catch (RemoteException e) {
692 throw new RuntimeException(e);
693 }
694 }
695
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
697 try {
698 mService.updateStatusIcon(imeToken, packageName, iconId);
699 } catch (RemoteException e) {
700 throw new RuntimeException(e);
701 }
702 }
703
704 public void hideStatusIcon(IBinder imeToken) {
705 try {
706 mService.updateStatusIcon(imeToken, null, 0);
707 } catch (RemoteException e) {
708 throw new RuntimeException(e);
709 }
710 }
711
712 /** @hide */
Joe Onorato857fd9b2011-01-27 15:08:35 -0800713 public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
satok06487a52010-10-29 11:37:18 +0900714 try {
Joe Onorato857fd9b2011-01-27 15:08:35 -0800715 mService.setImeWindowStatus(imeToken, vis, backDisposition);
satok06487a52010-10-29 11:37:18 +0900716 } catch (RemoteException e) {
717 throw new RuntimeException(e);
718 }
719 }
720
721 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 public void setFullscreenMode(boolean fullScreen) {
723 mFullscreenMode = fullScreen;
724 }
satokf9f01002011-05-19 21:31:50 +0900725
726 /** @hide */
727 public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
728 try {
729 mService.registerSuggestionSpansForNotification(spans);
730 } catch (RemoteException e) {
731 throw new RuntimeException(e);
732 }
733 }
734
735 /** @hide */
736 public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
737 try {
738 mService.notifySuggestionPicked(span, originalString, index);
739 } catch (RemoteException e) {
740 throw new RuntimeException(e);
741 }
742 }
743
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 /**
745 * Allows you to discover whether the attached input method is running
746 * in fullscreen mode. Return true if it is fullscreen, entirely covering
747 * your UI, else returns false.
748 */
749 public boolean isFullscreenMode() {
750 return mFullscreenMode;
751 }
752
753 /**
754 * Return true if the given view is the currently active view for the
755 * input method.
756 */
757 public boolean isActive(View view) {
758 checkFocus();
759 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700760 return (mServedView == view
761 || (mServedView != null
762 && mServedView.checkInputConnectionProxy(view)))
763 && mCurrentTextBoxAttribute != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 }
765 }
766
767 /**
768 * Return true if any view is currently active in the input method.
769 */
770 public boolean isActive() {
771 checkFocus();
772 synchronized (mH) {
773 return mServedView != null && mCurrentTextBoxAttribute != null;
774 }
775 }
776
777 /**
778 * Return true if the currently served view is accepting full text edits.
779 * If false, it has no input connection, so can only handle raw key events.
780 */
781 public boolean isAcceptingText() {
782 checkFocus();
783 return mServedInputConnection != null;
784 }
785
786 /**
787 * Reset all of the state associated with being bound to an input method.
788 */
789 void clearBindingLocked() {
Dianne Hackborn6b6b3fd2014-03-24 11:27:18 -0700790 if (DEBUG) Log.v(TAG, "Clearing binding!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 clearConnectionLocked();
Jeff Brown4d656882013-04-03 14:39:19 -0700792 setInputChannelLocked(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 mBindSequence = -1;
794 mCurId = null;
795 mCurMethod = null;
Jeff Brown4d656882013-04-03 14:39:19 -0700796 }
797
798 void setInputChannelLocked(InputChannel channel) {
799 if (mCurChannel != channel) {
800 if (mCurSender != null) {
801 flushPendingEventsLocked();
802 mCurSender.dispose();
803 mCurSender = null;
804 }
805 if (mCurChannel != null) {
806 mCurChannel.dispose();
807 }
808 mCurChannel = channel;
Jeff Brownc28867a2013-03-26 15:42:39 -0700809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 }
Jeff Brown4d656882013-04-03 14:39:19 -0700811
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 /**
813 * Reset all of the state associated with a served view being connected
814 * to an input method
815 */
816 void clearConnectionLocked() {
817 mCurrentTextBoxAttribute = null;
818 mServedInputConnection = null;
Dianne Hackbornac920872012-05-22 11:49:49 -0700819 if (mServedInputConnectionWrapper != null) {
820 mServedInputConnectionWrapper.deactivate();
821 mServedInputConnectionWrapper = null;
822 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 }
Yohei Yukawa0f3a99d2015-05-21 00:15:05 -0700824
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 /**
826 * Disconnect any existing input connection, clearing the served view.
827 */
828 void finishInputLocked() {
829 mNextServedView = null;
830 if (mServedView != null) {
831 if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 if (mCurrentTextBoxAttribute != null) {
833 try {
834 mService.finishInput(mClient);
835 } catch (RemoteException e) {
836 }
837 }
Gilles Debunnec478c172011-12-19 17:29:24 -0800838 notifyInputConnectionFinished();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 mServedView = null;
840 mCompletions = null;
841 mServedConnecting = false;
842 clearConnectionLocked();
843 }
844 }
Gilles Debunnec478c172011-12-19 17:29:24 -0800845
846 /**
847 * Notifies the served view that the current InputConnection will no longer be used.
848 */
849 private void notifyInputConnectionFinished() {
850 if (mServedView != null && mServedInputConnection != null) {
851 // We need to tell the previously served view that it is no
852 // longer the input target, so it can reset its state. Schedule
853 // this call on its window's Handler so it will be on the correct
854 // thread and outside of our lock.
Jeff Browna175a5b2012-02-15 19:18:31 -0800855 ViewRootImpl viewRootImpl = mServedView.getViewRootImpl();
856 if (viewRootImpl != null) {
Gilles Debunnec478c172011-12-19 17:29:24 -0800857 // This will result in a call to reportFinishInputConnection() below.
Jeff Browna175a5b2012-02-15 19:18:31 -0800858 viewRootImpl.dispatchFinishInputConnection(mServedInputConnection);
Gilles Debunnec478c172011-12-19 17:29:24 -0800859 }
860 }
861 }
862
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 /**
864 * Called from the FINISH_INPUT_CONNECTION message above.
865 * @hide
866 */
867 public void reportFinishInputConnection(InputConnection ic) {
868 if (mServedInputConnection != ic) {
869 ic.finishComposingText();
Gilles Debunne9d69ecb2012-02-24 16:07:09 -0800870 // To avoid modifying the public InputConnection interface
871 if (ic instanceof BaseInputConnection) {
872 ((BaseInputConnection) ic).reportFinish();
873 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 }
875 }
Gilles Debunnec478c172011-12-19 17:29:24 -0800876
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 public void displayCompletions(View view, CompletionInfo[] completions) {
878 checkFocus();
879 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700880 if (mServedView != view && (mServedView == null
881 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 return;
883 }
884
885 mCompletions = completions;
886 if (mCurMethod != null) {
887 try {
888 mCurMethod.displayCompletions(mCompletions);
889 } catch (RemoteException e) {
890 }
891 }
892 }
893 }
894
895 public void updateExtractedText(View view, int token, ExtractedText text) {
896 checkFocus();
897 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700898 if (mServedView != view && (mServedView == null
899 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 return;
901 }
902
903 if (mCurMethod != null) {
904 try {
905 mCurMethod.updateExtractedText(token, text);
906 } catch (RemoteException e) {
907 }
908 }
909 }
910 }
911
912 /**
913 * Flag for {@link #showSoftInput} to indicate that this is an implicit
914 * request to show the input window, not as the result of a direct request
915 * by the user. The window may not be shown in this case.
916 */
917 public static final int SHOW_IMPLICIT = 0x0001;
918
919 /**
920 * Flag for {@link #showSoftInput} to indicate that the user has forced
921 * the input method open (such as by long-pressing menu) so it should
922 * not be closed until they explicitly do so.
923 */
924 public static final int SHOW_FORCED = 0x0002;
925
926 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -0800927 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
928 * a result receiver: explicitly request that the current input method's
929 * soft input area be shown to the user, if needed.
930 *
931 * @param view The currently focused view, which would like to receive
932 * soft keyboard input.
933 * @param flags Provides additional operating flags. Currently may be
934 * 0 or have the {@link #SHOW_IMPLICIT} bit set.
935 */
936 public boolean showSoftInput(View view, int flags) {
937 return showSoftInput(view, flags, null);
938 }
939
940 /**
941 * Flag for the {@link ResultReceiver} result code from
942 * {@link #showSoftInput(View, int, ResultReceiver)} and
943 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
944 * state of the soft input window was unchanged and remains shown.
945 */
946 public static final int RESULT_UNCHANGED_SHOWN = 0;
947
948 /**
949 * Flag for the {@link ResultReceiver} result code from
950 * {@link #showSoftInput(View, int, ResultReceiver)} and
951 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
952 * state of the soft input window was unchanged and remains hidden.
953 */
954 public static final int RESULT_UNCHANGED_HIDDEN = 1;
955
956 /**
957 * Flag for the {@link ResultReceiver} result code from
958 * {@link #showSoftInput(View, int, ResultReceiver)} and
959 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
960 * state of the soft input window changed from hidden to shown.
961 */
962 public static final int RESULT_SHOWN = 2;
963
964 /**
965 * Flag for the {@link ResultReceiver} result code from
966 * {@link #showSoftInput(View, int, ResultReceiver)} and
967 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
968 * state of the soft input window changed from shown to hidden.
969 */
970 public static final int RESULT_HIDDEN = 3;
971
972 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 * Explicitly request that the current input method's soft input area be
974 * shown to the user, if needed. Call this if the user interacts with
975 * your view in such a way that they have expressed they would like to
976 * start performing input into it.
977 *
978 * @param view The currently focused view, which would like to receive
979 * soft keyboard input.
980 * @param flags Provides additional operating flags. Currently may be
981 * 0 or have the {@link #SHOW_IMPLICIT} bit set.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800982 * @param resultReceiver If non-null, this will be called by the IME when
983 * it has processed your request to tell you what it has done. The result
984 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
985 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
986 * {@link #RESULT_HIDDEN}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 */
Gilles Debunnead8484b2011-02-17 17:37:51 -0800988 public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 checkFocus();
990 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700991 if (mServedView != view && (mServedView == null
992 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800993 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 }
995
996 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800997 return mService.showSoftInput(mClient, flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 } catch (RemoteException e) {
999 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001000
1001 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 }
1003 }
1004
1005 /** @hide */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001006 public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001008 mService.showSoftInput(mClient, flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 } catch (RemoteException e) {
1010 }
1011 }
1012
1013 /**
1014 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
1015 * input window should only be hidden if it was not explicitly shown
1016 * by the user.
1017 */
1018 public static final int HIDE_IMPLICIT_ONLY = 0x0001;
1019
1020 /**
1021 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
1022 * input window should normally be hidden, unless it was originally
1023 * shown with {@link #SHOW_FORCED}.
1024 */
1025 public static final int HIDE_NOT_ALWAYS = 0x0002;
Gilles Debunnec478c172011-12-19 17:29:24 -08001026
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 /**
Gilles Debunne7c8c6d62011-01-24 14:48:14 -08001028 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}
The Android Open Source Project4df24232009-03-05 14:34:35 -08001029 * without a result: request to hide the soft input window from the
1030 * context of the window that is currently accepting input.
1031 *
1032 * @param windowToken The token of the window that is making the request,
1033 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
1034 * @param flags Provides additional operating flags. Currently may be
1035 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
1036 */
1037 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
1038 return hideSoftInputFromWindow(windowToken, flags, null);
1039 }
1040
1041 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 * Request to hide the soft input window from the context of the window
1043 * that is currently accepting input. This should be called as a result
1044 * of the user doing some actually than fairly explicitly requests to
1045 * have the input window hidden.
1046 *
1047 * @param windowToken The token of the window that is making the request,
1048 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
1049 * @param flags Provides additional operating flags. Currently may be
1050 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
The Android Open Source Project4df24232009-03-05 14:34:35 -08001051 * @param resultReceiver If non-null, this will be called by the IME when
1052 * it has processed your request to tell you what it has done. The result
1053 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
1054 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
1055 * {@link #RESULT_HIDDEN}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001057 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
1058 ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 checkFocus();
1060 synchronized (mH) {
1061 if (mServedView == null || mServedView.getWindowToken() != windowToken) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001062 return false;
1063 }
1064
1065 try {
1066 return mService.hideSoftInput(mClient, flags, resultReceiver);
1067 } catch (RemoteException e) {
1068 }
1069 return false;
1070 }
1071 }
1072
1073
1074 /**
1075 * This method toggles the input method window display.
1076 * If the input window is already displayed, it gets hidden.
1077 * If not the input window will be displayed.
1078 * @param windowToken The token of the window that is making the request,
1079 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
1080 * @param showFlags Provides additional operating flags. May be
1081 * 0 or have the {@link #SHOW_IMPLICIT},
1082 * {@link #SHOW_FORCED} bit set.
1083 * @param hideFlags Provides additional operating flags. May be
1084 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1085 * {@link #HIDE_NOT_ALWAYS} bit set.
1086 **/
1087 public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
1088 synchronized (mH) {
1089 if (mServedView == null || mServedView.getWindowToken() != windowToken) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 return;
1091 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001092 if (mCurMethod != null) {
1093 try {
1094 mCurMethod.toggleSoftInput(showFlags, hideFlags);
1095 } catch (RemoteException e) {
1096 }
1097 }
1098 }
1099 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100
The Android Open Source Project4df24232009-03-05 14:34:35 -08001101 /*
1102 * This method toggles the input method window display.
1103 * If the input window is already displayed, it gets hidden.
1104 * If not the input window will be displayed.
1105 * @param showFlags Provides additional operating flags. May be
1106 * 0 or have the {@link #SHOW_IMPLICIT},
1107 * {@link #SHOW_FORCED} bit set.
1108 * @param hideFlags Provides additional operating flags. May be
1109 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1110 * {@link #HIDE_NOT_ALWAYS} bit set.
1111 * @hide
1112 */
1113 public void toggleSoftInput(int showFlags, int hideFlags) {
1114 if (mCurMethod != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001116 mCurMethod.toggleSoftInput(showFlags, hideFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117 } catch (RemoteException e) {
1118 }
1119 }
1120 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 /**
1123 * If the input method is currently connected to the given view,
1124 * restart it with its new contents. You should call this when the text
1125 * within your view changes outside of the normal input method or key
1126 * input flow, such as when an application calls TextView.setText().
1127 *
1128 * @param view The view whose text has changed.
1129 */
1130 public void restartInput(View view) {
1131 checkFocus();
1132 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001133 if (mServedView != view && (mServedView == null
1134 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 return;
1136 }
1137
1138 mServedConnecting = true;
1139 }
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001140
1141 startInputInner(InputMethodClient.START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, null, 0,
1142 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 }
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001144
1145 boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
1146 IBinder windowGainingFocus, int controlFlags, int softInputMode,
Dianne Hackborn7663d802012-02-24 13:08:49 -08001147 int windowFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 final View view;
1149 synchronized (mH) {
1150 view = mServedView;
1151
1152 // Make sure we have a window token for the served view.
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001153 if (DEBUG) {
1154 Log.v(TAG, "Starting input: view=" + view +
1155 " reason=" + InputMethodClient.getStartInputReason(startInputReason));
1156 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 if (view == null) {
1158 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
Dianne Hackborn7663d802012-02-24 13:08:49 -08001159 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 }
1161 }
1162
1163 // Now we need to get an input connection from the served view.
1164 // This is complicated in a couple ways: we can't be holding our lock
1165 // when calling out to the view, and we need to make sure we call into
1166 // the view on the same thread that is driving its view hierarchy.
1167 Handler vh = view.getHandler();
1168 if (vh == null) {
1169 // If the view doesn't have a handler, something has changed out
Satoshi Kataoka35739502012-10-02 19:00:26 +09001170 // from under us, so just close the current input.
1171 // If we don't close the current input, the current input method can remain on the
1172 // screen without a connection.
1173 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view! Close current input.");
1174 closeCurrentInput();
Dianne Hackborn7663d802012-02-24 13:08:49 -08001175 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 }
1177 if (vh.getLooper() != Looper.myLooper()) {
1178 // The view is running on a different thread than our own, so
1179 // we need to reschedule our work for over there.
1180 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
1181 vh.post(new Runnable() {
Jeff Brownc28867a2013-03-26 15:42:39 -07001182 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 public void run() {
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001184 startInputInner(startInputReason, null, 0, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 }
1186 });
Dianne Hackborn7663d802012-02-24 13:08:49 -08001187 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 }
1189
1190 // Okay we are now ready to call into the served view and have it
1191 // do its stuff.
1192 // Life is good: let's hook everything up!
1193 EditorInfo tba = new EditorInfo();
Yohei Yukawa02df3282015-06-03 15:58:59 -07001194 // Note: Use Context#getOpPackageName() rather than Context#getPackageName() so that the
1195 // system can verify the consistency between the uid of this process and package name passed
1196 // from here. See comment of Context#getOpPackageName() for details.
1197 tba.packageName = view.getContext().getOpPackageName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 tba.fieldId = view.getId();
1199 InputConnection ic = view.onCreateInputConnection(tba);
1200 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
Gilles Debunnec478c172011-12-19 17:29:24 -08001201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202 synchronized (mH) {
1203 // Now that we are locked again, validate that our state hasn't
1204 // changed.
1205 if (mServedView != view || !mServedConnecting) {
1206 // Something else happened, so abort.
1207 if (DEBUG) Log.v(TAG,
1208 "Starting input: finished by someone else (view="
1209 + mServedView + " conn=" + mServedConnecting + ")");
Dianne Hackborn7663d802012-02-24 13:08:49 -08001210 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001211 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 // If we already have a text box, then this view is already
1214 // connected so we want to restart it.
Dianne Hackborn7663d802012-02-24 13:08:49 -08001215 if (mCurrentTextBoxAttribute == null) {
1216 controlFlags |= CONTROL_START_INITIAL;
1217 }
Yohei Yukawa612cce92016-02-11 17:47:33 -08001218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 // Hook 'em up and let 'er rip.
1220 mCurrentTextBoxAttribute = tba;
1221 mServedConnecting = false;
Gilles Debunnec478c172011-12-19 17:29:24 -08001222 // Notify the served view that its previous input connection is finished
1223 notifyInputConnectionFinished();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 mServedInputConnection = ic;
Dianne Hackbornac920872012-05-22 11:49:49 -07001225 ControlledInputConnectionWrapper servedContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 if (ic != null) {
1227 mCursorSelStart = tba.initialSelStart;
1228 mCursorSelEnd = tba.initialSelEnd;
1229 mCursorCandStart = -1;
1230 mCursorCandEnd = -1;
1231 mCursorRect.setEmpty();
Yohei Yukawa056ffe62014-05-13 14:26:09 +09001232 mCursorAnchorInfo = null;
Yohei Yukawa612cce92016-02-11 17:47:33 -08001233 final Handler icHandler = ic.getHandler();
1234 servedContext = new ControlledInputConnectionWrapper(
1235 icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 } else {
1237 servedContext = null;
1238 }
Dianne Hackbornac920872012-05-22 11:49:49 -07001239 if (mServedInputConnectionWrapper != null) {
1240 mServedInputConnectionWrapper.deactivate();
1241 }
1242 mServedInputConnectionWrapper = servedContext;
Yohei Yukawa612cce92016-02-11 17:47:33 -08001243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 try {
1245 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
Dianne Hackborn7663d802012-02-24 13:08:49 -08001246 + ic + " tba=" + tba + " controlFlags=#"
1247 + Integer.toHexString(controlFlags));
Yohei Yukawa05c25f82016-02-22 12:41:17 -08001248 final InputBindResult res = mService.startInputOrWindowGainedFocus(
1249 startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode,
1250 windowFlags, tba, servedContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
1252 if (res != null) {
1253 if (res.id != null) {
Jeff Brown4d656882013-04-03 14:39:19 -07001254 setInputChannelLocked(res.channel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 mBindSequence = res.sequence;
1256 mCurMethod = res.method;
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001257 mCurId = res.id;
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09001258 mNextUserActionNotificationSequenceNumber =
1259 res.userActionNotificationSequenceNumber;
Jeff Brownc28867a2013-03-26 15:42:39 -07001260 } else {
Jeff Brown4d656882013-04-03 14:39:19 -07001261 if (res.channel != null && res.channel != mCurChannel) {
Jeff Brownc28867a2013-03-26 15:42:39 -07001262 res.channel.dispose();
1263 }
1264 if (mCurMethod == null) {
1265 // This means there is no input method available.
1266 if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1267 return true;
1268 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 }
1270 }
1271 if (mCurMethod != null && mCompletions != null) {
1272 try {
1273 mCurMethod.displayCompletions(mCompletions);
1274 } catch (RemoteException e) {
1275 }
1276 }
1277 } catch (RemoteException e) {
1278 Log.w(TAG, "IME died: " + mCurId, e);
1279 }
1280 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001281
1282 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 }
1284
1285 /**
1286 * When the focused window is dismissed, this method is called to finish the
1287 * input method started before.
1288 * @hide
1289 */
1290 public void windowDismissed(IBinder appWindowToken) {
1291 checkFocus();
1292 synchronized (mH) {
1293 if (mServedView != null &&
1294 mServedView.getWindowToken() == appWindowToken) {
1295 finishInputLocked();
1296 }
1297 }
1298 }
1299
1300 /**
1301 * Call this when a view receives focus.
1302 * @hide
1303 */
1304 public void focusIn(View view) {
1305 synchronized (mH) {
1306 focusInLocked(view);
1307 }
1308 }
1309
1310 void focusInLocked(View view) {
1311 if (DEBUG) Log.v(TAG, "focusIn: " + view);
Yohei Yukawa5f059652015-05-14 22:16:41 -07001312
Wale Ogunwale159c3d82015-05-14 12:20:53 -07001313 if (mCurRootView != view.getRootView()) {
1314 // This is a request from a window that isn't in the window with
1315 // IME focus, so ignore it.
1316 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 return;
1318 }
Yohei Yukawa5f059652015-05-14 22:16:41 -07001319
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320 mNextServedView = view;
1321 scheduleCheckFocusLocked(view);
1322 }
1323
1324 /**
1325 * Call this when a view loses focus.
1326 * @hide
1327 */
1328 public void focusOut(View view) {
1329 synchronized (mH) {
1330 if (DEBUG) Log.v(TAG, "focusOut: " + view
1331 + " mServedView=" + mServedView
1332 + " winFocus=" + view.hasWindowFocus());
Yohei Yukawa0b52ed02015-05-29 11:07:02 -07001333 if (mServedView != view) {
1334 // The following code would auto-hide the IME if we end up
1335 // with no more views with focus. This can happen, however,
1336 // whenever we go into touch mode, so it ends up hiding
1337 // at times when we don't really want it to. For now it
1338 // seems better to just turn it all off.
1339 if (false && view.hasWindowFocus()) {
1340 mNextServedView = null;
1341 scheduleCheckFocusLocked(view);
1342 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 }
1344 }
1345 }
1346
Yohei Yukawab13f0152015-05-29 17:09:14 -07001347 /**
1348 * Call this when a view is being detached from a {@link android.view.Window}.
1349 * @hide
1350 */
1351 public void onViewDetachedFromWindow(View view) {
1352 synchronized (mH) {
1353 if (DEBUG) Log.v(TAG, "onViewDetachedFromWindow: " + view
1354 + " mServedView=" + mServedView
1355 + " hasWindowFocus=" + view.hasWindowFocus());
1356 if (mServedView == view && view.hasWindowFocus()) {
1357 mNextServedView = null;
1358 scheduleCheckFocusLocked(view);
1359 }
1360 }
1361 }
1362
Gilles Debunnec478c172011-12-19 17:29:24 -08001363 static void scheduleCheckFocusLocked(View view) {
Jeff Browna175a5b2012-02-15 19:18:31 -08001364 ViewRootImpl viewRootImpl = view.getViewRootImpl();
1365 if (viewRootImpl != null) {
1366 viewRootImpl.dispatchCheckFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 }
1368 }
Jeff Browna175a5b2012-02-15 19:18:31 -08001369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 /**
1371 * @hide
1372 */
1373 public void checkFocus() {
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001374 if (checkFocusNoStartInput(false, true)) {
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001375 startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0);
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001376 }
Dianne Hackborna82ba542012-02-15 18:19:55 -08001377 }
1378
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001379 private boolean checkFocusNoStartInput(boolean forceNewFocus, boolean finishComposingText) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 // This is called a lot, so short-circuit before locking.
Dianne Hackborn7663d802012-02-24 13:08:49 -08001381 if (mServedView == mNextServedView && !forceNewFocus) {
Dianne Hackborna82ba542012-02-15 18:19:55 -08001382 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 }
satok863fcd62011-06-21 17:38:02 +09001384
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 InputConnection ic = null;
1386 synchronized (mH) {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001387 if (mServedView == mNextServedView && !forceNewFocus) {
Dianne Hackborna82ba542012-02-15 18:19:55 -08001388 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389 }
1390 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1391 + " next=" + mNextServedView
satok05a6cbe2012-04-05 23:04:08 +09001392 + " forceNewFocus=" + forceNewFocus
1393 + " package="
1394 + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
Dianne Hackborna82ba542012-02-15 18:19:55 -08001395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 if (mNextServedView == null) {
1397 finishInputLocked();
1398 // In this case, we used to have a focused view on the window,
1399 // but no longer do. We should make sure the input method is
1400 // no longer shown, since it serves no purpose.
1401 closeCurrentInput();
Dianne Hackborna82ba542012-02-15 18:19:55 -08001402 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 }
Dianne Hackborna82ba542012-02-15 18:19:55 -08001404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 ic = mServedInputConnection;
Dianne Hackborna82ba542012-02-15 18:19:55 -08001406
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 mServedView = mNextServedView;
1408 mCurrentTextBoxAttribute = null;
1409 mCompletions = null;
1410 mServedConnecting = true;
1411 }
Dianne Hackborna82ba542012-02-15 18:19:55 -08001412
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001413 if (finishComposingText && ic != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 ic.finishComposingText();
1415 }
Dianne Hackborna82ba542012-02-15 18:19:55 -08001416
1417 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001418 }
1419
1420 void closeCurrentInput() {
1421 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001422 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 } catch (RemoteException e) {
1424 }
1425 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001427 /**
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07001428 * Called by ViewAncestor when its window gets input focus.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 * @hide
1430 */
Yohei Yukawa5f059652015-05-14 22:16:41 -07001431 public void onPostWindowFocus(View rootView, View focusedView, int softInputMode,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 boolean first, int windowFlags) {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001433 boolean forceNewFocus = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001434 synchronized (mH) {
1435 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1436 + " softInputMode=" + softInputMode
1437 + " first=" + first + " flags=#"
1438 + Integer.toHexString(windowFlags));
1439 if (mHasBeenInactive) {
1440 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh");
1441 mHasBeenInactive = false;
Dianne Hackborn7663d802012-02-24 13:08:49 -08001442 forceNewFocus = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001443 }
1444 focusInLocked(focusedView != null ? focusedView : rootView);
1445 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001446
1447 int controlFlags = 0;
1448 if (focusedView != null) {
1449 controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
1450 if (focusedView.onCheckIsTextEditor()) {
1451 controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001452 }
1453 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001454 if (first) {
1455 controlFlags |= CONTROL_WINDOW_FIRST;
1456 }
1457
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001458 if (checkFocusNoStartInput(forceNewFocus, true)) {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001459 // We need to restart input on the current focus view. This
1460 // should be done in conjunction with telling the system service
1461 // about the window gaining focus, to help make the transition
1462 // smooth.
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001463 if (startInputInner(InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN,
1464 rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001465 return;
1466 }
1467 }
Yohei Yukawa05c25f82016-02-22 12:41:17 -08001468
Dianne Hackborn7663d802012-02-24 13:08:49 -08001469 // For some reason we didn't do a startInput + windowFocusGain, so
1470 // we'll just do a window focus gain and call it a day.
1471 synchronized (mH) {
1472 try {
Dianne Hackbornac920872012-05-22 11:49:49 -07001473 if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
Yohei Yukawa05c25f82016-02-22 12:41:17 -08001474 mService.startInputOrWindowGainedFocus(
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001475 InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
1476 rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
1477 null);
Dianne Hackborn7663d802012-02-24 13:08:49 -08001478 } catch (RemoteException e) {
1479 }
Dianne Hackborn06a591c2012-02-16 10:25:26 -08001480 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001481 }
Yohei Yukawa5f059652015-05-14 22:16:41 -07001482
Wale Ogunwale159c3d82015-05-14 12:20:53 -07001483 /** @hide */
Yohei Yukawa5f059652015-05-14 22:16:41 -07001484 public void onPreWindowFocus(View rootView, boolean hasWindowFocus) {
Wale Ogunwale159c3d82015-05-14 12:20:53 -07001485 synchronized (mH) {
Yohei Yukawa5f059652015-05-14 22:16:41 -07001486 if (rootView == null) {
1487 mCurRootView = null;
1488 } if (hasWindowFocus) {
1489 mCurRootView = rootView;
1490 } else if (rootView == mCurRootView) {
1491 // If the mCurRootView is losing window focus, release the strong reference to it
1492 // so as not to prevent it from being garbage-collected.
1493 mCurRootView = null;
1494 } else {
1495 if (DEBUG) {
1496 Log.v(TAG, "Ignoring onPreWindowFocus()."
1497 + " mCurRootView=" + mCurRootView + " rootView=" + rootView);
1498 }
1499 }
Wale Ogunwale159c3d82015-05-14 12:20:53 -07001500 }
1501 }
Yohei Yukawa5f059652015-05-14 22:16:41 -07001502
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 /**
1504 * Report the current selection range.
Jean Chalard6fd68e02014-02-20 17:48:46 +09001505 *
1506 * <p><strong>Editor authors</strong>, you need to call this method whenever
1507 * the cursor moves in your editor. Remember that in addition to doing this, your
1508 * editor needs to always supply current cursor values in
1509 * {@link EditorInfo#initialSelStart} and {@link EditorInfo#initialSelEnd} every
1510 * time {@link android.view.View#onCreateInputConnection(EditorInfo)} is
1511 * called, which happens whenever the keyboard shows up or the focus changes
1512 * to a text field, among other cases.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 */
1514 public void updateSelection(View view, int selStart, int selEnd,
1515 int candidatesStart, int candidatesEnd) {
1516 checkFocus();
1517 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001518 if ((mServedView != view && (mServedView == null
1519 || !mServedView.checkInputConnectionProxy(view)))
1520 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 return;
1522 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +09001523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1525 || mCursorCandStart != candidatesStart
1526 || mCursorCandEnd != candidatesEnd) {
1527 if (DEBUG) Log.d(TAG, "updateSelection");
1528
1529 try {
1530 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
Jean Chalardc743cb92013-09-12 16:28:45 +09001531 final int oldSelStart = mCursorSelStart;
1532 final int oldSelEnd = mCursorSelEnd;
1533 // Update internal values before sending updateSelection to the IME, because
1534 // if it changes the text within its onUpdateSelection handler in a way that
1535 // does not move the cursor we don't want to call it again with the same values.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 mCursorSelStart = selStart;
1537 mCursorSelEnd = selEnd;
1538 mCursorCandStart = candidatesStart;
1539 mCursorCandEnd = candidatesEnd;
Jean Chalardc743cb92013-09-12 16:28:45 +09001540 mCurMethod.updateSelection(oldSelStart, oldSelEnd,
1541 selStart, selEnd, candidatesStart, candidatesEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001542 } catch (RemoteException e) {
1543 Log.w(TAG, "IME died: " + mCurId, e);
1544 }
1545 }
1546 }
1547 }
1548
1549 /**
satok863fcd62011-06-21 17:38:02 +09001550 * Notify the event when the user tapped or clicked the text view.
1551 */
1552 public void viewClicked(View view) {
1553 final boolean focusChanged = mServedView != mNextServedView;
1554 checkFocus();
1555 synchronized (mH) {
1556 if ((mServedView != view && (mServedView == null
1557 || !mServedView.checkInputConnectionProxy(view)))
1558 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1559 return;
1560 }
1561 try {
1562 if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
1563 mCurMethod.viewClicked(focusChanged);
1564 } catch (RemoteException e) {
1565 Log.w(TAG, "IME died: " + mCurId, e);
1566 }
1567 }
1568 }
1569
1570 /**
Yohei Yukawaa277db22014-08-21 18:38:44 -07001571 * Return true if the current input method wants to watch the location
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001572 * of the input editor's cursor in its window.
Yohei Yukawa4de04792014-04-17 12:40:31 +09001573 *
Yohei Yukawad8636ea2014-09-02 22:03:30 -07001574 * @deprecated Use {@link InputConnection#requestCursorUpdates(int)} instead.
Yohei Yukawa4de04792014-04-17 12:40:31 +09001575 */
Yohei Yukawaa277db22014-08-21 18:38:44 -07001576 @Deprecated
1577 public boolean isWatchingCursor(View view) {
1578 return false;
Yohei Yukawa4de04792014-04-17 12:40:31 +09001579 }
1580
1581 /**
Yohei Yukawaa277db22014-08-21 18:38:44 -07001582 * Return true if the current input method wants to be notified when cursor/anchor location
Yohei Yukawa0023d0e2014-07-11 04:13:03 +09001583 * is changed.
Yohei Yukawab7b79072014-03-25 11:02:00 +09001584 *
1585 * @hide
1586 */
Yohei Yukawa0023d0e2014-07-11 04:13:03 +09001587 public boolean isCursorAnchorInfoEnabled() {
1588 synchronized (mH) {
Yohei Yukawaa277db22014-08-21 18:38:44 -07001589 final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode &
Yohei Yukawad8636ea2014-09-02 22:03:30 -07001590 InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0;
Yohei Yukawaa277db22014-08-21 18:38:44 -07001591 final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode &
Yohei Yukawad8636ea2014-09-02 22:03:30 -07001592 InputConnection.CURSOR_UPDATE_MONITOR) != 0;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +09001593 return isImmediate || isMonitoring;
1594 }
1595 }
1596
1597 /**
Yohei Yukawaa277db22014-08-21 18:38:44 -07001598 * Set the requested mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}.
Yohei Yukawa0023d0e2014-07-11 04:13:03 +09001599 *
1600 * @hide
1601 */
Yohei Yukawaa277db22014-08-21 18:38:44 -07001602 public void setUpdateCursorAnchorInfoMode(int flags) {
Yohei Yukawa0023d0e2014-07-11 04:13:03 +09001603 synchronized (mH) {
Yohei Yukawaa277db22014-08-21 18:38:44 -07001604 mRequestUpdateCursorAnchorInfoMonitorMode = flags;
Yohei Yukawab7b79072014-03-25 11:02:00 +09001605 }
1606 }
1607
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001608 /**
1609 * Report the current cursor location in its window.
Yohei Yukawaa277db22014-08-21 18:38:44 -07001610 *
1611 * @deprecated Use {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001612 */
Yohei Yukawaa277db22014-08-21 18:38:44 -07001613 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614 public void updateCursor(View view, int left, int top, int right, int bottom) {
1615 checkFocus();
1616 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001617 if ((mServedView != view && (mServedView == null
1618 || !mServedView.checkInputConnectionProxy(view)))
1619 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001620 return;
1621 }
Yohei Yukawaa277db22014-08-21 18:38:44 -07001622
1623 mTmpCursorRect.set(left, top, right, bottom);
1624 if (!mCursorRect.equals(mTmpCursorRect)) {
1625 if (DEBUG) Log.d(TAG, "updateCursor");
1626
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001627 try {
Yohei Yukawaa277db22014-08-21 18:38:44 -07001628 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1629 mCurMethod.updateCursor(mTmpCursorRect);
1630 mCursorRect.set(mTmpCursorRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 } catch (RemoteException e) {
1632 Log.w(TAG, "IME died: " + mCurId, e);
1633 }
1634 }
1635 }
1636 }
1637
1638 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +09001639 * Report positional change of the text insertion point and/or characters in the composition
1640 * string.
1641 */
1642 public void updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo) {
1643 if (view == null || cursorAnchorInfo == null) {
1644 return;
1645 }
1646 checkFocus();
1647 synchronized (mH) {
1648 if ((mServedView != view &&
1649 (mServedView == null || !mServedView.checkInputConnectionProxy(view)))
1650 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1651 return;
1652 }
Yohei Yukawa0023d0e2014-07-11 04:13:03 +09001653 // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has
1654 // not been changed from the previous call.
Yohei Yukawaa277db22014-08-21 18:38:44 -07001655 final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode &
Yohei Yukawad8636ea2014-09-02 22:03:30 -07001656 InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +09001657 if (!isImmediate && Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) {
Yohei Yukawa73605912014-08-13 16:49:35 +09001658 // TODO: Consider always emitting this message once we have addressed redundant
1659 // calls of this method from android.widget.Editor.
1660 if (DEBUG) {
1661 Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info="
1662 + cursorAnchorInfo);
1663 }
Yohei Yukawa056ffe62014-05-13 14:26:09 +09001664 return;
1665 }
1666 if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
Yohei Yukawac2ddd602014-05-06 21:22:49 +09001667 try {
1668 mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
Yohei Yukawa056ffe62014-05-13 14:26:09 +09001669 mCursorAnchorInfo = cursorAnchorInfo;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +09001670 // Clear immediate bit (if any).
Yohei Yukawaa277db22014-08-21 18:38:44 -07001671 mRequestUpdateCursorAnchorInfoMonitorMode &=
Yohei Yukawad8636ea2014-09-02 22:03:30 -07001672 ~InputConnection.CURSOR_UPDATE_IMMEDIATE;
Yohei Yukawac2ddd602014-05-06 21:22:49 +09001673 } catch (RemoteException e) {
1674 Log.w(TAG, "IME died: " + mCurId, e);
1675 }
1676 }
1677 }
1678
1679 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001680 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1681 * InputMethodSession.appPrivateCommand()} on the current Input Method.
1682 * @param view Optional View that is sending the command, or null if
1683 * you want to send the command regardless of the view that is attached
1684 * to the input method.
1685 * @param action Name of the command to be performed. This <em>must</em>
1686 * be a scoped name, i.e. prefixed with a package name you own, so that
1687 * different developers will not create conflicting commands.
1688 * @param data Any data to include with the command.
1689 */
1690 public void sendAppPrivateCommand(View view, String action, Bundle data) {
1691 checkFocus();
1692 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001693 if ((mServedView != view && (mServedView == null
1694 || !mServedView.checkInputConnectionProxy(view)))
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1696 return;
1697 }
1698 try {
1699 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1700 mCurMethod.appPrivateCommand(action, data);
1701 } catch (RemoteException e) {
1702 Log.w(TAG, "IME died: " + mCurId, e);
1703 }
1704 }
1705 }
satok28203512010-11-24 11:06:49 +09001706
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 /**
satok28203512010-11-24 11:06:49 +09001708 * Force switch to a new input method component. This can only be called
1709 * from an application or a service which has a token of the currently active input method.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 * @param token Supplies the identifying token given to an input method
1711 * when it was started, which allows it to perform this operation on
1712 * itself.
1713 * @param id The unique identifier for the new input method to be switched to.
1714 */
1715 public void setInputMethod(IBinder token, String id) {
1716 try {
1717 mService.setInputMethod(token, id);
1718 } catch (RemoteException e) {
1719 throw new RuntimeException(e);
1720 }
1721 }
satok28203512010-11-24 11:06:49 +09001722
1723 /**
1724 * Force switch to a new input method and subtype. This can only be called
1725 * from an application or a service which has a token of the currently active input method.
1726 * @param token Supplies the identifying token given to an input method
1727 * when it was started, which allows it to perform this operation on
1728 * itself.
1729 * @param id The unique identifier for the new input method to be switched to.
1730 * @param subtype The new subtype of the new input method to be switched to.
1731 */
1732 public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
1733 try {
1734 mService.setInputMethodAndSubtype(token, id, subtype);
1735 } catch (RemoteException e) {
1736 throw new RuntimeException(e);
1737 }
1738 }
1739
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 /**
1741 * Close/hide the input method's soft input area, so the user no longer
1742 * sees it or can interact with it. This can only be called
1743 * from the currently active input method, as validated by the given token.
1744 *
1745 * @param token Supplies the identifying token given to an input method
1746 * when it was started, which allows it to perform this operation on
1747 * itself.
1748 * @param flags Provides additional operating flags. Currently may be
The Android Open Source Project4df24232009-03-05 14:34:35 -08001749 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1750 * {@link #HIDE_NOT_ALWAYS} bit set.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 */
1752 public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1753 try {
1754 mService.hideMySoftInput(token, flags);
1755 } catch (RemoteException e) {
1756 throw new RuntimeException(e);
1757 }
1758 }
1759
1760 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -08001761 * Show the input method's soft input area, so the user
1762 * sees the input method window and can interact with it.
1763 * This can only be called from the currently active input method,
1764 * as validated by the given token.
1765 *
1766 * @param token Supplies the identifying token given to an input method
1767 * when it was started, which allows it to perform this operation on
1768 * itself.
1769 * @param flags Provides additional operating flags. Currently may be
1770 * 0 or have the {@link #SHOW_IMPLICIT} or
1771 * {@link #SHOW_FORCED} bit set.
1772 */
1773 public void showSoftInputFromInputMethod(IBinder token, int flags) {
1774 try {
1775 mService.showMySoftInput(token, flags);
1776 } catch (RemoteException e) {
1777 throw new RuntimeException(e);
1778 }
1779 }
Jeff Brownc28867a2013-03-26 15:42:39 -07001780
The Android Open Source Project4df24232009-03-05 14:34:35 -08001781 /**
Jeff Brownf9e989d2013-04-04 23:04:03 -07001782 * Dispatches an input event to the IME.
1783 *
1784 * Returns {@link #DISPATCH_HANDLED} if the event was handled.
1785 * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
1786 * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
1787 * callback will be invoked later.
1788 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 * @hide
1790 */
Jeff Brownf9e989d2013-04-04 23:04:03 -07001791 public int dispatchInputEvent(InputEvent event, Object token,
1792 FinishedInputEventCallback callback, Handler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001793 synchronized (mH) {
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001794 if (mCurMethod != null) {
Jeff Brownc28867a2013-03-26 15:42:39 -07001795 if (event instanceof KeyEvent) {
1796 KeyEvent keyEvent = (KeyEvent)event;
1797 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
1798 && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
1799 && keyEvent.getRepeatCount() == 0) {
1800 showInputMethodPickerLocked();
Jeff Brownf9e989d2013-04-04 23:04:03 -07001801 return DISPATCH_HANDLED;
Jeff Brownc28867a2013-03-26 15:42:39 -07001802 }
Jeff Brown29c0ed22013-01-14 13:50:37 -08001803 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804
Jeff Brownc28867a2013-03-26 15:42:39 -07001805 if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
Jeff Brownf9e989d2013-04-04 23:04:03 -07001806
1807 PendingEvent p = obtainPendingEventLocked(
1808 event, token, mCurId, callback, handler);
1809 if (mMainLooper.isCurrentThread()) {
1810 // Already running on the IMM thread so we can send the event immediately.
1811 return sendInputEventOnMainLooperLocked(p);
Victoria Leaseb38070c2012-08-24 13:46:02 -07001812 }
Jeff Brownf9e989d2013-04-04 23:04:03 -07001813
1814 // Post the event to the IMM thread.
1815 Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
1816 msg.setAsynchronous(true);
1817 mH.sendMessage(msg);
1818 return DISPATCH_IN_PROGRESS;
Victoria Leaseb38070c2012-08-24 13:46:02 -07001819 }
1820 }
Jeff Brownf9e989d2013-04-04 23:04:03 -07001821 return DISPATCH_NOT_HANDLED;
Victoria Leaseb38070c2012-08-24 13:46:02 -07001822 }
1823
Yohei Yukawa2afe2aa2016-01-07 18:09:44 -08001824 /**
1825 * Provides the default implementation of {@link InputConnection#sendKeyEvent(KeyEvent)}, which
1826 * is expected to dispatch an keyboard event sent from the IME to an appropriate event target
1827 * depending on the given {@link View} and the current focus state.
1828 *
1829 * <p>CAUTION: This method is provided only for the situation where
1830 * {@link InputConnection#sendKeyEvent(KeyEvent)} needs to be implemented without relying on
1831 * {@link BaseInputConnection}. Do not use this API for anything else.</p>
1832 *
1833 * @param targetView the default target view. If {@code null} is specified, then this method
1834 * tries to find a good event target based on the current focus state.
1835 * @param event the key event to be dispatched.
1836 */
1837 public void dispatchKeyEventFromInputMethod(@Nullable View targetView,
1838 @NonNull KeyEvent event) {
1839 synchronized (mH) {
1840 ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
1841 if (viewRootImpl == null) {
1842 if (mServedView != null) {
1843 viewRootImpl = mServedView.getViewRootImpl();
1844 }
1845 }
1846 if (viewRootImpl != null) {
1847 viewRootImpl.dispatchKeyFromIme(event);
1848 }
1849 }
1850 }
1851
Jeff Brownf9e989d2013-04-04 23:04:03 -07001852 // Must be called on the main looper
1853 void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
1854 final boolean handled;
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001855 synchronized (mH) {
Jeff Brownf9e989d2013-04-04 23:04:03 -07001856 int result = sendInputEventOnMainLooperLocked(p);
1857 if (result == DISPATCH_IN_PROGRESS) {
1858 return;
1859 }
1860
1861 handled = (result == DISPATCH_HANDLED);
1862 }
1863
1864 invokeFinishedInputEventCallback(p, handled);
1865 }
1866
1867 // Must be called on the main looper
1868 int sendInputEventOnMainLooperLocked(PendingEvent p) {
1869 if (mCurChannel != null) {
1870 if (mCurSender == null) {
1871 mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
1872 }
1873
1874 final InputEvent event = p.mEvent;
1875 final int seq = event.getSequenceNumber();
1876 if (mCurSender.sendInputEvent(seq, event)) {
1877 mPendingEvents.put(seq, p);
1878 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
1879 mPendingEvents.size());
1880
1881 Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
1882 msg.setAsynchronous(true);
1883 mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
1884 return DISPATCH_IN_PROGRESS;
1885 }
1886
1887 Log.w(TAG, "Unable to send input event to IME: "
1888 + mCurId + " dropping: " + event);
1889 }
1890 return DISPATCH_NOT_HANDLED;
1891 }
1892
1893 void finishedInputEvent(int seq, boolean handled, boolean timeout) {
1894 final PendingEvent p;
1895 synchronized (mH) {
1896 int index = mPendingEvents.indexOfKey(seq);
1897 if (index < 0) {
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001898 return; // spurious, event already finished or timed out
1899 }
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001900
Jeff Brownf9e989d2013-04-04 23:04:03 -07001901 p = mPendingEvents.valueAt(index);
1902 mPendingEvents.removeAt(index);
1903 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001904
Jeff Brownf9e989d2013-04-04 23:04:03 -07001905 if (timeout) {
1906 Log.w(TAG, "Timeout waiting for IME to handle input event after "
1907 + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
1908 } else {
1909 mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
Jeff Brown4d656882013-04-03 14:39:19 -07001910 }
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001911 }
Jeff Brownf9e989d2013-04-04 23:04:03 -07001912
1913 invokeFinishedInputEventCallback(p, handled);
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001914 }
1915
Jeff Brownf9e989d2013-04-04 23:04:03 -07001916 // Assumes the event has already been removed from the queue.
1917 void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
1918 p.mHandled = handled;
1919 if (p.mHandler.getLooper().isCurrentThread()) {
1920 // Already running on the callback handler thread so we can send the
1921 // callback immediately.
1922 p.run();
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001923 } else {
Jeff Brownf9e989d2013-04-04 23:04:03 -07001924 // Post the event to the callback handler thread.
1925 // In this case, the callback will be responsible for recycling the event.
1926 Message msg = Message.obtain(p.mHandler, p);
1927 msg.setAsynchronous(true);
1928 msg.sendToTarget();
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001929 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 }
satokab751aa2010-09-14 19:17:36 +09001931
Michael Wrightef17e872013-04-01 13:15:55 -07001932 private void flushPendingEventsLocked() {
Jeff Brownf9e989d2013-04-04 23:04:03 -07001933 mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
Jeff Brown4d656882013-04-03 14:39:19 -07001934
Jeff Brownf9e989d2013-04-04 23:04:03 -07001935 final int count = mPendingEvents.size();
1936 for (int i = 0; i < count; i++) {
1937 int seq = mPendingEvents.keyAt(i);
1938 Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0);
Michael Wrightef17e872013-04-01 13:15:55 -07001939 msg.setAsynchronous(true);
Jeff Brownf9e989d2013-04-04 23:04:03 -07001940 msg.sendToTarget();
Michael Wrightef17e872013-04-01 13:15:55 -07001941 }
1942 }
1943
Jeff Brownf9e989d2013-04-04 23:04:03 -07001944 private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
1945 String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
1946 PendingEvent p = mPendingEventPool.acquire();
1947 if (p == null) {
1948 p = new PendingEvent();
1949 }
1950 p.mEvent = event;
1951 p.mToken = token;
1952 p.mInputMethodId = inputMethodId;
1953 p.mCallback = callback;
1954 p.mHandler = handler;
1955 return p;
1956 }
1957
1958 private void recyclePendingEventLocked(PendingEvent p) {
1959 p.recycle();
1960 mPendingEventPool.release(p);
1961 }
1962
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 public void showInputMethodPicker() {
1964 synchronized (mH) {
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001965 showInputMethodPickerLocked();
1966 }
1967 }
1968
Seigo Nonaka14e13912015-05-06 21:04:13 -07001969 /**
1970 * Shows the input method chooser dialog.
1971 *
1972 * @param showAuxiliarySubtypes Set true to show auxiliary input methods.
1973 * @hide
1974 */
1975 public void showInputMethodPicker(boolean showAuxiliarySubtypes) {
1976 synchronized (mH) {
1977 try {
1978 final int mode = showAuxiliarySubtypes ?
1979 SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
1980 SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
1981 mService.showInputMethodPickerFromClient(mClient, mode);
1982 } catch (RemoteException e) {
1983 Log.w(TAG, "IME died: " + mCurId, e);
1984 }
1985 }
1986 }
1987
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001988 private void showInputMethodPickerLocked() {
1989 try {
Seigo Nonaka14e13912015-05-06 21:04:13 -07001990 mService.showInputMethodPickerFromClient(mClient, SHOW_IM_PICKER_MODE_AUTO);
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001991 } catch (RemoteException e) {
1992 Log.w(TAG, "IME died: " + mCurId, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001993 }
1994 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001995
satokd4fce2b2011-04-11 12:07:13 +09001996 /**
1997 * Show the settings for enabling subtypes of the specified input method.
1998 * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
1999 * subtypes of all input methods will be shown.
2000 */
2001 public void showInputMethodAndSubtypeEnabler(String imiId) {
satok47a44912010-10-06 16:03:58 +09002002 synchronized (mH) {
2003 try {
satokd4fce2b2011-04-11 12:07:13 +09002004 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
satok47a44912010-10-06 16:03:58 +09002005 } catch (RemoteException e) {
2006 Log.w(TAG, "IME died: " + mCurId, e);
2007 }
2008 }
2009 }
2010
satokd4fce2b2011-04-11 12:07:13 +09002011 /**
2012 * Returns the current input method subtype. This subtype is one of the subtypes in
2013 * the current input method. This method returns null when the current input method doesn't
2014 * have any input method subtype.
2015 */
satok04d50202010-10-25 22:20:12 +09002016 public InputMethodSubtype getCurrentInputMethodSubtype() {
2017 synchronized (mH) {
2018 try {
2019 return mService.getCurrentInputMethodSubtype();
2020 } catch (RemoteException e) {
2021 Log.w(TAG, "IME died: " + mCurId, e);
2022 return null;
2023 }
2024 }
2025 }
2026
satokd4fce2b2011-04-11 12:07:13 +09002027 /**
2028 * Switch to a new input method subtype of the current input method.
2029 * @param subtype A new input method subtype to switch.
2030 * @return true if the current subtype was successfully switched. When the specified subtype is
2031 * null, this method returns false.
2032 */
Yoshiki Iguchi00d51222015-05-29 15:36:22 +09002033 @RequiresPermission(WRITE_SECURE_SETTINGS)
satokb66d2872010-11-10 01:04:04 +09002034 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
2035 synchronized (mH) {
2036 try {
2037 return mService.setCurrentInputMethodSubtype(subtype);
2038 } catch (RemoteException e) {
2039 Log.w(TAG, "IME died: " + mCurId, e);
2040 return false;
2041 }
2042 }
2043 }
2044
satokd4fce2b2011-04-11 12:07:13 +09002045 /**
Yohei Yukawa02970512014-06-05 16:16:18 +09002046 * Notify that a user took some action with this input method.
Satoshi Kataokad7443c82013-10-15 17:45:43 +09002047 * @hide
2048 */
Yohei Yukawa02970512014-06-05 16:16:18 +09002049 public void notifyUserAction() {
Satoshi Kataokad7443c82013-10-15 17:45:43 +09002050 synchronized (mH) {
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09002051 if (mLastSentUserActionNotificationSequenceNumber ==
2052 mNextUserActionNotificationSequenceNumber) {
2053 if (DEBUG) {
2054 Log.w(TAG, "Ignoring notifyUserAction as it has already been sent."
2055 + " mLastSentUserActionNotificationSequenceNumber: "
2056 + mLastSentUserActionNotificationSequenceNumber
2057 + " mNextUserActionNotificationSequenceNumber: "
2058 + mNextUserActionNotificationSequenceNumber);
2059 }
2060 return;
2061 }
Satoshi Kataokad7443c82013-10-15 17:45:43 +09002062 try {
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09002063 if (DEBUG) {
2064 Log.w(TAG, "notifyUserAction: "
2065 + " mLastSentUserActionNotificationSequenceNumber: "
2066 + mLastSentUserActionNotificationSequenceNumber
2067 + " mNextUserActionNotificationSequenceNumber: "
2068 + mNextUserActionNotificationSequenceNumber);
2069 }
2070 mService.notifyUserAction(mNextUserActionNotificationSequenceNumber);
2071 mLastSentUserActionNotificationSequenceNumber =
2072 mNextUserActionNotificationSequenceNumber;
Satoshi Kataokad7443c82013-10-15 17:45:43 +09002073 } catch (RemoteException e) {
2074 Log.w(TAG, "IME died: " + mCurId, e);
2075 }
2076 }
2077 }
2078
2079 /**
satokd4fce2b2011-04-11 12:07:13 +09002080 * Returns a map of all shortcut input method info and their subtypes.
2081 */
satokf3db1af2010-11-23 13:34:33 +09002082 public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
satok4e4569d2010-11-19 18:45:53 +09002083 synchronized (mH) {
Yohei Yukawab0377bb2015-08-10 21:06:30 -07002084 HashMap<InputMethodInfo, List<InputMethodSubtype>> ret = new HashMap<>();
satok4e4569d2010-11-19 18:45:53 +09002085 try {
2086 // TODO: We should change the return type from List<Object> to List<Parcelable>
2087 List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
satokf3db1af2010-11-23 13:34:33 +09002088 // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
2089 ArrayList<InputMethodSubtype> subtypes = null;
Andreas Gampe4236ad72015-03-17 21:07:21 -07002090 if (info != null && !info.isEmpty()) {
2091 final int N = info.size();
satokf3db1af2010-11-23 13:34:33 +09002092 for (int i = 0; i < N; ++i) {
2093 Object o = info.get(i);
2094 if (o instanceof InputMethodInfo) {
2095 if (ret.containsKey(o)) {
2096 Log.e(TAG, "IMI list already contains the same InputMethod.");
2097 break;
satok4e4569d2010-11-19 18:45:53 +09002098 }
Yohei Yukawab0377bb2015-08-10 21:06:30 -07002099 subtypes = new ArrayList<>();
satokf3db1af2010-11-23 13:34:33 +09002100 ret.put((InputMethodInfo)o, subtypes);
2101 } else if (subtypes != null && o instanceof InputMethodSubtype) {
2102 subtypes.add((InputMethodSubtype)o);
satok4e4569d2010-11-19 18:45:53 +09002103 }
satok4e4569d2010-11-19 18:45:53 +09002104 }
2105 }
2106 } catch (RemoteException e) {
2107 Log.w(TAG, "IME died: " + mCurId, e);
2108 }
2109 return ret;
2110 }
2111 }
satokf3db1af2010-11-23 13:34:33 +09002112
satokd4fce2b2011-04-11 12:07:13 +09002113 /**
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09002114 * @return The current height of the input method window.
2115 * @hide
2116 */
2117 public int getInputMethodWindowVisibleHeight() {
2118 synchronized (mH) {
2119 try {
2120 return mService.getInputMethodWindowVisibleHeight();
2121 } catch (RemoteException e) {
2122 Log.w(TAG, "IME died: " + mCurId, e);
2123 return 0;
2124 }
2125 }
2126 }
2127
2128 /**
satokd4fce2b2011-04-11 12:07:13 +09002129 * Force switch to the last used input method and subtype. If the last input method didn't have
2130 * any subtypes, the framework will simply switch to the last input method with no subtype
2131 * specified.
2132 * @param imeToken Supplies the identifying token given to an input method when it was started,
2133 * which allows it to perform this operation on itself.
2134 * @return true if the current input method and subtype was successfully switched to the last
2135 * used input method and subtype.
2136 */
satok735cf382010-11-11 20:40:09 +09002137 public boolean switchToLastInputMethod(IBinder imeToken) {
2138 synchronized (mH) {
2139 try {
2140 return mService.switchToLastInputMethod(imeToken);
2141 } catch (RemoteException e) {
2142 Log.w(TAG, "IME died: " + mCurId, e);
2143 return false;
2144 }
2145 }
2146 }
2147
satoke7c6998e2011-06-03 17:57:59 +09002148 /**
satok688bd472012-02-09 20:09:17 +09002149 * Force switch to the next input method and subtype. If there is no IME enabled except
2150 * current IME and subtype, do nothing.
2151 * @param imeToken Supplies the identifying token given to an input method when it was started,
2152 * which allows it to perform this operation on itself.
2153 * @param onlyCurrentIme if true, the framework will find the next subtype which
2154 * belongs to the current IME
2155 * @return true if the current input method and subtype was successfully switched to the next
2156 * input method and subtype.
2157 */
2158 public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
2159 synchronized (mH) {
2160 try {
2161 return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
2162 } catch (RemoteException e) {
2163 Log.w(TAG, "IME died: " + mCurId, e);
2164 return false;
2165 }
2166 }
2167 }
2168
2169 /**
satok15ab6b02013-08-26 14:17:18 +09002170 * Returns true if the current IME needs to offer the users ways to switch to a next input
2171 * method (e.g. a globe key.).
2172 * When an IME sets supportsSwitchingToNextInputMethod and this method returns true,
2173 * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly.
2174 * <p> Note that the system determines the most appropriate next input method
2175 * and subtype in order to provide the consistent user experience in switching
2176 * between IMEs and subtypes.
Satoshi Kataoka2b10b522013-08-21 20:39:12 +09002177 * @param imeToken Supplies the identifying token given to an input method when it was started,
2178 * which allows it to perform this operation on itself.
2179 */
2180 public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
2181 synchronized (mH) {
2182 try {
2183 return mService.shouldOfferSwitchingToNextInputMethod(imeToken);
2184 } catch (RemoteException e) {
2185 Log.w(TAG, "IME died: " + mCurId, e);
2186 return false;
2187 }
2188 }
2189 }
2190
2191 /**
satok91e88122011-07-18 11:11:42 +09002192 * Set additional input method subtypes. Only a process which shares the same uid with the IME
2193 * can add additional input method subtypes to the IME.
satok75917b62011-08-31 23:27:39 +09002194 * Please note that a subtype's status is stored in the system.
2195 * For example, enabled subtypes are remembered by the framework even after they are removed
2196 * by using this method. If you re-add the same subtypes again,
2197 * they will just get enabled. If you want to avoid such conflicts, for instance, you may
2198 * want to create a "different" new subtype even with the same locale and mode,
2199 * by changing its extra value. The different subtype won't get affected by the stored past
2200 * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer
2201 * to the current implementation.)
Yohei Yukawa70f5c482016-01-04 19:42:36 -08002202 *
2203 * <p>NOTE: If the same subtype exists in both the manifest XML file and additional subtypes
2204 * specified by {@code subtypes}, those multiple instances are automatically merged into one
2205 * instance.</p>
2206 *
2207 * <p>CAVEAT: In API Level 23 and prior, the system may do nothing if an empty
2208 * {@link InputMethodSubtype} is specified in {@code subtypes}, which prevents you from removing
2209 * the last one entry of additional subtypes. If your IME statically defines one or more
2210 * subtypes in the manifest XML file, you may be able to work around this limitation by
2211 * specifying one of those statically defined subtypes in {@code subtypes}.</p>
2212 *
satok91e88122011-07-18 11:11:42 +09002213 * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
satoke7c6998e2011-06-03 17:57:59 +09002214 * @param subtypes subtypes will be added as additional subtypes of the current input method.
satoke7c6998e2011-06-03 17:57:59 +09002215 */
satokee5e77c2011-09-02 18:50:15 +09002216 public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
satoke7c6998e2011-06-03 17:57:59 +09002217 synchronized (mH) {
2218 try {
satokee5e77c2011-09-02 18:50:15 +09002219 mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
satoke7c6998e2011-06-03 17:57:59 +09002220 } catch (RemoteException e) {
2221 Log.w(TAG, "IME died: " + mCurId, e);
satoke7c6998e2011-06-03 17:57:59 +09002222 }
2223 }
2224 }
2225
satok68f1b782011-04-11 14:26:04 +09002226 public InputMethodSubtype getLastInputMethodSubtype() {
2227 synchronized (mH) {
2228 try {
2229 return mService.getLastInputMethodSubtype();
2230 } catch (RemoteException e) {
2231 Log.w(TAG, "IME died: " + mCurId, e);
2232 return null;
2233 }
2234 }
2235 }
2236
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002237 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
2238 final Printer p = new PrintWriterPrinter(fout);
2239 p.println("Input method client state for " + this + ":");
2240
2241 p.println(" mService=" + mService);
2242 p.println(" mMainLooper=" + mMainLooper);
2243 p.println(" mIInputContext=" + mIInputContext);
2244 p.println(" mActive=" + mActive
2245 + " mHasBeenInactive=" + mHasBeenInactive
2246 + " mBindSequence=" + mBindSequence
2247 + " mCurId=" + mCurId);
2248 p.println(" mCurMethod=" + mCurMethod);
Wale Ogunwale159c3d82015-05-14 12:20:53 -07002249 p.println(" mCurRootView=" + mCurRootView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002250 p.println(" mServedView=" + mServedView);
Dianne Hackborn7663d802012-02-24 13:08:49 -08002251 p.println(" mNextServedView=" + mNextServedView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002252 p.println(" mServedConnecting=" + mServedConnecting);
2253 if (mCurrentTextBoxAttribute != null) {
2254 p.println(" mCurrentTextBoxAttribute:");
2255 mCurrentTextBoxAttribute.dump(p, " ");
2256 } else {
2257 p.println(" mCurrentTextBoxAttribute: null");
2258 }
2259 p.println(" mServedInputConnection=" + mServedInputConnection);
Andreas Gampee6748ce2015-12-11 18:00:38 -08002260 p.println(" mCompletions=" + Arrays.toString(mCompletions));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002261 p.println(" mCursorRect=" + mCursorRect);
2262 p.println(" mCursorSelStart=" + mCursorSelStart
2263 + " mCursorSelEnd=" + mCursorSelEnd
2264 + " mCursorCandStart=" + mCursorCandStart
2265 + " mCursorCandEnd=" + mCursorCandEnd);
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09002266 p.println(" mNextUserActionNotificationSequenceNumber="
2267 + mNextUserActionNotificationSequenceNumber
2268 + " mLastSentUserActionNotificationSequenceNumber="
2269 + mLastSentUserActionNotificationSequenceNumber);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002270 }
Jeff Brown04ddf3c2012-06-14 03:57:49 -07002271
2272 /**
2273 * Callback that is invoked when an input event that was dispatched to
2274 * the IME has been finished.
2275 * @hide
2276 */
Jeff Brownf9e989d2013-04-04 23:04:03 -07002277 public interface FinishedInputEventCallback {
2278 public void onFinishedInputEvent(Object token, boolean handled);
Jeff Brown04ddf3c2012-06-14 03:57:49 -07002279 }
2280
Jeff Brownc28867a2013-03-26 15:42:39 -07002281 private final class ImeInputEventSender extends InputEventSender {
2282 public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
2283 super(inputChannel, looper);
2284 }
2285
2286 @Override
2287 public void onInputEventFinished(int seq, boolean handled) {
Jeff Brownf9e989d2013-04-04 23:04:03 -07002288 finishedInputEvent(seq, handled, false);
Jeff Brownc28867a2013-03-26 15:42:39 -07002289 }
2290 }
2291
Jeff Brownf9e989d2013-04-04 23:04:03 -07002292 private final class PendingEvent implements Runnable {
2293 public InputEvent mEvent;
2294 public Object mToken;
Jeff Brown04ddf3c2012-06-14 03:57:49 -07002295 public String mInputMethodId;
Jeff Brownf9e989d2013-04-04 23:04:03 -07002296 public FinishedInputEventCallback mCallback;
2297 public Handler mHandler;
2298 public boolean mHandled;
2299
2300 public void recycle() {
2301 mEvent = null;
2302 mToken = null;
2303 mInputMethodId = null;
2304 mCallback = null;
2305 mHandler = null;
2306 mHandled = false;
2307 }
2308
2309 @Override
2310 public void run() {
2311 mCallback.onFinishedInputEvent(mToken, mHandled);
2312
2313 synchronized (mH) {
2314 recyclePendingEventLocked(this);
2315 }
2316 }
Jeff Brown04ddf3c2012-06-14 03:57:49 -07002317 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002318}