blob: 70c53d274d99ecb4886cdb572ab27267d199c8d7 [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;
26
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.Context;
28import android.graphics.Rect;
29import android.os.Bundle;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.Looper;
33import android.os.Message;
34import android.os.RemoteException;
The Android Open Source Project4df24232009-03-05 14:34:35 -080035import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.os.ServiceManager;
Jeff Brownf9e989d2013-04-04 23:04:03 -070037import android.os.Trace;
satokf9f01002011-05-19 21:31:50 +090038import android.text.style.SuggestionSpan;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.util.Log;
Jeff Brownf9e989d2013-04-04 23:04:03 -070040import android.util.Pools.Pool;
41import android.util.Pools.SimplePool;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.util.PrintWriterPrinter;
43import android.util.Printer;
Jeff Brownc28867a2013-03-26 15:42:39 -070044import android.view.InputChannel;
45import android.view.InputEvent;
46import android.view.InputEventSender;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.view.View;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -070049import android.view.ViewRootImpl;
Jeff Brownf9e989d2013-04-04 23:04:03 -070050import android.util.SparseArray;
Gilles Debunned4723bb2010-09-02 15:27:32 -070051
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import java.io.FileDescriptor;
53import java.io.PrintWriter;
satok4e4569d2010-11-19 18:45:53 +090054import java.util.ArrayList;
satokf3db1af2010-11-23 13:34:33 +090055import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import java.util.List;
satokf3db1af2010-11-23 13:34:33 +090057import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import java.util.concurrent.CountDownLatch;
59import java.util.concurrent.TimeUnit;
60
61/**
62 * Central system API to the overall input method framework (IMF) architecture,
63 * which arbitrates interaction between applications and the current input method.
64 * You can retrieve an instance of this interface with
65 * {@link Context#getSystemService(String) Context.getSystemService()}.
66 *
67 * <p>Topics covered here:
68 * <ol>
69 * <li><a href="#ArchitectureOverview">Architecture Overview</a>
Ken Wakasa384f8ba2012-03-10 09:59:31 +090070 * <li><a href="#Applications">Applications</a>
71 * <li><a href="#InputMethods">Input Methods</a>
72 * <li><a href="#Security">Security</a>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 * </ol>
74 *
75 * <a name="ArchitectureOverview"></a>
76 * <h3>Architecture Overview</h3>
77 *
78 * <p>There are three primary parties involved in the input method
79 * framework (IMF) architecture:</p>
80 *
81 * <ul>
82 * <li> The <strong>input method manager</strong> as expressed by this class
83 * is the central point of the system that manages interaction between all
84 * other parts. It is expressed as the client-side API here which exists
85 * in each application context and communicates with a global system service
86 * that manages the interaction across all processes.
87 * <li> An <strong>input method (IME)</strong> implements a particular
88 * interaction model allowing the user to generate text. The system binds
89 * to the current input method that is use, causing it to be created and run,
90 * and tells it when to hide and show its UI. Only one IME is running at a time.
91 * <li> Multiple <strong>client applications</strong> arbitrate with the input
92 * method manager for input focus and control over the state of the IME. Only
93 * one such client is ever active (working with the IME) at a time.
94 * </ul>
95 *
96 *
97 * <a name="Applications"></a>
98 * <h3>Applications</h3>
99 *
100 * <p>In most cases, applications that are using the standard
101 * {@link android.widget.TextView} or its subclasses will have little they need
102 * to do to work well with soft input methods. The main things you need to
103 * be aware of are:</p>
104 *
105 * <ul>
Gilles Debunne8cbb4c62011-01-24 12:33:56 -0800106 * <li> Properly set the {@link android.R.attr#inputType} in your editable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 * text views, so that the input method will have enough context to help the
108 * user in entering text into them.
109 * <li> Deal well with losing screen space when the input method is
110 * displayed. Ideally an application should handle its window being resized
111 * smaller, but it can rely on the system performing panning of the window
112 * if needed. You should set the {@link android.R.attr#windowSoftInputMode}
113 * attribute on your activity or the corresponding values on windows you
114 * create to help the system determine whether to pan or resize (it will
115 * try to determine this automatically but may get it wrong).
116 * <li> You can also control the preferred soft input state (open, closed, etc)
117 * for your window using the same {@link android.R.attr#windowSoftInputMode}
118 * attribute.
119 * </ul>
120 *
121 * <p>More finer-grained control is available through the APIs here to directly
122 * interact with the IMF and its IME -- either showing or hiding the input
123 * area, letting the user pick an input method, etc.</p>
124 *
125 * <p>For the rare people amongst us writing their own text editors, you
126 * will need to implement {@link android.view.View#onCreateInputConnection}
127 * to return a new instance of your own {@link InputConnection} interface
128 * allowing the IME to interact with your editor.</p>
129 *
130 *
131 * <a name="InputMethods"></a>
132 * <h3>Input Methods</h3>
133 *
134 * <p>An input method (IME) is implemented
135 * as a {@link android.app.Service}, typically deriving from
136 * {@link android.inputmethodservice.InputMethodService}. It must provide
137 * the core {@link InputMethod} interface, though this is normally handled by
138 * {@link android.inputmethodservice.InputMethodService} and implementors will
139 * only need to deal with the higher-level API there.</p>
140 *
141 * See the {@link android.inputmethodservice.InputMethodService} class for
142 * more information on implementing IMEs.
143 *
144 *
145 * <a name="Security"></a>
146 * <h3>Security</h3>
147 *
148 * <p>There are a lot of security issues associated with input methods,
149 * since they essentially have freedom to completely drive the UI and monitor
150 * everything the user enters. The Android input method framework also allows
151 * arbitrary third party IMEs, so care must be taken to restrict their
152 * selection and interactions.</p>
153 *
154 * <p>Here are some key points about the security architecture behind the
155 * IMF:</p>
156 *
157 * <ul>
158 * <li> <p>Only the system is allowed to directly access an IME's
159 * {@link InputMethod} interface, via the
160 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission. This is
161 * enforced in the system by not binding to an input method service that does
162 * not require this permission, so the system can guarantee no other untrusted
163 * clients are accessing the current input method outside of its control.</p>
164 *
165 * <li> <p>There may be many client processes of the IMF, but only one may
166 * be active at a time. The inactive clients can not interact with key
167 * parts of the IMF through the mechanisms described below.</p>
168 *
169 * <li> <p>Clients of an input method are only given access to its
170 * {@link InputMethodSession} interface. One instance of this interface is
171 * created for each client, and only calls from the session associated with
172 * the active client will be processed by the current IME. This is enforced
173 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
174 * IMEs, but must be explicitly handled by an IME that is customizing the
175 * raw {@link InputMethodSession} implementation.</p>
176 *
177 * <li> <p>Only the active client's {@link InputConnection} will accept
178 * operations. The IMF tells each client process whether it is active, and
179 * the framework enforces that in inactive processes calls on to the current
180 * InputConnection will be ignored. This ensures that the current IME can
181 * only deliver events and text edits to the UI that the user sees as
182 * being in focus.</p>
183 *
184 * <li> <p>An IME can never interact with an {@link InputConnection} while
185 * the screen is off. This is enforced by making all clients inactive while
186 * the screen is off, and prevents bad IMEs from driving the UI when the user
187 * can not be aware of its behavior.</p>
188 *
189 * <li> <p>A client application can ask that the system let the user pick a
190 * new IME, but can not programmatically switch to one itself. This avoids
191 * malicious applications from switching the user to their own IME, which
192 * remains running when the user navigates away to another application. An
193 * IME, on the other hand, <em>is</em> allowed to programmatically switch
194 * the system to another IME, since it already has full control of user
195 * input.</p>
196 *
197 * <li> <p>The user must explicitly enable a new IME in settings before
198 * they can switch to it, to confirm with the system that they know about it
199 * and want to make it available for use.</p>
200 * </ul>
201 */
202public final class InputMethodManager {
Dianne Hackborn4eba2712009-03-24 19:20:06 -0700203 static final boolean DEBUG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 static final String TAG = "InputMethodManager";
205
Jeff Brownf9e989d2013-04-04 23:04:03 -0700206 static final String PENDING_EVENT_COUNTER = "aq:imm";
207
208 static InputMethodManager sInstance;
Dianne Hackborn7663d802012-02-24 13:08:49 -0800209
210 /**
211 * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
212 * the window has input focus.
213 */
214 public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0;
215
216 /**
217 * @hide Flag for IInputMethodManager.windowGainedFocus: the focus
218 * is a text editor.
219 */
220 public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1;
221
222 /**
223 * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first
224 * time the window has gotten focus.
225 */
226 public static final int CONTROL_WINDOW_FIRST = 1<<2;
227
228 /**
229 * @hide Flag for IInputMethodManager.startInput: this is the first
230 * time the window has gotten focus.
231 */
232 public static final int CONTROL_START_INITIAL = 1<<8;
233
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700234 /**
235 * Timeout in milliseconds for delivering a key to an IME.
236 */
237 static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
238
Jeff Brownf9e989d2013-04-04 23:04:03 -0700239 /** @hide */
240 public static final int DISPATCH_IN_PROGRESS = -1;
241
242 /** @hide */
243 public static final int DISPATCH_NOT_HANDLED = 0;
244
245 /** @hide */
246 public static final int DISPATCH_HANDLED = 1;
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 final IInputMethodManager mService;
249 final Looper mMainLooper;
250
251 // For scheduling work on the main thread. This also serves as our
252 // global lock.
253 final H mH;
254
255 // Our generic input connection if the current target does not have its own.
256 final IInputContext mIInputContext;
257
258 /**
259 * True if this input method client is active, initially false.
260 */
261 boolean mActive = false;
262
263 /**
264 * Set whenever this client becomes inactive, to know we need to reset
Dianne Hackborn7663d802012-02-24 13:08:49 -0800265 * state with the IME the next time we receive focus.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 */
267 boolean mHasBeenInactive = true;
268
269 /**
270 * As reported by IME through InputConnection.
271 */
272 boolean mFullscreenMode;
273
274 // -----------------------------------------------------------
275
276 /**
277 * This is the root view of the overall window that currently has input
278 * method focus.
279 */
280 View mCurRootView;
281 /**
282 * This is the view that should currently be served by an input method,
283 * regardless of the state of setting that up.
284 */
285 View mServedView;
286 /**
287 * This is then next view that will be served by the input method, when
288 * we get around to updating things.
289 */
290 View mNextServedView;
291 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 * This is set when we are in the process of connecting, to determine
293 * when we have actually finished.
294 */
295 boolean mServedConnecting;
296 /**
297 * This is non-null when we have connected the served view; it holds
298 * the attributes that were last retrieved from the served view and given
299 * to the input connection.
300 */
301 EditorInfo mCurrentTextBoxAttribute;
302 /**
303 * The InputConnection that was last retrieved from the served view.
304 */
305 InputConnection mServedInputConnection;
Dianne Hackbornac920872012-05-22 11:49:49 -0700306 ControlledInputConnectionWrapper mServedInputConnectionWrapper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 /**
308 * The completions that were last provided by the served view.
309 */
310 CompletionInfo[] mCompletions;
311
312 // Cursor position on the screen.
313 Rect mTmpCursorRect = new Rect();
314 Rect mCursorRect = new Rect();
315 int mCursorSelStart;
316 int mCursorSelEnd;
317 int mCursorCandStart;
318 int mCursorCandEnd;
319
320 // -----------------------------------------------------------
321
322 /**
323 * Sequence number of this binding, as returned by the server.
324 */
325 int mBindSequence = -1;
326 /**
327 * ID of the method we are bound to.
328 */
329 String mCurId;
330 /**
331 * The actual instance of the method to make calls on it.
332 */
333 IInputMethodSession mCurMethod;
Jeff Brownc28867a2013-03-26 15:42:39 -0700334 InputChannel mCurChannel;
335 ImeInputEventSender mCurSender;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336
Jeff Brownf9e989d2013-04-04 23:04:03 -0700337 final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
338 final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700339
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 // -----------------------------------------------------------
341
342 static final int MSG_DUMP = 1;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800343 static final int MSG_BIND = 2;
344 static final int MSG_UNBIND = 3;
345 static final int MSG_SET_ACTIVE = 4;
Jeff Brownf9e989d2013-04-04 23:04:03 -0700346 static final int MSG_SEND_INPUT_EVENT = 5;
347 static final int MSG_TIMEOUT_INPUT_EVENT = 6;
348 static final int MSG_FLUSH_INPUT_EVENT = 7;
349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 class H extends Handler {
351 H(Looper looper) {
Jeff Brown29c0ed22013-01-14 13:50:37 -0800352 super(looper, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 }
354
355 @Override
356 public void handleMessage(Message msg) {
357 switch (msg.what) {
358 case MSG_DUMP: {
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700359 SomeArgs args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 try {
361 doDump((FileDescriptor)args.arg1,
362 (PrintWriter)args.arg2, (String[])args.arg3);
363 } catch (RuntimeException e) {
364 ((PrintWriter)args.arg2).println("Exception: " + e);
365 }
366 synchronized (args.arg4) {
367 ((CountDownLatch)args.arg4).countDown();
368 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700369 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 return;
371 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800372 case MSG_BIND: {
373 final InputBindResult res = (InputBindResult)msg.obj;
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +0900374 if (DEBUG) {
375 Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
376 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800377 synchronized (mH) {
378 if (mBindSequence < 0 || mBindSequence != res.sequence) {
379 Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
380 + ", given seq=" + res.sequence);
Jeff Brown4d656882013-04-03 14:39:19 -0700381 if (res.channel != null && res.channel != mCurChannel) {
Jeff Brownc28867a2013-03-26 15:42:39 -0700382 res.channel.dispose();
383 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800384 return;
385 }
Jeff Brown4d656882013-04-03 14:39:19 -0700386
387 setInputChannelLocked(res.channel);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800388 mCurMethod = res.method;
389 mCurId = res.id;
390 mBindSequence = res.sequence;
391 }
Dianne Hackborn7663d802012-02-24 13:08:49 -0800392 startInputInner(null, 0, 0, 0);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800393 return;
394 }
395 case MSG_UNBIND: {
396 final int sequence = msg.arg1;
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +0900397 if (DEBUG) {
398 Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence);
399 }
Dianne Hackborn06a591c2012-02-16 10:25:26 -0800400 boolean startInput = false;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800401 synchronized (mH) {
402 if (mBindSequence == sequence) {
403 if (false) {
404 // XXX the server has already unbound!
405 if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
406 try {
407 mCurMethod.finishInput();
408 } catch (RemoteException e) {
409 Log.w(TAG, "IME died: " + mCurId, e);
410 }
411 }
412 }
413 clearBindingLocked();
414
415 // If we were actively using the last input method, then
416 // we would like to re-connect to the next input method.
417 if (mServedView != null && mServedView.isFocused()) {
418 mServedConnecting = true;
419 }
Dianne Hackborna82ba542012-02-15 18:19:55 -0800420 if (mActive) {
Dianne Hackborn06a591c2012-02-16 10:25:26 -0800421 startInput = true;
Dianne Hackborna82ba542012-02-15 18:19:55 -0800422 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800423 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800424 }
Dianne Hackborn06a591c2012-02-16 10:25:26 -0800425 if (startInput) {
Dianne Hackborn7663d802012-02-24 13:08:49 -0800426 startInputInner(null, 0, 0, 0);
Dianne Hackborn06a591c2012-02-16 10:25:26 -0800427 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800428 return;
429 }
430 case MSG_SET_ACTIVE: {
431 final boolean active = msg.arg1 != 0;
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +0900432 if (DEBUG) {
433 Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive);
434 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800435 synchronized (mH) {
436 mActive = active;
437 mFullscreenMode = false;
438 if (!active) {
439 // Some other client has starting using the IME, so note
440 // that this happened and make sure our own editor's
441 // state is reset.
442 mHasBeenInactive = true;
443 try {
444 // Note that finishComposingText() is allowed to run
445 // even when we are not active.
446 mIInputContext.finishComposingText();
447 } catch (RemoteException e) {
448 }
satok31e4e142012-03-22 15:34:16 +0900449 // Check focus again in case that "onWindowFocus" is called before
450 // handling this message.
satok05a6cbe2012-04-05 23:04:08 +0900451 if (mServedView != null && mServedView.hasWindowFocus()) {
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +0900452 // "finishComposingText" has been already called above. So we
453 // should not call mServedInputConnection.finishComposingText here.
454 // Also, please note that this handler thread could be different
455 // from a thread that created mServedView. That could happen
456 // the current activity is running in the system process.
457 // In that case, we really should not call
458 // mServedInputConnection.finishComposingText.
459 if (checkFocusNoStartInput(mHasBeenInactive, false)) {
460 startInputInner(null, 0, 0, 0);
461 }
satok05a6cbe2012-04-05 23:04:08 +0900462 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800463 }
464 }
465 return;
466 }
Jeff Brownf9e989d2013-04-04 23:04:03 -0700467 case MSG_SEND_INPUT_EVENT: {
468 sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj);
469 return;
470 }
471 case MSG_TIMEOUT_INPUT_EVENT: {
472 finishedInputEvent(msg.arg1, false, true);
473 return;
474 }
475 case MSG_FLUSH_INPUT_EVENT: {
476 finishedInputEvent(msg.arg1, false, false);
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700477 return;
478 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 }
480 }
481 }
482
Jean Chalardde9dbb02011-10-20 19:50:45 +0900483 private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
484 private final InputMethodManager mParentInputMethodManager;
Dianne Hackbornac920872012-05-22 11:49:49 -0700485 private boolean mActive;
Jean Chalardde9dbb02011-10-20 19:50:45 +0900486
487 public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
488 final InputMethodManager inputMethodManager) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 super(mainLooper, conn);
Jean Chalardde9dbb02011-10-20 19:50:45 +0900490 mParentInputMethodManager = inputMethodManager;
Dianne Hackbornac920872012-05-22 11:49:49 -0700491 mActive = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 }
493
Gilles Debunne8cbb4c62011-01-24 12:33:56 -0800494 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 public boolean isActive() {
Dianne Hackbornac920872012-05-22 11:49:49 -0700496 return mParentInputMethodManager.mActive && mActive;
497 }
498
499 void deactivate() {
500 mActive = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 }
502 }
503
504 final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
Jeff Brownc28867a2013-03-26 15:42:39 -0700505 @Override
506 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 // No need to check for dump permission, since we only give this
508 // interface to the system.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 CountDownLatch latch = new CountDownLatch(1);
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700510 SomeArgs sargs = SomeArgs.obtain();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 sargs.arg1 = fd;
512 sargs.arg2 = fout;
513 sargs.arg3 = args;
514 sargs.arg4 = latch;
515 mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
516 try {
517 if (!latch.await(5, TimeUnit.SECONDS)) {
518 fout.println("Timeout waiting for dump");
519 }
520 } catch (InterruptedException e) {
521 fout.println("Interrupted waiting for dump");
522 }
523 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700524
525 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 public void setUsingInputMethod(boolean state) {
527 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700528
529 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 public void onBindMethod(InputBindResult res) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800531 mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700533
534 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 public void onUnbindMethod(int sequence) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800536 mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700538
539 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 public void setActive(boolean active) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800541 mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 }
Jeff Brownc28867a2013-03-26 15:42:39 -0700543 };
544
Dianne Hackborn51bf0772009-03-24 19:11:41 -0700545 final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
Jeff Brown04ddf3c2012-06-14 03:57:49 -0700546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 InputMethodManager(IInputMethodManager service, Looper looper) {
548 mService = service;
549 mMainLooper = looper;
550 mH = new H(looper);
551 mIInputContext = new ControlledInputConnectionWrapper(looper,
Jean Chalardde9dbb02011-10-20 19:50:45 +0900552 mDummyInputConnection, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553 }
554
555 /**
556 * Retrieve the global InputMethodManager instance, creating it if it
557 * doesn't already exist.
558 * @hide
559 */
Jeff Brownf9e989d2013-04-04 23:04:03 -0700560 public static InputMethodManager getInstance() {
561 synchronized (InputMethodManager.class) {
562 if (sInstance == null) {
563 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
564 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
565 sInstance = new InputMethodManager(service, Looper.getMainLooper());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 }
Jeff Brownf9e989d2013-04-04 23:04:03 -0700567 return sInstance;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 }
570
571 /**
572 * Private optimization: retrieve the global InputMethodManager instance,
573 * if it exists.
574 * @hide
575 */
Jeff Brownf9e989d2013-04-04 23:04:03 -0700576 public static InputMethodManager peekInstance() {
577 return sInstance;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 }
579
580 /** @hide */
581 public IInputMethodClient getClient() {
582 return mClient;
583 }
584
585 /** @hide */
586 public IInputContext getInputContext() {
587 return mIInputContext;
588 }
589
590 public List<InputMethodInfo> getInputMethodList() {
591 try {
592 return mService.getInputMethodList();
593 } catch (RemoteException e) {
594 throw new RuntimeException(e);
595 }
596 }
597
598 public List<InputMethodInfo> getEnabledInputMethodList() {
599 try {
600 return mService.getEnabledInputMethodList();
601 } catch (RemoteException e) {
602 throw new RuntimeException(e);
603 }
604 }
605
satokd4fce2b2011-04-11 12:07:13 +0900606 /**
607 * Returns a list of enabled input method subtypes for the specified input method info.
608 * @param imi An input method info whose subtypes list will be returned.
609 * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly
610 * selected subtypes. If an input method info doesn't have enabled subtypes, the framework
611 * will implicitly enable subtypes according to the current system language.
612 */
satok16331c82010-12-20 23:48:46 +0900613 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
614 boolean allowsImplicitlySelectedSubtypes) {
satok67ddf9c2010-11-17 09:45:54 +0900615 try {
Satoshi Kataokab3c21ac2013-08-07 15:43:29 +0900616 return mService.getEnabledInputMethodSubtypeList(
617 imi == null ? null : imi.getId(), allowsImplicitlySelectedSubtypes);
satok67ddf9c2010-11-17 09:45:54 +0900618 } catch (RemoteException e) {
619 throw new RuntimeException(e);
620 }
621 }
622
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
624 try {
625 mService.updateStatusIcon(imeToken, packageName, iconId);
626 } catch (RemoteException e) {
627 throw new RuntimeException(e);
628 }
629 }
630
631 public void hideStatusIcon(IBinder imeToken) {
632 try {
633 mService.updateStatusIcon(imeToken, null, 0);
634 } catch (RemoteException e) {
635 throw new RuntimeException(e);
636 }
637 }
638
639 /** @hide */
Joe Onorato857fd9b2011-01-27 15:08:35 -0800640 public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
satok06487a52010-10-29 11:37:18 +0900641 try {
Joe Onorato857fd9b2011-01-27 15:08:35 -0800642 mService.setImeWindowStatus(imeToken, vis, backDisposition);
satok06487a52010-10-29 11:37:18 +0900643 } catch (RemoteException e) {
644 throw new RuntimeException(e);
645 }
646 }
647
648 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 public void setFullscreenMode(boolean fullScreen) {
650 mFullscreenMode = fullScreen;
651 }
satokf9f01002011-05-19 21:31:50 +0900652
653 /** @hide */
654 public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
655 try {
656 mService.registerSuggestionSpansForNotification(spans);
657 } catch (RemoteException e) {
658 throw new RuntimeException(e);
659 }
660 }
661
662 /** @hide */
663 public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
664 try {
665 mService.notifySuggestionPicked(span, originalString, index);
666 } catch (RemoteException e) {
667 throw new RuntimeException(e);
668 }
669 }
670
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 /**
672 * Allows you to discover whether the attached input method is running
673 * in fullscreen mode. Return true if it is fullscreen, entirely covering
674 * your UI, else returns false.
675 */
676 public boolean isFullscreenMode() {
677 return mFullscreenMode;
678 }
679
680 /**
681 * Return true if the given view is the currently active view for the
682 * input method.
683 */
684 public boolean isActive(View view) {
685 checkFocus();
686 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700687 return (mServedView == view
688 || (mServedView != null
689 && mServedView.checkInputConnectionProxy(view)))
690 && mCurrentTextBoxAttribute != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 }
692 }
693
694 /**
695 * Return true if any view is currently active in the input method.
696 */
697 public boolean isActive() {
698 checkFocus();
699 synchronized (mH) {
700 return mServedView != null && mCurrentTextBoxAttribute != null;
701 }
702 }
703
704 /**
705 * Return true if the currently served view is accepting full text edits.
706 * If false, it has no input connection, so can only handle raw key events.
707 */
708 public boolean isAcceptingText() {
709 checkFocus();
710 return mServedInputConnection != null;
711 }
712
713 /**
714 * Reset all of the state associated with being bound to an input method.
715 */
716 void clearBindingLocked() {
717 clearConnectionLocked();
Jeff Brown4d656882013-04-03 14:39:19 -0700718 setInputChannelLocked(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 mBindSequence = -1;
720 mCurId = null;
721 mCurMethod = null;
Jeff Brown4d656882013-04-03 14:39:19 -0700722 }
723
724 void setInputChannelLocked(InputChannel channel) {
725 if (mCurChannel != channel) {
726 if (mCurSender != null) {
727 flushPendingEventsLocked();
728 mCurSender.dispose();
729 mCurSender = null;
730 }
731 if (mCurChannel != null) {
732 mCurChannel.dispose();
733 }
734 mCurChannel = channel;
Jeff Brownc28867a2013-03-26 15:42:39 -0700735 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
Jeff Brown4d656882013-04-03 14:39:19 -0700737
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 /**
739 * Reset all of the state associated with a served view being connected
740 * to an input method
741 */
742 void clearConnectionLocked() {
743 mCurrentTextBoxAttribute = null;
744 mServedInputConnection = null;
Dianne Hackbornac920872012-05-22 11:49:49 -0700745 if (mServedInputConnectionWrapper != null) {
746 mServedInputConnectionWrapper.deactivate();
747 mServedInputConnectionWrapper = null;
748 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 }
750
751 /**
752 * Disconnect any existing input connection, clearing the served view.
753 */
754 void finishInputLocked() {
Jeff Sharkey4478de32012-04-27 15:41:24 -0700755 mCurRootView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 mNextServedView = null;
757 if (mServedView != null) {
758 if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
759
760 if (mCurrentTextBoxAttribute != null) {
761 try {
762 mService.finishInput(mClient);
763 } catch (RemoteException e) {
764 }
765 }
766
Gilles Debunnec478c172011-12-19 17:29:24 -0800767 notifyInputConnectionFinished();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768
769 mServedView = null;
770 mCompletions = null;
771 mServedConnecting = false;
772 clearConnectionLocked();
773 }
774 }
Gilles Debunnec478c172011-12-19 17:29:24 -0800775
776 /**
777 * Notifies the served view that the current InputConnection will no longer be used.
778 */
779 private void notifyInputConnectionFinished() {
780 if (mServedView != null && mServedInputConnection != null) {
781 // We need to tell the previously served view that it is no
782 // longer the input target, so it can reset its state. Schedule
783 // this call on its window's Handler so it will be on the correct
784 // thread and outside of our lock.
Jeff Browna175a5b2012-02-15 19:18:31 -0800785 ViewRootImpl viewRootImpl = mServedView.getViewRootImpl();
786 if (viewRootImpl != null) {
Gilles Debunnec478c172011-12-19 17:29:24 -0800787 // This will result in a call to reportFinishInputConnection() below.
Jeff Browna175a5b2012-02-15 19:18:31 -0800788 viewRootImpl.dispatchFinishInputConnection(mServedInputConnection);
Gilles Debunnec478c172011-12-19 17:29:24 -0800789 }
790 }
791 }
792
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 /**
794 * Called from the FINISH_INPUT_CONNECTION message above.
795 * @hide
796 */
797 public void reportFinishInputConnection(InputConnection ic) {
798 if (mServedInputConnection != ic) {
799 ic.finishComposingText();
Gilles Debunne9d69ecb2012-02-24 16:07:09 -0800800 // To avoid modifying the public InputConnection interface
801 if (ic instanceof BaseInputConnection) {
802 ((BaseInputConnection) ic).reportFinish();
803 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
805 }
Gilles Debunnec478c172011-12-19 17:29:24 -0800806
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 public void displayCompletions(View view, CompletionInfo[] completions) {
808 checkFocus();
809 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700810 if (mServedView != view && (mServedView == null
811 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 return;
813 }
814
815 mCompletions = completions;
816 if (mCurMethod != null) {
817 try {
818 mCurMethod.displayCompletions(mCompletions);
819 } catch (RemoteException e) {
820 }
821 }
822 }
823 }
824
825 public void updateExtractedText(View view, int token, ExtractedText text) {
826 checkFocus();
827 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700828 if (mServedView != view && (mServedView == null
829 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 return;
831 }
832
833 if (mCurMethod != null) {
834 try {
835 mCurMethod.updateExtractedText(token, text);
836 } catch (RemoteException e) {
837 }
838 }
839 }
840 }
841
842 /**
843 * Flag for {@link #showSoftInput} to indicate that this is an implicit
844 * request to show the input window, not as the result of a direct request
845 * by the user. The window may not be shown in this case.
846 */
847 public static final int SHOW_IMPLICIT = 0x0001;
848
849 /**
850 * Flag for {@link #showSoftInput} to indicate that the user has forced
851 * the input method open (such as by long-pressing menu) so it should
852 * not be closed until they explicitly do so.
853 */
854 public static final int SHOW_FORCED = 0x0002;
855
856 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -0800857 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
858 * a result receiver: explicitly request that the current input method's
859 * soft input area be shown to the user, if needed.
860 *
861 * @param view The currently focused view, which would like to receive
862 * soft keyboard input.
863 * @param flags Provides additional operating flags. Currently may be
864 * 0 or have the {@link #SHOW_IMPLICIT} bit set.
865 */
866 public boolean showSoftInput(View view, int flags) {
867 return showSoftInput(view, flags, null);
868 }
869
870 /**
871 * Flag for the {@link ResultReceiver} result code from
872 * {@link #showSoftInput(View, int, ResultReceiver)} and
873 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
874 * state of the soft input window was unchanged and remains shown.
875 */
876 public static final int RESULT_UNCHANGED_SHOWN = 0;
877
878 /**
879 * Flag for the {@link ResultReceiver} result code from
880 * {@link #showSoftInput(View, int, ResultReceiver)} and
881 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
882 * state of the soft input window was unchanged and remains hidden.
883 */
884 public static final int RESULT_UNCHANGED_HIDDEN = 1;
885
886 /**
887 * Flag for the {@link ResultReceiver} result code from
888 * {@link #showSoftInput(View, int, ResultReceiver)} and
889 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
890 * state of the soft input window changed from hidden to shown.
891 */
892 public static final int RESULT_SHOWN = 2;
893
894 /**
895 * Flag for the {@link ResultReceiver} result code from
896 * {@link #showSoftInput(View, int, ResultReceiver)} and
897 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
898 * state of the soft input window changed from shown to hidden.
899 */
900 public static final int RESULT_HIDDEN = 3;
901
902 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 * Explicitly request that the current input method's soft input area be
904 * shown to the user, if needed. Call this if the user interacts with
905 * your view in such a way that they have expressed they would like to
906 * start performing input into it.
907 *
908 * @param view The currently focused view, which would like to receive
909 * soft keyboard input.
910 * @param flags Provides additional operating flags. Currently may be
911 * 0 or have the {@link #SHOW_IMPLICIT} bit set.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800912 * @param resultReceiver If non-null, this will be called by the IME when
913 * it has processed your request to tell you what it has done. The result
914 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
915 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
916 * {@link #RESULT_HIDDEN}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 */
Gilles Debunnead8484b2011-02-17 17:37:51 -0800918 public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 checkFocus();
920 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700921 if (mServedView != view && (mServedView == null
922 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800923 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 }
925
926 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800927 return mService.showSoftInput(mClient, flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800928 } catch (RemoteException e) {
929 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800930
931 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 }
933 }
934
935 /** @hide */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800936 public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800938 mService.showSoftInput(mClient, flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 } catch (RemoteException e) {
940 }
941 }
942
943 /**
944 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
945 * input window should only be hidden if it was not explicitly shown
946 * by the user.
947 */
948 public static final int HIDE_IMPLICIT_ONLY = 0x0001;
949
950 /**
951 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
952 * input window should normally be hidden, unless it was originally
953 * shown with {@link #SHOW_FORCED}.
954 */
955 public static final int HIDE_NOT_ALWAYS = 0x0002;
Gilles Debunnec478c172011-12-19 17:29:24 -0800956
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 /**
Gilles Debunne7c8c6d62011-01-24 14:48:14 -0800958 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}
The Android Open Source Project4df24232009-03-05 14:34:35 -0800959 * without a result: request to hide the soft input window from the
960 * context of the window that is currently accepting input.
961 *
962 * @param windowToken The token of the window that is making the request,
963 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
964 * @param flags Provides additional operating flags. Currently may be
965 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
966 */
967 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
968 return hideSoftInputFromWindow(windowToken, flags, null);
969 }
970
971 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 * Request to hide the soft input window from the context of the window
973 * that is currently accepting input. This should be called as a result
974 * of the user doing some actually than fairly explicitly requests to
975 * have the input window hidden.
976 *
977 * @param windowToken The token of the window that is making the request,
978 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
979 * @param flags Provides additional operating flags. Currently may be
980 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800981 * @param resultReceiver If non-null, this will be called by the IME when
982 * it has processed your request to tell you what it has done. The result
983 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
984 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
985 * {@link #RESULT_HIDDEN}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800987 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
988 ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 checkFocus();
990 synchronized (mH) {
991 if (mServedView == null || mServedView.getWindowToken() != windowToken) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800992 return false;
993 }
994
995 try {
996 return mService.hideSoftInput(mClient, flags, resultReceiver);
997 } catch (RemoteException e) {
998 }
999 return false;
1000 }
1001 }
1002
1003
1004 /**
1005 * This method toggles the input method window display.
1006 * If the input window is already displayed, it gets hidden.
1007 * If not the input window will be displayed.
1008 * @param windowToken The token of the window that is making the request,
1009 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
1010 * @param showFlags Provides additional operating flags. May be
1011 * 0 or have the {@link #SHOW_IMPLICIT},
1012 * {@link #SHOW_FORCED} bit set.
1013 * @param hideFlags Provides additional operating flags. May be
1014 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1015 * {@link #HIDE_NOT_ALWAYS} bit set.
1016 **/
1017 public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
1018 synchronized (mH) {
1019 if (mServedView == null || mServedView.getWindowToken() != windowToken) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 return;
1021 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001022 if (mCurMethod != null) {
1023 try {
1024 mCurMethod.toggleSoftInput(showFlags, hideFlags);
1025 } catch (RemoteException e) {
1026 }
1027 }
1028 }
1029 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030
The Android Open Source Project4df24232009-03-05 14:34:35 -08001031 /*
1032 * This method toggles the input method window display.
1033 * If the input window is already displayed, it gets hidden.
1034 * If not the input window will be displayed.
1035 * @param showFlags Provides additional operating flags. May be
1036 * 0 or have the {@link #SHOW_IMPLICIT},
1037 * {@link #SHOW_FORCED} bit set.
1038 * @param hideFlags Provides additional operating flags. May be
1039 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1040 * {@link #HIDE_NOT_ALWAYS} bit set.
1041 * @hide
1042 */
1043 public void toggleSoftInput(int showFlags, int hideFlags) {
1044 if (mCurMethod != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001046 mCurMethod.toggleSoftInput(showFlags, hideFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 } catch (RemoteException e) {
1048 }
1049 }
1050 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001051
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 /**
1053 * If the input method is currently connected to the given view,
1054 * restart it with its new contents. You should call this when the text
1055 * within your view changes outside of the normal input method or key
1056 * input flow, such as when an application calls TextView.setText().
1057 *
1058 * @param view The view whose text has changed.
1059 */
1060 public void restartInput(View view) {
1061 checkFocus();
1062 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001063 if (mServedView != view && (mServedView == null
1064 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 return;
1066 }
1067
1068 mServedConnecting = true;
1069 }
1070
Dianne Hackborn7663d802012-02-24 13:08:49 -08001071 startInputInner(null, 0, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 }
1073
Dianne Hackborn7663d802012-02-24 13:08:49 -08001074 boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
1075 int windowFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076 final View view;
1077 synchronized (mH) {
1078 view = mServedView;
1079
1080 // Make sure we have a window token for the served view.
1081 if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
1082 if (view == null) {
1083 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
Dianne Hackborn7663d802012-02-24 13:08:49 -08001084 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 }
1086 }
1087
1088 // Now we need to get an input connection from the served view.
1089 // This is complicated in a couple ways: we can't be holding our lock
1090 // when calling out to the view, and we need to make sure we call into
1091 // the view on the same thread that is driving its view hierarchy.
1092 Handler vh = view.getHandler();
1093 if (vh == null) {
1094 // If the view doesn't have a handler, something has changed out
Satoshi Kataoka35739502012-10-02 19:00:26 +09001095 // from under us, so just close the current input.
1096 // If we don't close the current input, the current input method can remain on the
1097 // screen without a connection.
1098 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view! Close current input.");
1099 closeCurrentInput();
Dianne Hackborn7663d802012-02-24 13:08:49 -08001100 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 }
1102 if (vh.getLooper() != Looper.myLooper()) {
1103 // The view is running on a different thread than our own, so
1104 // we need to reschedule our work for over there.
1105 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
1106 vh.post(new Runnable() {
Jeff Brownc28867a2013-03-26 15:42:39 -07001107 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 public void run() {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001109 startInputInner(null, 0, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 }
1111 });
Dianne Hackborn7663d802012-02-24 13:08:49 -08001112 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 }
1114
1115 // Okay we are now ready to call into the served view and have it
1116 // do its stuff.
1117 // Life is good: let's hook everything up!
1118 EditorInfo tba = new EditorInfo();
1119 tba.packageName = view.getContext().getPackageName();
1120 tba.fieldId = view.getId();
1121 InputConnection ic = view.onCreateInputConnection(tba);
1122 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
Gilles Debunnec478c172011-12-19 17:29:24 -08001123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 synchronized (mH) {
1125 // Now that we are locked again, validate that our state hasn't
1126 // changed.
1127 if (mServedView != view || !mServedConnecting) {
1128 // Something else happened, so abort.
1129 if (DEBUG) Log.v(TAG,
1130 "Starting input: finished by someone else (view="
1131 + mServedView + " conn=" + mServedConnecting + ")");
Dianne Hackborn7663d802012-02-24 13:08:49 -08001132 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 // If we already have a text box, then this view is already
1136 // connected so we want to restart it.
Dianne Hackborn7663d802012-02-24 13:08:49 -08001137 if (mCurrentTextBoxAttribute == null) {
1138 controlFlags |= CONTROL_START_INITIAL;
1139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140
1141 // Hook 'em up and let 'er rip.
1142 mCurrentTextBoxAttribute = tba;
1143 mServedConnecting = false;
Gilles Debunnec478c172011-12-19 17:29:24 -08001144 // Notify the served view that its previous input connection is finished
1145 notifyInputConnectionFinished();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 mServedInputConnection = ic;
Dianne Hackbornac920872012-05-22 11:49:49 -07001147 ControlledInputConnectionWrapper servedContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 if (ic != null) {
1149 mCursorSelStart = tba.initialSelStart;
1150 mCursorSelEnd = tba.initialSelEnd;
1151 mCursorCandStart = -1;
1152 mCursorCandEnd = -1;
1153 mCursorRect.setEmpty();
Jean Chalardde9dbb02011-10-20 19:50:45 +09001154 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001155 } else {
1156 servedContext = null;
1157 }
Dianne Hackbornac920872012-05-22 11:49:49 -07001158 if (mServedInputConnectionWrapper != null) {
1159 mServedInputConnectionWrapper.deactivate();
1160 }
1161 mServedInputConnectionWrapper = servedContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162
1163 try {
1164 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
Dianne Hackborn7663d802012-02-24 13:08:49 -08001165 + ic + " tba=" + tba + " controlFlags=#"
1166 + Integer.toHexString(controlFlags));
1167 InputBindResult res;
1168 if (windowGainingFocus != null) {
1169 res = mService.windowGainedFocus(mClient, windowGainingFocus,
1170 controlFlags, softInputMode, windowFlags,
1171 tba, servedContext);
1172 } else {
1173 res = mService.startInput(mClient,
1174 servedContext, tba, controlFlags);
1175 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
1177 if (res != null) {
1178 if (res.id != null) {
Jeff Brown4d656882013-04-03 14:39:19 -07001179 setInputChannelLocked(res.channel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 mBindSequence = res.sequence;
1181 mCurMethod = res.method;
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001182 mCurId = res.id;
Jeff Brownc28867a2013-03-26 15:42:39 -07001183 } else {
Jeff Brown4d656882013-04-03 14:39:19 -07001184 if (res.channel != null && res.channel != mCurChannel) {
Jeff Brownc28867a2013-03-26 15:42:39 -07001185 res.channel.dispose();
1186 }
1187 if (mCurMethod == null) {
1188 // This means there is no input method available.
1189 if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1190 return true;
1191 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192 }
1193 }
1194 if (mCurMethod != null && mCompletions != null) {
1195 try {
1196 mCurMethod.displayCompletions(mCompletions);
1197 } catch (RemoteException e) {
1198 }
1199 }
1200 } catch (RemoteException e) {
1201 Log.w(TAG, "IME died: " + mCurId, e);
1202 }
1203 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001204
1205 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 }
1207
1208 /**
1209 * When the focused window is dismissed, this method is called to finish the
1210 * input method started before.
1211 * @hide
1212 */
1213 public void windowDismissed(IBinder appWindowToken) {
1214 checkFocus();
1215 synchronized (mH) {
1216 if (mServedView != null &&
1217 mServedView.getWindowToken() == appWindowToken) {
1218 finishInputLocked();
1219 }
1220 }
1221 }
1222
1223 /**
1224 * Call this when a view receives focus.
1225 * @hide
1226 */
1227 public void focusIn(View view) {
1228 synchronized (mH) {
1229 focusInLocked(view);
1230 }
1231 }
1232
1233 void focusInLocked(View view) {
1234 if (DEBUG) Log.v(TAG, "focusIn: " + view);
1235
1236 if (mCurRootView != view.getRootView()) {
1237 // This is a request from a window that isn't in the window with
1238 // IME focus, so ignore it.
1239 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
1240 return;
1241 }
1242
1243 mNextServedView = view;
1244 scheduleCheckFocusLocked(view);
1245 }
1246
1247 /**
1248 * Call this when a view loses focus.
1249 * @hide
1250 */
1251 public void focusOut(View view) {
1252 synchronized (mH) {
1253 if (DEBUG) Log.v(TAG, "focusOut: " + view
1254 + " mServedView=" + mServedView
1255 + " winFocus=" + view.hasWindowFocus());
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001256 if (mServedView != view) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 // The following code would auto-hide the IME if we end up
1258 // with no more views with focus. This can happen, however,
1259 // whenever we go into touch mode, so it ends up hiding
1260 // at times when we don't really want it to. For now it
1261 // seems better to just turn it all off.
1262 if (false && view.hasWindowFocus()) {
1263 mNextServedView = null;
1264 scheduleCheckFocusLocked(view);
1265 }
1266 }
1267 }
1268 }
1269
Gilles Debunnec478c172011-12-19 17:29:24 -08001270 static void scheduleCheckFocusLocked(View view) {
Jeff Browna175a5b2012-02-15 19:18:31 -08001271 ViewRootImpl viewRootImpl = view.getViewRootImpl();
1272 if (viewRootImpl != null) {
1273 viewRootImpl.dispatchCheckFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274 }
1275 }
Jeff Browna175a5b2012-02-15 19:18:31 -08001276
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 /**
1278 * @hide
1279 */
1280 public void checkFocus() {
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001281 if (checkFocusNoStartInput(false, true)) {
1282 startInputInner(null, 0, 0, 0);
1283 }
Dianne Hackborna82ba542012-02-15 18:19:55 -08001284 }
1285
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001286 private boolean checkFocusNoStartInput(boolean forceNewFocus, boolean finishComposingText) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 // This is called a lot, so short-circuit before locking.
Dianne Hackborn7663d802012-02-24 13:08:49 -08001288 if (mServedView == mNextServedView && !forceNewFocus) {
Dianne Hackborna82ba542012-02-15 18:19:55 -08001289 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 }
satok863fcd62011-06-21 17:38:02 +09001291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 InputConnection ic = null;
1293 synchronized (mH) {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001294 if (mServedView == mNextServedView && !forceNewFocus) {
Dianne Hackborna82ba542012-02-15 18:19:55 -08001295 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 }
1297 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1298 + " next=" + mNextServedView
satok05a6cbe2012-04-05 23:04:08 +09001299 + " forceNewFocus=" + forceNewFocus
1300 + " package="
1301 + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
Dianne Hackborna82ba542012-02-15 18:19:55 -08001302
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001303 if (mNextServedView == null) {
1304 finishInputLocked();
1305 // In this case, we used to have a focused view on the window,
1306 // but no longer do. We should make sure the input method is
1307 // no longer shown, since it serves no purpose.
1308 closeCurrentInput();
Dianne Hackborna82ba542012-02-15 18:19:55 -08001309 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 }
Dianne Hackborna82ba542012-02-15 18:19:55 -08001311
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 ic = mServedInputConnection;
Dianne Hackborna82ba542012-02-15 18:19:55 -08001313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 mServedView = mNextServedView;
1315 mCurrentTextBoxAttribute = null;
1316 mCompletions = null;
1317 mServedConnecting = true;
1318 }
Dianne Hackborna82ba542012-02-15 18:19:55 -08001319
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001320 if (finishComposingText && ic != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 ic.finishComposingText();
1322 }
Dianne Hackborna82ba542012-02-15 18:19:55 -08001323
1324 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 }
1326
1327 void closeCurrentInput() {
1328 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001329 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 } catch (RemoteException e) {
1331 }
1332 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001333
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 /**
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07001335 * Called by ViewAncestor when its window gets input focus.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 * @hide
1337 */
1338 public void onWindowFocus(View rootView, View focusedView, int softInputMode,
1339 boolean first, int windowFlags) {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001340 boolean forceNewFocus = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001341 synchronized (mH) {
1342 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1343 + " softInputMode=" + softInputMode
1344 + " first=" + first + " flags=#"
1345 + Integer.toHexString(windowFlags));
1346 if (mHasBeenInactive) {
1347 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh");
1348 mHasBeenInactive = false;
Dianne Hackborn7663d802012-02-24 13:08:49 -08001349 forceNewFocus = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 }
1351 focusInLocked(focusedView != null ? focusedView : rootView);
1352 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001353
1354 int controlFlags = 0;
1355 if (focusedView != null) {
1356 controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
1357 if (focusedView.onCheckIsTextEditor()) {
1358 controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 }
1360 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08001361 if (first) {
1362 controlFlags |= CONTROL_WINDOW_FIRST;
1363 }
1364
Satoshi Kataoka4e5184f2012-07-13 23:10:58 +09001365 if (checkFocusNoStartInput(forceNewFocus, true)) {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001366 // We need to restart input on the current focus view. This
1367 // should be done in conjunction with telling the system service
1368 // about the window gaining focus, to help make the transition
1369 // smooth.
1370 if (startInputInner(rootView.getWindowToken(),
1371 controlFlags, softInputMode, windowFlags)) {
1372 return;
1373 }
1374 }
1375
1376 // For some reason we didn't do a startInput + windowFocusGain, so
1377 // we'll just do a window focus gain and call it a day.
1378 synchronized (mH) {
1379 try {
Dianne Hackbornac920872012-05-22 11:49:49 -07001380 if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
Dianne Hackborn7663d802012-02-24 13:08:49 -08001381 mService.windowGainedFocus(mClient, rootView.getWindowToken(),
1382 controlFlags, softInputMode, windowFlags, null, null);
1383 } catch (RemoteException e) {
1384 }
Dianne Hackborn06a591c2012-02-16 10:25:26 -08001385 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 }
1387
1388 /** @hide */
1389 public void startGettingWindowFocus(View rootView) {
1390 synchronized (mH) {
1391 mCurRootView = rootView;
1392 }
1393 }
1394
1395 /**
1396 * Report the current selection range.
1397 */
1398 public void updateSelection(View view, int selStart, int selEnd,
1399 int candidatesStart, int candidatesEnd) {
1400 checkFocus();
1401 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001402 if ((mServedView != view && (mServedView == null
1403 || !mServedView.checkInputConnectionProxy(view)))
1404 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 return;
1406 }
1407
1408 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1409 || mCursorCandStart != candidatesStart
1410 || mCursorCandEnd != candidatesEnd) {
1411 if (DEBUG) Log.d(TAG, "updateSelection");
1412
1413 try {
1414 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
Jean Chalardc743cb92013-09-12 16:28:45 +09001415 final int oldSelStart = mCursorSelStart;
1416 final int oldSelEnd = mCursorSelEnd;
1417 // Update internal values before sending updateSelection to the IME, because
1418 // if it changes the text within its onUpdateSelection handler in a way that
1419 // 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 -08001420 mCursorSelStart = selStart;
1421 mCursorSelEnd = selEnd;
1422 mCursorCandStart = candidatesStart;
1423 mCursorCandEnd = candidatesEnd;
Jean Chalardc743cb92013-09-12 16:28:45 +09001424 mCurMethod.updateSelection(oldSelStart, oldSelEnd,
1425 selStart, selEnd, candidatesStart, candidatesEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 } catch (RemoteException e) {
1427 Log.w(TAG, "IME died: " + mCurId, e);
1428 }
1429 }
1430 }
1431 }
1432
1433 /**
satok863fcd62011-06-21 17:38:02 +09001434 * Notify the event when the user tapped or clicked the text view.
1435 */
1436 public void viewClicked(View view) {
1437 final boolean focusChanged = mServedView != mNextServedView;
1438 checkFocus();
1439 synchronized (mH) {
1440 if ((mServedView != view && (mServedView == null
1441 || !mServedView.checkInputConnectionProxy(view)))
1442 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1443 return;
1444 }
1445 try {
1446 if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
1447 mCurMethod.viewClicked(focusChanged);
1448 } catch (RemoteException e) {
1449 Log.w(TAG, "IME died: " + mCurId, e);
1450 }
1451 }
1452 }
1453
1454 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 * Returns true if the current input method wants to watch the location
1456 * of the input editor's cursor in its window.
1457 */
1458 public boolean isWatchingCursor(View view) {
1459 return false;
1460 }
1461
1462 /**
1463 * Report the current cursor location in its window.
1464 */
1465 public void updateCursor(View view, int left, int top, int right, int bottom) {
1466 checkFocus();
1467 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001468 if ((mServedView != view && (mServedView == null
1469 || !mServedView.checkInputConnectionProxy(view)))
1470 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 return;
1472 }
1473
1474 mTmpCursorRect.set(left, top, right, bottom);
1475 if (!mCursorRect.equals(mTmpCursorRect)) {
1476 if (DEBUG) Log.d(TAG, "updateCursor");
1477
1478 try {
1479 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1480 mCurMethod.updateCursor(mTmpCursorRect);
1481 mCursorRect.set(mTmpCursorRect);
1482 } catch (RemoteException e) {
1483 Log.w(TAG, "IME died: " + mCurId, e);
1484 }
1485 }
1486 }
1487 }
1488
1489 /**
1490 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1491 * InputMethodSession.appPrivateCommand()} on the current Input Method.
1492 * @param view Optional View that is sending the command, or null if
1493 * you want to send the command regardless of the view that is attached
1494 * to the input method.
1495 * @param action Name of the command to be performed. This <em>must</em>
1496 * be a scoped name, i.e. prefixed with a package name you own, so that
1497 * different developers will not create conflicting commands.
1498 * @param data Any data to include with the command.
1499 */
1500 public void sendAppPrivateCommand(View view, String action, Bundle data) {
1501 checkFocus();
1502 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001503 if ((mServedView != view && (mServedView == null
1504 || !mServedView.checkInputConnectionProxy(view)))
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1506 return;
1507 }
1508 try {
1509 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1510 mCurMethod.appPrivateCommand(action, data);
1511 } catch (RemoteException e) {
1512 Log.w(TAG, "IME died: " + mCurId, e);
1513 }
1514 }
1515 }
satok28203512010-11-24 11:06:49 +09001516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 /**
satok28203512010-11-24 11:06:49 +09001518 * Force switch to a new input method component. This can only be called
1519 * 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 -08001520 * @param token Supplies the identifying token given to an input method
1521 * when it was started, which allows it to perform this operation on
1522 * itself.
1523 * @param id The unique identifier for the new input method to be switched to.
1524 */
1525 public void setInputMethod(IBinder token, String id) {
1526 try {
1527 mService.setInputMethod(token, id);
1528 } catch (RemoteException e) {
1529 throw new RuntimeException(e);
1530 }
1531 }
satok28203512010-11-24 11:06:49 +09001532
1533 /**
1534 * Force switch to a new input method and subtype. This can only be called
1535 * from an application or a service which has a token of the currently active input method.
1536 * @param token Supplies the identifying token given to an input method
1537 * when it was started, which allows it to perform this operation on
1538 * itself.
1539 * @param id The unique identifier for the new input method to be switched to.
1540 * @param subtype The new subtype of the new input method to be switched to.
1541 */
1542 public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
1543 try {
1544 mService.setInputMethodAndSubtype(token, id, subtype);
1545 } catch (RemoteException e) {
1546 throw new RuntimeException(e);
1547 }
1548 }
1549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 /**
1551 * Close/hide the input method's soft input area, so the user no longer
1552 * sees it or can interact with it. This can only be called
1553 * from the currently active input method, as validated by the given token.
1554 *
1555 * @param token Supplies the identifying token given to an input method
1556 * when it was started, which allows it to perform this operation on
1557 * itself.
1558 * @param flags Provides additional operating flags. Currently may be
The Android Open Source Project4df24232009-03-05 14:34:35 -08001559 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1560 * {@link #HIDE_NOT_ALWAYS} bit set.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001561 */
1562 public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1563 try {
1564 mService.hideMySoftInput(token, flags);
1565 } catch (RemoteException e) {
1566 throw new RuntimeException(e);
1567 }
1568 }
1569
1570 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -08001571 * Show the input method's soft input area, so the user
1572 * sees the input method window and can interact with it.
1573 * This can only be called from the currently active input method,
1574 * as validated by the given token.
1575 *
1576 * @param token Supplies the identifying token given to an input method
1577 * when it was started, which allows it to perform this operation on
1578 * itself.
1579 * @param flags Provides additional operating flags. Currently may be
1580 * 0 or have the {@link #SHOW_IMPLICIT} or
1581 * {@link #SHOW_FORCED} bit set.
1582 */
1583 public void showSoftInputFromInputMethod(IBinder token, int flags) {
1584 try {
1585 mService.showMySoftInput(token, flags);
1586 } catch (RemoteException e) {
1587 throw new RuntimeException(e);
1588 }
1589 }
Jeff Brownc28867a2013-03-26 15:42:39 -07001590
The Android Open Source Project4df24232009-03-05 14:34:35 -08001591 /**
Jeff Brownf9e989d2013-04-04 23:04:03 -07001592 * Dispatches an input event to the IME.
1593 *
1594 * Returns {@link #DISPATCH_HANDLED} if the event was handled.
1595 * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
1596 * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
1597 * callback will be invoked later.
1598 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599 * @hide
1600 */
Jeff Brownf9e989d2013-04-04 23:04:03 -07001601 public int dispatchInputEvent(InputEvent event, Object token,
1602 FinishedInputEventCallback callback, Handler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603 synchronized (mH) {
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001604 if (mCurMethod != null) {
Jeff Brownc28867a2013-03-26 15:42:39 -07001605 if (event instanceof KeyEvent) {
1606 KeyEvent keyEvent = (KeyEvent)event;
1607 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
1608 && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
1609 && keyEvent.getRepeatCount() == 0) {
1610 showInputMethodPickerLocked();
Jeff Brownf9e989d2013-04-04 23:04:03 -07001611 return DISPATCH_HANDLED;
Jeff Brownc28867a2013-03-26 15:42:39 -07001612 }
Jeff Brown29c0ed22013-01-14 13:50:37 -08001613 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614
Jeff Brownc28867a2013-03-26 15:42:39 -07001615 if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
Jeff Brownf9e989d2013-04-04 23:04:03 -07001616
1617 PendingEvent p = obtainPendingEventLocked(
1618 event, token, mCurId, callback, handler);
1619 if (mMainLooper.isCurrentThread()) {
1620 // Already running on the IMM thread so we can send the event immediately.
1621 return sendInputEventOnMainLooperLocked(p);
Victoria Leaseb38070c2012-08-24 13:46:02 -07001622 }
Jeff Brownf9e989d2013-04-04 23:04:03 -07001623
1624 // Post the event to the IMM thread.
1625 Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
1626 msg.setAsynchronous(true);
1627 mH.sendMessage(msg);
1628 return DISPATCH_IN_PROGRESS;
Victoria Leaseb38070c2012-08-24 13:46:02 -07001629 }
1630 }
Jeff Brownf9e989d2013-04-04 23:04:03 -07001631 return DISPATCH_NOT_HANDLED;
Victoria Leaseb38070c2012-08-24 13:46:02 -07001632 }
1633
Jeff Brownf9e989d2013-04-04 23:04:03 -07001634 // Must be called on the main looper
1635 void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
1636 final boolean handled;
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001637 synchronized (mH) {
Jeff Brownf9e989d2013-04-04 23:04:03 -07001638 int result = sendInputEventOnMainLooperLocked(p);
1639 if (result == DISPATCH_IN_PROGRESS) {
1640 return;
1641 }
1642
1643 handled = (result == DISPATCH_HANDLED);
1644 }
1645
1646 invokeFinishedInputEventCallback(p, handled);
1647 }
1648
1649 // Must be called on the main looper
1650 int sendInputEventOnMainLooperLocked(PendingEvent p) {
1651 if (mCurChannel != null) {
1652 if (mCurSender == null) {
1653 mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
1654 }
1655
1656 final InputEvent event = p.mEvent;
1657 final int seq = event.getSequenceNumber();
1658 if (mCurSender.sendInputEvent(seq, event)) {
1659 mPendingEvents.put(seq, p);
1660 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
1661 mPendingEvents.size());
1662
1663 Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
1664 msg.setAsynchronous(true);
1665 mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
1666 return DISPATCH_IN_PROGRESS;
1667 }
1668
1669 Log.w(TAG, "Unable to send input event to IME: "
1670 + mCurId + " dropping: " + event);
1671 }
1672 return DISPATCH_NOT_HANDLED;
1673 }
1674
1675 void finishedInputEvent(int seq, boolean handled, boolean timeout) {
1676 final PendingEvent p;
1677 synchronized (mH) {
1678 int index = mPendingEvents.indexOfKey(seq);
1679 if (index < 0) {
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001680 return; // spurious, event already finished or timed out
1681 }
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001682
Jeff Brownf9e989d2013-04-04 23:04:03 -07001683 p = mPendingEvents.valueAt(index);
1684 mPendingEvents.removeAt(index);
1685 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001686
Jeff Brownf9e989d2013-04-04 23:04:03 -07001687 if (timeout) {
1688 Log.w(TAG, "Timeout waiting for IME to handle input event after "
1689 + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
1690 } else {
1691 mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
Jeff Brown4d656882013-04-03 14:39:19 -07001692 }
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001693 }
Jeff Brownf9e989d2013-04-04 23:04:03 -07001694
1695 invokeFinishedInputEventCallback(p, handled);
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001696 }
1697
Jeff Brownf9e989d2013-04-04 23:04:03 -07001698 // Assumes the event has already been removed from the queue.
1699 void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
1700 p.mHandled = handled;
1701 if (p.mHandler.getLooper().isCurrentThread()) {
1702 // Already running on the callback handler thread so we can send the
1703 // callback immediately.
1704 p.run();
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001705 } else {
Jeff Brownf9e989d2013-04-04 23:04:03 -07001706 // Post the event to the callback handler thread.
1707 // In this case, the callback will be responsible for recycling the event.
1708 Message msg = Message.obtain(p.mHandler, p);
1709 msg.setAsynchronous(true);
1710 msg.sendToTarget();
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001711 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 }
satokab751aa2010-09-14 19:17:36 +09001713
Michael Wrightef17e872013-04-01 13:15:55 -07001714 private void flushPendingEventsLocked() {
Jeff Brownf9e989d2013-04-04 23:04:03 -07001715 mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
Jeff Brown4d656882013-04-03 14:39:19 -07001716
Jeff Brownf9e989d2013-04-04 23:04:03 -07001717 final int count = mPendingEvents.size();
1718 for (int i = 0; i < count; i++) {
1719 int seq = mPendingEvents.keyAt(i);
1720 Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0);
Michael Wrightef17e872013-04-01 13:15:55 -07001721 msg.setAsynchronous(true);
Jeff Brownf9e989d2013-04-04 23:04:03 -07001722 msg.sendToTarget();
Michael Wrightef17e872013-04-01 13:15:55 -07001723 }
1724 }
1725
Jeff Brownf9e989d2013-04-04 23:04:03 -07001726 private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
1727 String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
1728 PendingEvent p = mPendingEventPool.acquire();
1729 if (p == null) {
1730 p = new PendingEvent();
1731 }
1732 p.mEvent = event;
1733 p.mToken = token;
1734 p.mInputMethodId = inputMethodId;
1735 p.mCallback = callback;
1736 p.mHandler = handler;
1737 return p;
1738 }
1739
1740 private void recyclePendingEventLocked(PendingEvent p) {
1741 p.recycle();
1742 mPendingEventPool.release(p);
1743 }
1744
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 public void showInputMethodPicker() {
1746 synchronized (mH) {
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001747 showInputMethodPickerLocked();
1748 }
1749 }
1750
1751 private void showInputMethodPickerLocked() {
1752 try {
1753 mService.showInputMethodPickerFromClient(mClient);
1754 } catch (RemoteException e) {
1755 Log.w(TAG, "IME died: " + mCurId, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001756 }
1757 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001758
satokd4fce2b2011-04-11 12:07:13 +09001759 /**
1760 * Show the settings for enabling subtypes of the specified input method.
1761 * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
1762 * subtypes of all input methods will be shown.
1763 */
1764 public void showInputMethodAndSubtypeEnabler(String imiId) {
satok47a44912010-10-06 16:03:58 +09001765 synchronized (mH) {
1766 try {
satokd4fce2b2011-04-11 12:07:13 +09001767 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
satok47a44912010-10-06 16:03:58 +09001768 } catch (RemoteException e) {
1769 Log.w(TAG, "IME died: " + mCurId, e);
1770 }
1771 }
1772 }
1773
satokd4fce2b2011-04-11 12:07:13 +09001774 /**
1775 * Returns the current input method subtype. This subtype is one of the subtypes in
1776 * the current input method. This method returns null when the current input method doesn't
1777 * have any input method subtype.
1778 */
satok04d50202010-10-25 22:20:12 +09001779 public InputMethodSubtype getCurrentInputMethodSubtype() {
1780 synchronized (mH) {
1781 try {
1782 return mService.getCurrentInputMethodSubtype();
1783 } catch (RemoteException e) {
1784 Log.w(TAG, "IME died: " + mCurId, e);
1785 return null;
1786 }
1787 }
1788 }
1789
satokd4fce2b2011-04-11 12:07:13 +09001790 /**
1791 * Switch to a new input method subtype of the current input method.
1792 * @param subtype A new input method subtype to switch.
1793 * @return true if the current subtype was successfully switched. When the specified subtype is
1794 * null, this method returns false.
1795 */
satokb66d2872010-11-10 01:04:04 +09001796 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
1797 synchronized (mH) {
1798 try {
1799 return mService.setCurrentInputMethodSubtype(subtype);
1800 } catch (RemoteException e) {
1801 Log.w(TAG, "IME died: " + mCurId, e);
1802 return false;
1803 }
1804 }
1805 }
1806
satokd4fce2b2011-04-11 12:07:13 +09001807 /**
Satoshi Kataokad7443c82013-10-15 17:45:43 +09001808 * Notify the current IME commits text
1809 * @hide
1810 */
1811 public void notifyTextCommitted() {
1812 synchronized (mH) {
1813 try {
1814 mService.notifyTextCommitted();
1815 } catch (RemoteException e) {
1816 Log.w(TAG, "IME died: " + mCurId, e);
1817 }
1818 }
1819 }
1820
1821 /**
satokd4fce2b2011-04-11 12:07:13 +09001822 * Returns a map of all shortcut input method info and their subtypes.
1823 */
satokf3db1af2010-11-23 13:34:33 +09001824 public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
satok4e4569d2010-11-19 18:45:53 +09001825 synchronized (mH) {
satokf3db1af2010-11-23 13:34:33 +09001826 HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
1827 new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
satok4e4569d2010-11-19 18:45:53 +09001828 try {
1829 // TODO: We should change the return type from List<Object> to List<Parcelable>
1830 List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
satokf3db1af2010-11-23 13:34:33 +09001831 // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
1832 ArrayList<InputMethodSubtype> subtypes = null;
1833 final int N = info.size();
1834 if (info != null && N > 0) {
1835 for (int i = 0; i < N; ++i) {
1836 Object o = info.get(i);
1837 if (o instanceof InputMethodInfo) {
1838 if (ret.containsKey(o)) {
1839 Log.e(TAG, "IMI list already contains the same InputMethod.");
1840 break;
satok4e4569d2010-11-19 18:45:53 +09001841 }
satokf3db1af2010-11-23 13:34:33 +09001842 subtypes = new ArrayList<InputMethodSubtype>();
1843 ret.put((InputMethodInfo)o, subtypes);
1844 } else if (subtypes != null && o instanceof InputMethodSubtype) {
1845 subtypes.add((InputMethodSubtype)o);
satok4e4569d2010-11-19 18:45:53 +09001846 }
satok4e4569d2010-11-19 18:45:53 +09001847 }
1848 }
1849 } catch (RemoteException e) {
1850 Log.w(TAG, "IME died: " + mCurId, e);
1851 }
1852 return ret;
1853 }
1854 }
satokf3db1af2010-11-23 13:34:33 +09001855
satokd4fce2b2011-04-11 12:07:13 +09001856 /**
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09001857 * @return The current height of the input method window.
1858 * @hide
1859 */
1860 public int getInputMethodWindowVisibleHeight() {
1861 synchronized (mH) {
1862 try {
1863 return mService.getInputMethodWindowVisibleHeight();
1864 } catch (RemoteException e) {
1865 Log.w(TAG, "IME died: " + mCurId, e);
1866 return 0;
1867 }
1868 }
1869 }
1870
1871 /**
satokd4fce2b2011-04-11 12:07:13 +09001872 * Force switch to the last used input method and subtype. If the last input method didn't have
1873 * any subtypes, the framework will simply switch to the last input method with no subtype
1874 * specified.
1875 * @param imeToken Supplies the identifying token given to an input method when it was started,
1876 * which allows it to perform this operation on itself.
1877 * @return true if the current input method and subtype was successfully switched to the last
1878 * used input method and subtype.
1879 */
satok735cf382010-11-11 20:40:09 +09001880 public boolean switchToLastInputMethod(IBinder imeToken) {
1881 synchronized (mH) {
1882 try {
1883 return mService.switchToLastInputMethod(imeToken);
1884 } catch (RemoteException e) {
1885 Log.w(TAG, "IME died: " + mCurId, e);
1886 return false;
1887 }
1888 }
1889 }
1890
satoke7c6998e2011-06-03 17:57:59 +09001891 /**
satok688bd472012-02-09 20:09:17 +09001892 * Force switch to the next input method and subtype. If there is no IME enabled except
1893 * current IME and subtype, do nothing.
1894 * @param imeToken Supplies the identifying token given to an input method when it was started,
1895 * which allows it to perform this operation on itself.
1896 * @param onlyCurrentIme if true, the framework will find the next subtype which
1897 * belongs to the current IME
1898 * @return true if the current input method and subtype was successfully switched to the next
1899 * input method and subtype.
1900 */
1901 public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
1902 synchronized (mH) {
1903 try {
1904 return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
1905 } catch (RemoteException e) {
1906 Log.w(TAG, "IME died: " + mCurId, e);
1907 return false;
1908 }
1909 }
1910 }
1911
1912 /**
satok15ab6b02013-08-26 14:17:18 +09001913 * Returns true if the current IME needs to offer the users ways to switch to a next input
1914 * method (e.g. a globe key.).
1915 * When an IME sets supportsSwitchingToNextInputMethod and this method returns true,
1916 * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly.
1917 * <p> Note that the system determines the most appropriate next input method
1918 * and subtype in order to provide the consistent user experience in switching
1919 * between IMEs and subtypes.
Satoshi Kataoka2b10b522013-08-21 20:39:12 +09001920 * @param imeToken Supplies the identifying token given to an input method when it was started,
1921 * which allows it to perform this operation on itself.
1922 */
1923 public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
1924 synchronized (mH) {
1925 try {
1926 return mService.shouldOfferSwitchingToNextInputMethod(imeToken);
1927 } catch (RemoteException e) {
1928 Log.w(TAG, "IME died: " + mCurId, e);
1929 return false;
1930 }
1931 }
1932 }
1933
1934 /**
satok91e88122011-07-18 11:11:42 +09001935 * Set additional input method subtypes. Only a process which shares the same uid with the IME
1936 * can add additional input method subtypes to the IME.
satok75917b62011-08-31 23:27:39 +09001937 * Please note that a subtype's status is stored in the system.
1938 * For example, enabled subtypes are remembered by the framework even after they are removed
1939 * by using this method. If you re-add the same subtypes again,
1940 * they will just get enabled. If you want to avoid such conflicts, for instance, you may
1941 * want to create a "different" new subtype even with the same locale and mode,
1942 * by changing its extra value. The different subtype won't get affected by the stored past
1943 * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer
1944 * to the current implementation.)
satok91e88122011-07-18 11:11:42 +09001945 * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
satoke7c6998e2011-06-03 17:57:59 +09001946 * @param subtypes subtypes will be added as additional subtypes of the current input method.
satoke7c6998e2011-06-03 17:57:59 +09001947 */
satokee5e77c2011-09-02 18:50:15 +09001948 public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
satoke7c6998e2011-06-03 17:57:59 +09001949 synchronized (mH) {
1950 try {
satokee5e77c2011-09-02 18:50:15 +09001951 mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
satoke7c6998e2011-06-03 17:57:59 +09001952 } catch (RemoteException e) {
1953 Log.w(TAG, "IME died: " + mCurId, e);
satoke7c6998e2011-06-03 17:57:59 +09001954 }
1955 }
1956 }
1957
satok68f1b782011-04-11 14:26:04 +09001958 public InputMethodSubtype getLastInputMethodSubtype() {
1959 synchronized (mH) {
1960 try {
1961 return mService.getLastInputMethodSubtype();
1962 } catch (RemoteException e) {
1963 Log.w(TAG, "IME died: " + mCurId, e);
1964 return null;
1965 }
1966 }
1967 }
1968
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1970 final Printer p = new PrintWriterPrinter(fout);
1971 p.println("Input method client state for " + this + ":");
1972
1973 p.println(" mService=" + mService);
1974 p.println(" mMainLooper=" + mMainLooper);
1975 p.println(" mIInputContext=" + mIInputContext);
1976 p.println(" mActive=" + mActive
1977 + " mHasBeenInactive=" + mHasBeenInactive
1978 + " mBindSequence=" + mBindSequence
1979 + " mCurId=" + mCurId);
1980 p.println(" mCurMethod=" + mCurMethod);
1981 p.println(" mCurRootView=" + mCurRootView);
1982 p.println(" mServedView=" + mServedView);
Dianne Hackborn7663d802012-02-24 13:08:49 -08001983 p.println(" mNextServedView=" + mNextServedView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001984 p.println(" mServedConnecting=" + mServedConnecting);
1985 if (mCurrentTextBoxAttribute != null) {
1986 p.println(" mCurrentTextBoxAttribute:");
1987 mCurrentTextBoxAttribute.dump(p, " ");
1988 } else {
1989 p.println(" mCurrentTextBoxAttribute: null");
1990 }
1991 p.println(" mServedInputConnection=" + mServedInputConnection);
1992 p.println(" mCompletions=" + mCompletions);
1993 p.println(" mCursorRect=" + mCursorRect);
1994 p.println(" mCursorSelStart=" + mCursorSelStart
1995 + " mCursorSelEnd=" + mCursorSelEnd
1996 + " mCursorCandStart=" + mCursorCandStart
1997 + " mCursorCandEnd=" + mCursorCandEnd);
1998 }
Jeff Brown04ddf3c2012-06-14 03:57:49 -07001999
2000 /**
2001 * Callback that is invoked when an input event that was dispatched to
2002 * the IME has been finished.
2003 * @hide
2004 */
Jeff Brownf9e989d2013-04-04 23:04:03 -07002005 public interface FinishedInputEventCallback {
2006 public void onFinishedInputEvent(Object token, boolean handled);
Jeff Brown04ddf3c2012-06-14 03:57:49 -07002007 }
2008
Jeff Brownc28867a2013-03-26 15:42:39 -07002009 private final class ImeInputEventSender extends InputEventSender {
2010 public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
2011 super(inputChannel, looper);
2012 }
2013
2014 @Override
2015 public void onInputEventFinished(int seq, boolean handled) {
Jeff Brownf9e989d2013-04-04 23:04:03 -07002016 finishedInputEvent(seq, handled, false);
Jeff Brownc28867a2013-03-26 15:42:39 -07002017 }
2018 }
2019
Jeff Brownf9e989d2013-04-04 23:04:03 -07002020 private final class PendingEvent implements Runnable {
2021 public InputEvent mEvent;
2022 public Object mToken;
Jeff Brown04ddf3c2012-06-14 03:57:49 -07002023 public String mInputMethodId;
Jeff Brownf9e989d2013-04-04 23:04:03 -07002024 public FinishedInputEventCallback mCallback;
2025 public Handler mHandler;
2026 public boolean mHandled;
2027
2028 public void recycle() {
2029 mEvent = null;
2030 mToken = null;
2031 mInputMethodId = null;
2032 mCallback = null;
2033 mHandler = null;
2034 mHandled = false;
2035 }
2036
2037 @Override
2038 public void run() {
2039 mCallback.onFinishedInputEvent(mToken, mHandled);
2040
2041 synchronized (mH) {
2042 recyclePendingEventLocked(this);
2043 }
2044 }
Jeff Brown04ddf3c2012-06-14 03:57:49 -07002045 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002046}