blob: 27cbaf702457b471f2ece9237f18d9548c7db135 [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
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080019import com.android.internal.os.HandlerCaller;
20import com.android.internal.view.IInputConnectionWrapper;
21import com.android.internal.view.IInputContext;
22import com.android.internal.view.IInputMethodCallback;
23import com.android.internal.view.IInputMethodClient;
24import com.android.internal.view.IInputMethodManager;
25import com.android.internal.view.IInputMethodSession;
26import com.android.internal.view.InputBindResult;
27
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.Context;
29import android.graphics.Rect;
30import android.os.Bundle;
31import android.os.Handler;
32import android.os.IBinder;
33import android.os.Looper;
34import android.os.Message;
35import android.os.RemoteException;
The Android Open Source Project4df24232009-03-05 14:34:35 -080036import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.os.ServiceManager;
38import android.util.Log;
39import android.util.PrintWriterPrinter;
40import android.util.Printer;
41import android.view.KeyEvent;
42import android.view.MotionEvent;
43import android.view.View;
Joe Onoratoc6cc0f82011-04-12 11:53:13 -070044import android.view.ViewAncestor;
Gilles Debunned4723bb2010-09-02 15:27:32 -070045
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import java.io.FileDescriptor;
47import java.io.PrintWriter;
satok4e4569d2010-11-19 18:45:53 +090048import java.util.ArrayList;
satokf3db1af2010-11-23 13:34:33 +090049import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import java.util.List;
satokf3db1af2010-11-23 13:34:33 +090051import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import java.util.concurrent.CountDownLatch;
53import java.util.concurrent.TimeUnit;
54
55/**
56 * Central system API to the overall input method framework (IMF) architecture,
57 * which arbitrates interaction between applications and the current input method.
58 * You can retrieve an instance of this interface with
59 * {@link Context#getSystemService(String) Context.getSystemService()}.
60 *
61 * <p>Topics covered here:
62 * <ol>
63 * <li><a href="#ArchitectureOverview">Architecture Overview</a>
64 * </ol>
65 *
66 * <a name="ArchitectureOverview"></a>
67 * <h3>Architecture Overview</h3>
68 *
69 * <p>There are three primary parties involved in the input method
70 * framework (IMF) architecture:</p>
71 *
72 * <ul>
73 * <li> The <strong>input method manager</strong> as expressed by this class
74 * is the central point of the system that manages interaction between all
75 * other parts. It is expressed as the client-side API here which exists
76 * in each application context and communicates with a global system service
77 * that manages the interaction across all processes.
78 * <li> An <strong>input method (IME)</strong> implements a particular
79 * interaction model allowing the user to generate text. The system binds
80 * to the current input method that is use, causing it to be created and run,
81 * and tells it when to hide and show its UI. Only one IME is running at a time.
82 * <li> Multiple <strong>client applications</strong> arbitrate with the input
83 * method manager for input focus and control over the state of the IME. Only
84 * one such client is ever active (working with the IME) at a time.
85 * </ul>
86 *
87 *
88 * <a name="Applications"></a>
89 * <h3>Applications</h3>
90 *
91 * <p>In most cases, applications that are using the standard
92 * {@link android.widget.TextView} or its subclasses will have little they need
93 * to do to work well with soft input methods. The main things you need to
94 * be aware of are:</p>
95 *
96 * <ul>
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080097 * <li> Properly set the {@link android.R.attr#inputType} in your editable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 * text views, so that the input method will have enough context to help the
99 * user in entering text into them.
100 * <li> Deal well with losing screen space when the input method is
101 * displayed. Ideally an application should handle its window being resized
102 * smaller, but it can rely on the system performing panning of the window
103 * if needed. You should set the {@link android.R.attr#windowSoftInputMode}
104 * attribute on your activity or the corresponding values on windows you
105 * create to help the system determine whether to pan or resize (it will
106 * try to determine this automatically but may get it wrong).
107 * <li> You can also control the preferred soft input state (open, closed, etc)
108 * for your window using the same {@link android.R.attr#windowSoftInputMode}
109 * attribute.
110 * </ul>
111 *
112 * <p>More finer-grained control is available through the APIs here to directly
113 * interact with the IMF and its IME -- either showing or hiding the input
114 * area, letting the user pick an input method, etc.</p>
115 *
116 * <p>For the rare people amongst us writing their own text editors, you
117 * will need to implement {@link android.view.View#onCreateInputConnection}
118 * to return a new instance of your own {@link InputConnection} interface
119 * allowing the IME to interact with your editor.</p>
120 *
121 *
122 * <a name="InputMethods"></a>
123 * <h3>Input Methods</h3>
124 *
125 * <p>An input method (IME) is implemented
126 * as a {@link android.app.Service}, typically deriving from
127 * {@link android.inputmethodservice.InputMethodService}. It must provide
128 * the core {@link InputMethod} interface, though this is normally handled by
129 * {@link android.inputmethodservice.InputMethodService} and implementors will
130 * only need to deal with the higher-level API there.</p>
131 *
132 * See the {@link android.inputmethodservice.InputMethodService} class for
133 * more information on implementing IMEs.
134 *
135 *
136 * <a name="Security"></a>
137 * <h3>Security</h3>
138 *
139 * <p>There are a lot of security issues associated with input methods,
140 * since they essentially have freedom to completely drive the UI and monitor
141 * everything the user enters. The Android input method framework also allows
142 * arbitrary third party IMEs, so care must be taken to restrict their
143 * selection and interactions.</p>
144 *
145 * <p>Here are some key points about the security architecture behind the
146 * IMF:</p>
147 *
148 * <ul>
149 * <li> <p>Only the system is allowed to directly access an IME's
150 * {@link InputMethod} interface, via the
151 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission. This is
152 * enforced in the system by not binding to an input method service that does
153 * not require this permission, so the system can guarantee no other untrusted
154 * clients are accessing the current input method outside of its control.</p>
155 *
156 * <li> <p>There may be many client processes of the IMF, but only one may
157 * be active at a time. The inactive clients can not interact with key
158 * parts of the IMF through the mechanisms described below.</p>
159 *
160 * <li> <p>Clients of an input method are only given access to its
161 * {@link InputMethodSession} interface. One instance of this interface is
162 * created for each client, and only calls from the session associated with
163 * the active client will be processed by the current IME. This is enforced
164 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
165 * IMEs, but must be explicitly handled by an IME that is customizing the
166 * raw {@link InputMethodSession} implementation.</p>
167 *
168 * <li> <p>Only the active client's {@link InputConnection} will accept
169 * operations. The IMF tells each client process whether it is active, and
170 * the framework enforces that in inactive processes calls on to the current
171 * InputConnection will be ignored. This ensures that the current IME can
172 * only deliver events and text edits to the UI that the user sees as
173 * being in focus.</p>
174 *
175 * <li> <p>An IME can never interact with an {@link InputConnection} while
176 * the screen is off. This is enforced by making all clients inactive while
177 * the screen is off, and prevents bad IMEs from driving the UI when the user
178 * can not be aware of its behavior.</p>
179 *
180 * <li> <p>A client application can ask that the system let the user pick a
181 * new IME, but can not programmatically switch to one itself. This avoids
182 * malicious applications from switching the user to their own IME, which
183 * remains running when the user navigates away to another application. An
184 * IME, on the other hand, <em>is</em> allowed to programmatically switch
185 * the system to another IME, since it already has full control of user
186 * input.</p>
187 *
188 * <li> <p>The user must explicitly enable a new IME in settings before
189 * they can switch to it, to confirm with the system that they know about it
190 * and want to make it available for use.</p>
191 * </ul>
192 */
193public final class InputMethodManager {
Dianne Hackborn4eba2712009-03-24 19:20:06 -0700194 static final boolean DEBUG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 static final String TAG = "InputMethodManager";
196
197 static final Object mInstanceSync = new Object();
198 static InputMethodManager mInstance;
199
200 final IInputMethodManager mService;
201 final Looper mMainLooper;
202
203 // For scheduling work on the main thread. This also serves as our
204 // global lock.
205 final H mH;
206
207 // Our generic input connection if the current target does not have its own.
208 final IInputContext mIInputContext;
209
210 /**
211 * True if this input method client is active, initially false.
212 */
213 boolean mActive = false;
214
215 /**
216 * Set whenever this client becomes inactive, to know we need to reset
217 * state with the IME then next time we receive focus.
218 */
219 boolean mHasBeenInactive = true;
220
221 /**
222 * As reported by IME through InputConnection.
223 */
224 boolean mFullscreenMode;
225
226 // -----------------------------------------------------------
227
228 /**
229 * This is the root view of the overall window that currently has input
230 * method focus.
231 */
232 View mCurRootView;
233 /**
234 * This is the view that should currently be served by an input method,
235 * regardless of the state of setting that up.
236 */
237 View mServedView;
238 /**
239 * This is then next view that will be served by the input method, when
240 * we get around to updating things.
241 */
242 View mNextServedView;
243 /**
244 * True if we should restart input in the next served view, even if the
245 * view hasn't actually changed from the current serve view.
246 */
247 boolean mNextServedNeedsStart;
248 /**
249 * This is set when we are in the process of connecting, to determine
250 * when we have actually finished.
251 */
252 boolean mServedConnecting;
253 /**
254 * This is non-null when we have connected the served view; it holds
255 * the attributes that were last retrieved from the served view and given
256 * to the input connection.
257 */
258 EditorInfo mCurrentTextBoxAttribute;
259 /**
260 * The InputConnection that was last retrieved from the served view.
261 */
262 InputConnection mServedInputConnection;
263 /**
264 * The completions that were last provided by the served view.
265 */
266 CompletionInfo[] mCompletions;
267
268 // Cursor position on the screen.
269 Rect mTmpCursorRect = new Rect();
270 Rect mCursorRect = new Rect();
271 int mCursorSelStart;
272 int mCursorSelEnd;
273 int mCursorCandStart;
274 int mCursorCandEnd;
275
276 // -----------------------------------------------------------
277
278 /**
279 * Sequence number of this binding, as returned by the server.
280 */
281 int mBindSequence = -1;
282 /**
283 * ID of the method we are bound to.
284 */
285 String mCurId;
286 /**
287 * The actual instance of the method to make calls on it.
288 */
289 IInputMethodSession mCurMethod;
290
291 // -----------------------------------------------------------
292
293 static final int MSG_DUMP = 1;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800294 static final int MSG_BIND = 2;
295 static final int MSG_UNBIND = 3;
296 static final int MSG_SET_ACTIVE = 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297
298 class H extends Handler {
299 H(Looper looper) {
300 super(looper);
301 }
302
303 @Override
304 public void handleMessage(Message msg) {
305 switch (msg.what) {
306 case MSG_DUMP: {
307 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
308 try {
309 doDump((FileDescriptor)args.arg1,
310 (PrintWriter)args.arg2, (String[])args.arg3);
311 } catch (RuntimeException e) {
312 ((PrintWriter)args.arg2).println("Exception: " + e);
313 }
314 synchronized (args.arg4) {
315 ((CountDownLatch)args.arg4).countDown();
316 }
317 return;
318 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800319 case MSG_BIND: {
320 final InputBindResult res = (InputBindResult)msg.obj;
321 synchronized (mH) {
322 if (mBindSequence < 0 || mBindSequence != res.sequence) {
323 Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
324 + ", given seq=" + res.sequence);
325 return;
326 }
327
328 mCurMethod = res.method;
329 mCurId = res.id;
330 mBindSequence = res.sequence;
331 }
332 startInputInner();
333 return;
334 }
335 case MSG_UNBIND: {
336 final int sequence = msg.arg1;
337 synchronized (mH) {
338 if (mBindSequence == sequence) {
339 if (false) {
340 // XXX the server has already unbound!
341 if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
342 try {
343 mCurMethod.finishInput();
344 } catch (RemoteException e) {
345 Log.w(TAG, "IME died: " + mCurId, e);
346 }
347 }
348 }
349 clearBindingLocked();
350
351 // If we were actively using the last input method, then
352 // we would like to re-connect to the next input method.
353 if (mServedView != null && mServedView.isFocused()) {
354 mServedConnecting = true;
355 }
356 }
357 startInputInner();
358 }
359 return;
360 }
361 case MSG_SET_ACTIVE: {
362 final boolean active = msg.arg1 != 0;
363 synchronized (mH) {
364 mActive = active;
365 mFullscreenMode = false;
366 if (!active) {
367 // Some other client has starting using the IME, so note
368 // that this happened and make sure our own editor's
369 // state is reset.
370 mHasBeenInactive = true;
371 try {
372 // Note that finishComposingText() is allowed to run
373 // even when we are not active.
374 mIInputContext.finishComposingText();
375 } catch (RemoteException e) {
376 }
377 }
378 }
379 return;
380 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 }
382 }
383 }
384
385 class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
386 public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
387 super(mainLooper, conn);
388 }
389
Gilles Debunne8cbb4c62011-01-24 12:33:56 -0800390 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 public boolean isActive() {
392 return mActive;
393 }
394 }
395
396 final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
397 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
398 // No need to check for dump permission, since we only give this
399 // interface to the system.
400
401 CountDownLatch latch = new CountDownLatch(1);
402 HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs();
403 sargs.arg1 = fd;
404 sargs.arg2 = fout;
405 sargs.arg3 = args;
406 sargs.arg4 = latch;
407 mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
408 try {
409 if (!latch.await(5, TimeUnit.SECONDS)) {
410 fout.println("Timeout waiting for dump");
411 }
412 } catch (InterruptedException e) {
413 fout.println("Interrupted waiting for dump");
414 }
415 }
416
417 public void setUsingInputMethod(boolean state) {
418 }
419
420 public void onBindMethod(InputBindResult res) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800421 mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 }
423
424 public void onUnbindMethod(int sequence) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800425 mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 }
427
428 public void setActive(boolean active) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800429 mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 }
431 };
432
Dianne Hackborn51bf0772009-03-24 19:11:41 -0700433 final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434
435 InputMethodManager(IInputMethodManager service, Looper looper) {
436 mService = service;
437 mMainLooper = looper;
438 mH = new H(looper);
439 mIInputContext = new ControlledInputConnectionWrapper(looper,
440 mDummyInputConnection);
441
442 if (mInstance == null) {
443 mInstance = this;
444 }
445 }
446
447 /**
448 * Retrieve the global InputMethodManager instance, creating it if it
449 * doesn't already exist.
450 * @hide
451 */
452 static public InputMethodManager getInstance(Context context) {
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700453 return getInstance(context.getMainLooper());
454 }
455
456 /**
457 * Internally, the input method manager can't be context-dependent, so
458 * we have this here for the places that need it.
459 * @hide
460 */
461 static public InputMethodManager getInstance(Looper mainLooper) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 synchronized (mInstanceSync) {
463 if (mInstance != null) {
464 return mInstance;
465 }
466 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
467 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700468 mInstance = new InputMethodManager(service, mainLooper);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 }
470 return mInstance;
471 }
472
473 /**
474 * Private optimization: retrieve the global InputMethodManager instance,
475 * if it exists.
476 * @hide
477 */
478 static public InputMethodManager peekInstance() {
479 return mInstance;
480 }
481
482 /** @hide */
483 public IInputMethodClient getClient() {
484 return mClient;
485 }
486
487 /** @hide */
488 public IInputContext getInputContext() {
489 return mIInputContext;
490 }
491
492 public List<InputMethodInfo> getInputMethodList() {
493 try {
494 return mService.getInputMethodList();
495 } catch (RemoteException e) {
496 throw new RuntimeException(e);
497 }
498 }
499
500 public List<InputMethodInfo> getEnabledInputMethodList() {
501 try {
502 return mService.getEnabledInputMethodList();
503 } catch (RemoteException e) {
504 throw new RuntimeException(e);
505 }
506 }
507
satokd4fce2b2011-04-11 12:07:13 +0900508 /**
509 * Returns a list of enabled input method subtypes for the specified input method info.
510 * @param imi An input method info whose subtypes list will be returned.
511 * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly
512 * selected subtypes. If an input method info doesn't have enabled subtypes, the framework
513 * will implicitly enable subtypes according to the current system language.
514 */
satok16331c82010-12-20 23:48:46 +0900515 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
516 boolean allowsImplicitlySelectedSubtypes) {
satok67ddf9c2010-11-17 09:45:54 +0900517 try {
satok16331c82010-12-20 23:48:46 +0900518 return mService.getEnabledInputMethodSubtypeList(imi, allowsImplicitlySelectedSubtypes);
satok67ddf9c2010-11-17 09:45:54 +0900519 } catch (RemoteException e) {
520 throw new RuntimeException(e);
521 }
522 }
523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
525 try {
526 mService.updateStatusIcon(imeToken, packageName, iconId);
527 } catch (RemoteException e) {
528 throw new RuntimeException(e);
529 }
530 }
531
532 public void hideStatusIcon(IBinder imeToken) {
533 try {
534 mService.updateStatusIcon(imeToken, null, 0);
535 } catch (RemoteException e) {
536 throw new RuntimeException(e);
537 }
538 }
539
540 /** @hide */
Joe Onorato857fd9b2011-01-27 15:08:35 -0800541 public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
satok06487a52010-10-29 11:37:18 +0900542 try {
Joe Onorato857fd9b2011-01-27 15:08:35 -0800543 mService.setImeWindowStatus(imeToken, vis, backDisposition);
satok06487a52010-10-29 11:37:18 +0900544 } catch (RemoteException e) {
545 throw new RuntimeException(e);
546 }
547 }
548
549 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 public void setFullscreenMode(boolean fullScreen) {
551 mFullscreenMode = fullScreen;
552 }
553
554 /**
555 * Allows you to discover whether the attached input method is running
556 * in fullscreen mode. Return true if it is fullscreen, entirely covering
557 * your UI, else returns false.
558 */
559 public boolean isFullscreenMode() {
560 return mFullscreenMode;
561 }
562
563 /**
564 * Return true if the given view is the currently active view for the
565 * input method.
566 */
567 public boolean isActive(View view) {
568 checkFocus();
569 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700570 return (mServedView == view
571 || (mServedView != null
572 && mServedView.checkInputConnectionProxy(view)))
573 && mCurrentTextBoxAttribute != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 }
575 }
576
577 /**
578 * Return true if any view is currently active in the input method.
579 */
580 public boolean isActive() {
581 checkFocus();
582 synchronized (mH) {
583 return mServedView != null && mCurrentTextBoxAttribute != null;
584 }
585 }
586
587 /**
588 * Return true if the currently served view is accepting full text edits.
589 * If false, it has no input connection, so can only handle raw key events.
590 */
591 public boolean isAcceptingText() {
592 checkFocus();
593 return mServedInputConnection != null;
594 }
595
596 /**
597 * Reset all of the state associated with being bound to an input method.
598 */
599 void clearBindingLocked() {
600 clearConnectionLocked();
601 mBindSequence = -1;
602 mCurId = null;
603 mCurMethod = null;
604 }
605
606 /**
607 * Reset all of the state associated with a served view being connected
608 * to an input method
609 */
610 void clearConnectionLocked() {
611 mCurrentTextBoxAttribute = null;
612 mServedInputConnection = null;
613 }
614
615 /**
616 * Disconnect any existing input connection, clearing the served view.
617 */
618 void finishInputLocked() {
619 mNextServedView = null;
620 if (mServedView != null) {
621 if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
622
623 if (mCurrentTextBoxAttribute != null) {
624 try {
625 mService.finishInput(mClient);
626 } catch (RemoteException e) {
627 }
628 }
629
630 if (mServedInputConnection != null) {
631 // We need to tell the previously served view that it is no
632 // longer the input target, so it can reset its state. Schedule
633 // this call on its window's Handler so it will be on the correct
634 // thread and outside of our lock.
635 Handler vh = mServedView.getHandler();
636 if (vh != null) {
637 // This will result in a call to reportFinishInputConnection()
638 // below.
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700639 vh.sendMessage(vh.obtainMessage(ViewAncestor.FINISH_INPUT_CONNECTION,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 mServedInputConnection));
641 }
642 }
643
644 mServedView = null;
645 mCompletions = null;
646 mServedConnecting = false;
647 clearConnectionLocked();
648 }
649 }
650
651 /**
652 * Called from the FINISH_INPUT_CONNECTION message above.
653 * @hide
654 */
655 public void reportFinishInputConnection(InputConnection ic) {
656 if (mServedInputConnection != ic) {
657 ic.finishComposingText();
658 }
659 }
660
661 public void displayCompletions(View view, CompletionInfo[] completions) {
662 checkFocus();
663 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700664 if (mServedView != view && (mServedView == null
665 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 return;
667 }
668
669 mCompletions = completions;
670 if (mCurMethod != null) {
671 try {
672 mCurMethod.displayCompletions(mCompletions);
673 } catch (RemoteException e) {
674 }
675 }
676 }
677 }
678
679 public void updateExtractedText(View view, int token, ExtractedText text) {
680 checkFocus();
681 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700682 if (mServedView != view && (mServedView == null
683 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 return;
685 }
686
687 if (mCurMethod != null) {
688 try {
689 mCurMethod.updateExtractedText(token, text);
690 } catch (RemoteException e) {
691 }
692 }
693 }
694 }
695
696 /**
697 * Flag for {@link #showSoftInput} to indicate that this is an implicit
698 * request to show the input window, not as the result of a direct request
699 * by the user. The window may not be shown in this case.
700 */
701 public static final int SHOW_IMPLICIT = 0x0001;
702
703 /**
704 * Flag for {@link #showSoftInput} to indicate that the user has forced
705 * the input method open (such as by long-pressing menu) so it should
706 * not be closed until they explicitly do so.
707 */
708 public static final int SHOW_FORCED = 0x0002;
709
710 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -0800711 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
712 * a result receiver: explicitly request that the current input method's
713 * soft input area be shown to the user, if needed.
714 *
715 * @param view The currently focused view, which would like to receive
716 * soft keyboard input.
717 * @param flags Provides additional operating flags. Currently may be
718 * 0 or have the {@link #SHOW_IMPLICIT} bit set.
719 */
720 public boolean showSoftInput(View view, int flags) {
721 return showSoftInput(view, flags, null);
722 }
723
724 /**
725 * Flag for the {@link ResultReceiver} result code from
726 * {@link #showSoftInput(View, int, ResultReceiver)} and
727 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
728 * state of the soft input window was unchanged and remains shown.
729 */
730 public static final int RESULT_UNCHANGED_SHOWN = 0;
731
732 /**
733 * Flag for the {@link ResultReceiver} result code from
734 * {@link #showSoftInput(View, int, ResultReceiver)} and
735 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
736 * state of the soft input window was unchanged and remains hidden.
737 */
738 public static final int RESULT_UNCHANGED_HIDDEN = 1;
739
740 /**
741 * Flag for the {@link ResultReceiver} result code from
742 * {@link #showSoftInput(View, int, ResultReceiver)} and
743 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
744 * state of the soft input window changed from hidden to shown.
745 */
746 public static final int RESULT_SHOWN = 2;
747
748 /**
749 * Flag for the {@link ResultReceiver} result code from
750 * {@link #showSoftInput(View, int, ResultReceiver)} and
751 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
752 * state of the soft input window changed from shown to hidden.
753 */
754 public static final int RESULT_HIDDEN = 3;
755
756 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 * Explicitly request that the current input method's soft input area be
758 * shown to the user, if needed. Call this if the user interacts with
759 * your view in such a way that they have expressed they would like to
760 * start performing input into it.
761 *
762 * @param view The currently focused view, which would like to receive
763 * soft keyboard input.
764 * @param flags Provides additional operating flags. Currently may be
765 * 0 or have the {@link #SHOW_IMPLICIT} bit set.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800766 * @param resultReceiver If non-null, this will be called by the IME when
767 * it has processed your request to tell you what it has done. The result
768 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
769 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
770 * {@link #RESULT_HIDDEN}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 */
Gilles Debunnead8484b2011-02-17 17:37:51 -0800772 public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 checkFocus();
774 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700775 if (mServedView != view && (mServedView == null
776 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800777 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 }
779
780 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800781 return mService.showSoftInput(mClient, flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 } catch (RemoteException e) {
783 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800784
785 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 }
787 }
788
789 /** @hide */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800790 public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800792 mService.showSoftInput(mClient, flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 } catch (RemoteException e) {
794 }
795 }
796
797 /**
798 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
799 * input window should only be hidden if it was not explicitly shown
800 * by the user.
801 */
802 public static final int HIDE_IMPLICIT_ONLY = 0x0001;
803
804 /**
805 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
806 * input window should normally be hidden, unless it was originally
807 * shown with {@link #SHOW_FORCED}.
808 */
809 public static final int HIDE_NOT_ALWAYS = 0x0002;
810
811 /**
Gilles Debunne7c8c6d62011-01-24 14:48:14 -0800812 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}
The Android Open Source Project4df24232009-03-05 14:34:35 -0800813 * without a result: request to hide the soft input window from the
814 * context of the window that is currently accepting input.
815 *
816 * @param windowToken The token of the window that is making the request,
817 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
818 * @param flags Provides additional operating flags. Currently may be
819 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
820 */
821 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
822 return hideSoftInputFromWindow(windowToken, flags, null);
823 }
824
825 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 * Request to hide the soft input window from the context of the window
827 * that is currently accepting input. This should be called as a result
828 * of the user doing some actually than fairly explicitly requests to
829 * have the input window hidden.
830 *
831 * @param windowToken The token of the window that is making the request,
832 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
833 * @param flags Provides additional operating flags. Currently may be
834 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800835 * @param resultReceiver If non-null, this will be called by the IME when
836 * it has processed your request to tell you what it has done. The result
837 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
838 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
839 * {@link #RESULT_HIDDEN}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800841 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
842 ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 checkFocus();
844 synchronized (mH) {
845 if (mServedView == null || mServedView.getWindowToken() != windowToken) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800846 return false;
847 }
848
849 try {
850 return mService.hideSoftInput(mClient, flags, resultReceiver);
851 } catch (RemoteException e) {
852 }
853 return false;
854 }
855 }
856
857
858 /**
859 * This method toggles the input method window display.
860 * If the input window is already displayed, it gets hidden.
861 * If not the input window will be displayed.
862 * @param windowToken The token of the window that is making the request,
863 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
864 * @param showFlags Provides additional operating flags. May be
865 * 0 or have the {@link #SHOW_IMPLICIT},
866 * {@link #SHOW_FORCED} bit set.
867 * @param hideFlags Provides additional operating flags. May be
868 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
869 * {@link #HIDE_NOT_ALWAYS} bit set.
870 **/
871 public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
872 synchronized (mH) {
873 if (mServedView == null || mServedView.getWindowToken() != windowToken) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 return;
875 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800876 if (mCurMethod != null) {
877 try {
878 mCurMethod.toggleSoftInput(showFlags, hideFlags);
879 } catch (RemoteException e) {
880 }
881 }
882 }
883 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884
The Android Open Source Project4df24232009-03-05 14:34:35 -0800885 /*
886 * This method toggles the input method window display.
887 * If the input window is already displayed, it gets hidden.
888 * If not the input window will be displayed.
889 * @param showFlags Provides additional operating flags. May be
890 * 0 or have the {@link #SHOW_IMPLICIT},
891 * {@link #SHOW_FORCED} bit set.
892 * @param hideFlags Provides additional operating flags. May be
893 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
894 * {@link #HIDE_NOT_ALWAYS} bit set.
895 * @hide
896 */
897 public void toggleSoftInput(int showFlags, int hideFlags) {
898 if (mCurMethod != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800900 mCurMethod.toggleSoftInput(showFlags, hideFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 } catch (RemoteException e) {
902 }
903 }
904 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800905
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 /**
907 * If the input method is currently connected to the given view,
908 * restart it with its new contents. You should call this when the text
909 * within your view changes outside of the normal input method or key
910 * input flow, such as when an application calls TextView.setText().
911 *
912 * @param view The view whose text has changed.
913 */
914 public void restartInput(View view) {
915 checkFocus();
916 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700917 if (mServedView != view && (mServedView == null
918 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 return;
920 }
921
922 mServedConnecting = true;
923 }
924
925 startInputInner();
926 }
927
928 void startInputInner() {
929 final View view;
930 synchronized (mH) {
931 view = mServedView;
932
933 // Make sure we have a window token for the served view.
934 if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
935 if (view == null) {
936 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
937 return;
938 }
939 }
940
941 // Now we need to get an input connection from the served view.
942 // This is complicated in a couple ways: we can't be holding our lock
943 // when calling out to the view, and we need to make sure we call into
944 // the view on the same thread that is driving its view hierarchy.
945 Handler vh = view.getHandler();
946 if (vh == null) {
947 // If the view doesn't have a handler, something has changed out
948 // from under us, so just bail.
949 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
950 return;
951 }
952 if (vh.getLooper() != Looper.myLooper()) {
953 // The view is running on a different thread than our own, so
954 // we need to reschedule our work for over there.
955 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
956 vh.post(new Runnable() {
957 public void run() {
958 startInputInner();
959 }
960 });
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700961 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 }
963
964 // Okay we are now ready to call into the served view and have it
965 // do its stuff.
966 // Life is good: let's hook everything up!
967 EditorInfo tba = new EditorInfo();
968 tba.packageName = view.getContext().getPackageName();
969 tba.fieldId = view.getId();
970 InputConnection ic = view.onCreateInputConnection(tba);
971 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
972
973 synchronized (mH) {
974 // Now that we are locked again, validate that our state hasn't
975 // changed.
976 if (mServedView != view || !mServedConnecting) {
977 // Something else happened, so abort.
978 if (DEBUG) Log.v(TAG,
979 "Starting input: finished by someone else (view="
980 + mServedView + " conn=" + mServedConnecting + ")");
981 return;
982 }
983
984 // If we already have a text box, then this view is already
985 // connected so we want to restart it.
986 final boolean initial = mCurrentTextBoxAttribute == null;
987
988 // Hook 'em up and let 'er rip.
989 mCurrentTextBoxAttribute = tba;
990 mServedConnecting = false;
991 mServedInputConnection = ic;
992 IInputContext servedContext;
993 if (ic != null) {
994 mCursorSelStart = tba.initialSelStart;
995 mCursorSelEnd = tba.initialSelEnd;
996 mCursorCandStart = -1;
997 mCursorCandEnd = -1;
998 mCursorRect.setEmpty();
999 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
1000 } else {
1001 servedContext = null;
1002 }
1003
1004 try {
1005 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
1006 + ic + " tba=" + tba + " initial=" + initial);
1007 InputBindResult res = mService.startInput(mClient,
1008 servedContext, tba, initial, mCurMethod == null);
1009 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
1010 if (res != null) {
1011 if (res.id != null) {
1012 mBindSequence = res.sequence;
1013 mCurMethod = res.method;
1014 } else {
1015 // This means there is no input method available.
1016 if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1017 return;
1018 }
1019 }
1020 if (mCurMethod != null && mCompletions != null) {
1021 try {
1022 mCurMethod.displayCompletions(mCompletions);
1023 } catch (RemoteException e) {
1024 }
1025 }
1026 } catch (RemoteException e) {
1027 Log.w(TAG, "IME died: " + mCurId, e);
1028 }
1029 }
1030 }
1031
1032 /**
1033 * When the focused window is dismissed, this method is called to finish the
1034 * input method started before.
1035 * @hide
1036 */
1037 public void windowDismissed(IBinder appWindowToken) {
1038 checkFocus();
1039 synchronized (mH) {
1040 if (mServedView != null &&
1041 mServedView.getWindowToken() == appWindowToken) {
1042 finishInputLocked();
1043 }
1044 }
1045 }
1046
1047 /**
1048 * Call this when a view receives focus.
1049 * @hide
1050 */
1051 public void focusIn(View view) {
1052 synchronized (mH) {
1053 focusInLocked(view);
1054 }
1055 }
1056
1057 void focusInLocked(View view) {
1058 if (DEBUG) Log.v(TAG, "focusIn: " + view);
1059
1060 if (mCurRootView != view.getRootView()) {
1061 // This is a request from a window that isn't in the window with
1062 // IME focus, so ignore it.
1063 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
1064 return;
1065 }
1066
1067 mNextServedView = view;
1068 scheduleCheckFocusLocked(view);
1069 }
1070
1071 /**
1072 * Call this when a view loses focus.
1073 * @hide
1074 */
1075 public void focusOut(View view) {
1076 synchronized (mH) {
1077 if (DEBUG) Log.v(TAG, "focusOut: " + view
1078 + " mServedView=" + mServedView
1079 + " winFocus=" + view.hasWindowFocus());
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001080 if (mServedView != view) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 // The following code would auto-hide the IME if we end up
1082 // with no more views with focus. This can happen, however,
1083 // whenever we go into touch mode, so it ends up hiding
1084 // at times when we don't really want it to. For now it
1085 // seems better to just turn it all off.
1086 if (false && view.hasWindowFocus()) {
1087 mNextServedView = null;
1088 scheduleCheckFocusLocked(view);
1089 }
1090 }
1091 }
1092 }
1093
1094 void scheduleCheckFocusLocked(View view) {
1095 Handler vh = view.getHandler();
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07001096 if (vh != null && !vh.hasMessages(ViewAncestor.CHECK_FOCUS)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 // This will result in a call to checkFocus() below.
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07001098 vh.sendMessage(vh.obtainMessage(ViewAncestor.CHECK_FOCUS));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 }
1100 }
1101
1102 /**
1103 * @hide
1104 */
1105 public void checkFocus() {
1106 // This is called a lot, so short-circuit before locking.
1107 if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1108 return;
1109 }
1110
1111 InputConnection ic = null;
1112 synchronized (mH) {
1113 if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1114 return;
1115 }
1116 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1117 + " next=" + mNextServedView
1118 + " restart=" + mNextServedNeedsStart);
1119
1120 mNextServedNeedsStart = false;
1121 if (mNextServedView == null) {
1122 finishInputLocked();
1123 // In this case, we used to have a focused view on the window,
1124 // but no longer do. We should make sure the input method is
1125 // no longer shown, since it serves no purpose.
1126 closeCurrentInput();
1127 return;
1128 }
1129
1130 ic = mServedInputConnection;
1131
1132 mServedView = mNextServedView;
1133 mCurrentTextBoxAttribute = null;
1134 mCompletions = null;
1135 mServedConnecting = true;
1136 }
1137
1138 if (ic != null) {
1139 ic.finishComposingText();
1140 }
1141
1142 startInputInner();
1143 }
1144
1145 void closeCurrentInput() {
1146 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001147 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 } catch (RemoteException e) {
1149 }
1150 }
1151
1152 /**
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07001153 * Called by ViewAncestor when its window gets input focus.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001154 * @hide
1155 */
1156 public void onWindowFocus(View rootView, View focusedView, int softInputMode,
1157 boolean first, int windowFlags) {
1158 synchronized (mH) {
1159 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1160 + " softInputMode=" + softInputMode
1161 + " first=" + first + " flags=#"
1162 + Integer.toHexString(windowFlags));
1163 if (mHasBeenInactive) {
1164 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh");
1165 mHasBeenInactive = false;
1166 mNextServedNeedsStart = true;
1167 }
1168 focusInLocked(focusedView != null ? focusedView : rootView);
1169 }
1170
1171 checkFocus();
1172
1173 synchronized (mH) {
1174 try {
1175 final boolean isTextEditor = focusedView != null &&
1176 focusedView.onCheckIsTextEditor();
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001177 mService.windowGainedFocus(mClient, rootView.getWindowToken(),
1178 focusedView != null, isTextEditor, softInputMode, first,
1179 windowFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 } catch (RemoteException e) {
1181 }
1182 }
1183 }
1184
1185 /** @hide */
1186 public void startGettingWindowFocus(View rootView) {
1187 synchronized (mH) {
1188 mCurRootView = rootView;
1189 }
1190 }
1191
1192 /**
1193 * Report the current selection range.
1194 */
1195 public void updateSelection(View view, int selStart, int selEnd,
1196 int candidatesStart, int candidatesEnd) {
1197 checkFocus();
1198 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001199 if ((mServedView != view && (mServedView == null
1200 || !mServedView.checkInputConnectionProxy(view)))
1201 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202 return;
1203 }
1204
1205 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1206 || mCursorCandStart != candidatesStart
1207 || mCursorCandEnd != candidatesEnd) {
1208 if (DEBUG) Log.d(TAG, "updateSelection");
1209
1210 try {
1211 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
1212 mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
1213 selStart, selEnd, candidatesStart, candidatesEnd);
1214 mCursorSelStart = selStart;
1215 mCursorSelEnd = selEnd;
1216 mCursorCandStart = candidatesStart;
1217 mCursorCandEnd = candidatesEnd;
1218 } catch (RemoteException e) {
1219 Log.w(TAG, "IME died: " + mCurId, e);
1220 }
1221 }
1222 }
1223 }
1224
1225 /**
1226 * Returns true if the current input method wants to watch the location
1227 * of the input editor's cursor in its window.
1228 */
1229 public boolean isWatchingCursor(View view) {
1230 return false;
1231 }
1232
1233 /**
1234 * Report the current cursor location in its window.
1235 */
1236 public void updateCursor(View view, int left, int top, int right, int bottom) {
1237 checkFocus();
1238 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001239 if ((mServedView != view && (mServedView == null
1240 || !mServedView.checkInputConnectionProxy(view)))
1241 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 return;
1243 }
1244
1245 mTmpCursorRect.set(left, top, right, bottom);
1246 if (!mCursorRect.equals(mTmpCursorRect)) {
1247 if (DEBUG) Log.d(TAG, "updateCursor");
1248
1249 try {
1250 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1251 mCurMethod.updateCursor(mTmpCursorRect);
1252 mCursorRect.set(mTmpCursorRect);
1253 } catch (RemoteException e) {
1254 Log.w(TAG, "IME died: " + mCurId, e);
1255 }
1256 }
1257 }
1258 }
1259
1260 /**
1261 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1262 * InputMethodSession.appPrivateCommand()} on the current Input Method.
1263 * @param view Optional View that is sending the command, or null if
1264 * you want to send the command regardless of the view that is attached
1265 * to the input method.
1266 * @param action Name of the command to be performed. This <em>must</em>
1267 * be a scoped name, i.e. prefixed with a package name you own, so that
1268 * different developers will not create conflicting commands.
1269 * @param data Any data to include with the command.
1270 */
1271 public void sendAppPrivateCommand(View view, String action, Bundle data) {
1272 checkFocus();
1273 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001274 if ((mServedView != view && (mServedView == null
1275 || !mServedView.checkInputConnectionProxy(view)))
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1277 return;
1278 }
1279 try {
1280 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1281 mCurMethod.appPrivateCommand(action, data);
1282 } catch (RemoteException e) {
1283 Log.w(TAG, "IME died: " + mCurId, e);
1284 }
1285 }
1286 }
satok28203512010-11-24 11:06:49 +09001287
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 /**
satok28203512010-11-24 11:06:49 +09001289 * Force switch to a new input method component. This can only be called
1290 * 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 -08001291 * @param token Supplies the identifying token given to an input method
1292 * when it was started, which allows it to perform this operation on
1293 * itself.
1294 * @param id The unique identifier for the new input method to be switched to.
1295 */
1296 public void setInputMethod(IBinder token, String id) {
1297 try {
1298 mService.setInputMethod(token, id);
1299 } catch (RemoteException e) {
1300 throw new RuntimeException(e);
1301 }
1302 }
satok28203512010-11-24 11:06:49 +09001303
1304 /**
1305 * Force switch to a new input method and subtype. This can only be called
1306 * from an application or a service which has a token of the currently active input method.
1307 * @param token Supplies the identifying token given to an input method
1308 * when it was started, which allows it to perform this operation on
1309 * itself.
1310 * @param id The unique identifier for the new input method to be switched to.
1311 * @param subtype The new subtype of the new input method to be switched to.
1312 */
1313 public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
1314 try {
1315 mService.setInputMethodAndSubtype(token, id, subtype);
1316 } catch (RemoteException e) {
1317 throw new RuntimeException(e);
1318 }
1319 }
1320
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 /**
1322 * Close/hide the input method's soft input area, so the user no longer
1323 * sees it or can interact with it. This can only be called
1324 * from the currently active input method, as validated by the given token.
1325 *
1326 * @param token Supplies the identifying token given to an input method
1327 * when it was started, which allows it to perform this operation on
1328 * itself.
1329 * @param flags Provides additional operating flags. Currently may be
The Android Open Source Project4df24232009-03-05 14:34:35 -08001330 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1331 * {@link #HIDE_NOT_ALWAYS} bit set.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 */
1333 public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1334 try {
1335 mService.hideMySoftInput(token, flags);
1336 } catch (RemoteException e) {
1337 throw new RuntimeException(e);
1338 }
1339 }
1340
1341 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -08001342 * Show the input method's soft input area, so the user
1343 * sees the input method window and can interact with it.
1344 * This can only be called from the currently active input method,
1345 * as validated by the given token.
1346 *
1347 * @param token Supplies the identifying token given to an input method
1348 * when it was started, which allows it to perform this operation on
1349 * itself.
1350 * @param flags Provides additional operating flags. Currently may be
1351 * 0 or have the {@link #SHOW_IMPLICIT} or
1352 * {@link #SHOW_FORCED} bit set.
1353 */
1354 public void showSoftInputFromInputMethod(IBinder token, int flags) {
1355 try {
1356 mService.showMySoftInput(token, flags);
1357 } catch (RemoteException e) {
1358 throw new RuntimeException(e);
1359 }
1360 }
1361
1362 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 * @hide
1364 */
1365 public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
1366 IInputMethodCallback callback) {
1367 synchronized (mH) {
1368 if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
1369
1370 if (mCurMethod == null) {
1371 try {
1372 callback.finishedEvent(seq, false);
1373 } catch (RemoteException e) {
1374 }
1375 return;
1376 }
1377
1378 if (key.getAction() == KeyEvent.ACTION_DOWN
1379 && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
1380 showInputMethodPicker();
1381 try {
1382 callback.finishedEvent(seq, true);
1383 } catch (RemoteException e) {
1384 }
1385 return;
1386 }
1387 try {
1388 if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
1389 mCurMethod.dispatchKeyEvent(seq, key, callback);
1390 } catch (RemoteException e) {
1391 Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
1392 try {
1393 callback.finishedEvent(seq, false);
1394 } catch (RemoteException ex) {
1395 }
1396 }
1397 }
1398 }
1399
1400 /**
1401 * @hide
1402 */
1403 void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
1404 IInputMethodCallback callback) {
1405 synchronized (mH) {
1406 if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
1407
1408 if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
1409 try {
1410 callback.finishedEvent(seq, false);
1411 } catch (RemoteException e) {
1412 }
1413 return;
1414 }
1415
1416 try {
1417 if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
1418 mCurMethod.dispatchTrackballEvent(seq, motion, callback);
1419 } catch (RemoteException e) {
1420 Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
1421 try {
1422 callback.finishedEvent(seq, false);
1423 } catch (RemoteException ex) {
1424 }
1425 }
1426 }
1427 }
satokab751aa2010-09-14 19:17:36 +09001428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 public void showInputMethodPicker() {
1430 synchronized (mH) {
1431 try {
1432 mService.showInputMethodPickerFromClient(mClient);
1433 } catch (RemoteException e) {
1434 Log.w(TAG, "IME died: " + mCurId, e);
1435 }
1436 }
1437 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001438
satokd4fce2b2011-04-11 12:07:13 +09001439 /**
1440 * Show the settings for enabling subtypes of the specified input method.
1441 * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
1442 * subtypes of all input methods will be shown.
1443 */
1444 public void showInputMethodAndSubtypeEnabler(String imiId) {
satok47a44912010-10-06 16:03:58 +09001445 synchronized (mH) {
1446 try {
satokd4fce2b2011-04-11 12:07:13 +09001447 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
satok47a44912010-10-06 16:03:58 +09001448 } catch (RemoteException e) {
1449 Log.w(TAG, "IME died: " + mCurId, e);
1450 }
1451 }
1452 }
1453
satokd4fce2b2011-04-11 12:07:13 +09001454 /**
1455 * Returns the current input method subtype. This subtype is one of the subtypes in
1456 * the current input method. This method returns null when the current input method doesn't
1457 * have any input method subtype.
1458 */
satok04d50202010-10-25 22:20:12 +09001459 public InputMethodSubtype getCurrentInputMethodSubtype() {
1460 synchronized (mH) {
1461 try {
1462 return mService.getCurrentInputMethodSubtype();
1463 } catch (RemoteException e) {
1464 Log.w(TAG, "IME died: " + mCurId, e);
1465 return null;
1466 }
1467 }
1468 }
1469
satokd4fce2b2011-04-11 12:07:13 +09001470 /**
1471 * Switch to a new input method subtype of the current input method.
1472 * @param subtype A new input method subtype to switch.
1473 * @return true if the current subtype was successfully switched. When the specified subtype is
1474 * null, this method returns false.
1475 */
satokb66d2872010-11-10 01:04:04 +09001476 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
1477 synchronized (mH) {
1478 try {
1479 return mService.setCurrentInputMethodSubtype(subtype);
1480 } catch (RemoteException e) {
1481 Log.w(TAG, "IME died: " + mCurId, e);
1482 return false;
1483 }
1484 }
1485 }
1486
satokd4fce2b2011-04-11 12:07:13 +09001487 /**
1488 * Returns a map of all shortcut input method info and their subtypes.
1489 */
satokf3db1af2010-11-23 13:34:33 +09001490 public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
satok4e4569d2010-11-19 18:45:53 +09001491 synchronized (mH) {
satokf3db1af2010-11-23 13:34:33 +09001492 HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
1493 new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
satok4e4569d2010-11-19 18:45:53 +09001494 try {
1495 // TODO: We should change the return type from List<Object> to List<Parcelable>
1496 List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
satokf3db1af2010-11-23 13:34:33 +09001497 // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
1498 ArrayList<InputMethodSubtype> subtypes = null;
1499 final int N = info.size();
1500 if (info != null && N > 0) {
1501 for (int i = 0; i < N; ++i) {
1502 Object o = info.get(i);
1503 if (o instanceof InputMethodInfo) {
1504 if (ret.containsKey(o)) {
1505 Log.e(TAG, "IMI list already contains the same InputMethod.");
1506 break;
satok4e4569d2010-11-19 18:45:53 +09001507 }
satokf3db1af2010-11-23 13:34:33 +09001508 subtypes = new ArrayList<InputMethodSubtype>();
1509 ret.put((InputMethodInfo)o, subtypes);
1510 } else if (subtypes != null && o instanceof InputMethodSubtype) {
1511 subtypes.add((InputMethodSubtype)o);
satok4e4569d2010-11-19 18:45:53 +09001512 }
satok4e4569d2010-11-19 18:45:53 +09001513 }
1514 }
1515 } catch (RemoteException e) {
1516 Log.w(TAG, "IME died: " + mCurId, e);
1517 }
1518 return ret;
1519 }
1520 }
satokf3db1af2010-11-23 13:34:33 +09001521
satokd4fce2b2011-04-11 12:07:13 +09001522 /**
1523 * Force switch to the last used input method and subtype. If the last input method didn't have
1524 * any subtypes, the framework will simply switch to the last input method with no subtype
1525 * specified.
1526 * @param imeToken Supplies the identifying token given to an input method when it was started,
1527 * which allows it to perform this operation on itself.
1528 * @return true if the current input method and subtype was successfully switched to the last
1529 * used input method and subtype.
1530 */
satok735cf382010-11-11 20:40:09 +09001531 public boolean switchToLastInputMethod(IBinder imeToken) {
1532 synchronized (mH) {
1533 try {
1534 return mService.switchToLastInputMethod(imeToken);
1535 } catch (RemoteException e) {
1536 Log.w(TAG, "IME died: " + mCurId, e);
1537 return false;
1538 }
1539 }
1540 }
1541
satok68f1b782011-04-11 14:26:04 +09001542 public InputMethodSubtype getLastInputMethodSubtype() {
1543 synchronized (mH) {
1544 try {
1545 return mService.getLastInputMethodSubtype();
1546 } catch (RemoteException e) {
1547 Log.w(TAG, "IME died: " + mCurId, e);
1548 return null;
1549 }
1550 }
1551 }
1552
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001553 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1554 final Printer p = new PrintWriterPrinter(fout);
1555 p.println("Input method client state for " + this + ":");
1556
1557 p.println(" mService=" + mService);
1558 p.println(" mMainLooper=" + mMainLooper);
1559 p.println(" mIInputContext=" + mIInputContext);
1560 p.println(" mActive=" + mActive
1561 + " mHasBeenInactive=" + mHasBeenInactive
1562 + " mBindSequence=" + mBindSequence
1563 + " mCurId=" + mCurId);
1564 p.println(" mCurMethod=" + mCurMethod);
1565 p.println(" mCurRootView=" + mCurRootView);
1566 p.println(" mServedView=" + mServedView);
1567 p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart
1568 + " mNextServedView=" + mNextServedView);
1569 p.println(" mServedConnecting=" + mServedConnecting);
1570 if (mCurrentTextBoxAttribute != null) {
1571 p.println(" mCurrentTextBoxAttribute:");
1572 mCurrentTextBoxAttribute.dump(p, " ");
1573 } else {
1574 p.println(" mCurrentTextBoxAttribute: null");
1575 }
1576 p.println(" mServedInputConnection=" + mServedInputConnection);
1577 p.println(" mCompletions=" + mCompletions);
1578 p.println(" mCursorRect=" + mCursorRect);
1579 p.println(" mCursorSelStart=" + mCursorSelStart
1580 + " mCursorSelEnd=" + mCursorSelEnd
1581 + " mCursorCandStart=" + mCursorCandStart
1582 + " mCursorCandEnd=" + mCursorCandEnd);
1583 }
1584}