blob: a39c7c70042d4ec83c62980a644097f7980feda7 [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;
44import android.view.ViewRoot;
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
satok16331c82010-12-20 23:48:46 +0900508 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
509 boolean allowsImplicitlySelectedSubtypes) {
satok67ddf9c2010-11-17 09:45:54 +0900510 try {
satok16331c82010-12-20 23:48:46 +0900511 return mService.getEnabledInputMethodSubtypeList(imi, allowsImplicitlySelectedSubtypes);
satok67ddf9c2010-11-17 09:45:54 +0900512 } catch (RemoteException e) {
513 throw new RuntimeException(e);
514 }
515 }
516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
518 try {
519 mService.updateStatusIcon(imeToken, packageName, iconId);
520 } catch (RemoteException e) {
521 throw new RuntimeException(e);
522 }
523 }
524
525 public void hideStatusIcon(IBinder imeToken) {
526 try {
527 mService.updateStatusIcon(imeToken, null, 0);
528 } catch (RemoteException e) {
529 throw new RuntimeException(e);
530 }
531 }
532
533 /** @hide */
Joe Onorato857fd9b2011-01-27 15:08:35 -0800534 public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
satok06487a52010-10-29 11:37:18 +0900535 try {
Joe Onorato857fd9b2011-01-27 15:08:35 -0800536 mService.setImeWindowStatus(imeToken, vis, backDisposition);
satok06487a52010-10-29 11:37:18 +0900537 } catch (RemoteException e) {
538 throw new RuntimeException(e);
539 }
540 }
541
542 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 public void setFullscreenMode(boolean fullScreen) {
544 mFullscreenMode = fullScreen;
545 }
546
547 /**
548 * Allows you to discover whether the attached input method is running
549 * in fullscreen mode. Return true if it is fullscreen, entirely covering
550 * your UI, else returns false.
551 */
552 public boolean isFullscreenMode() {
553 return mFullscreenMode;
554 }
555
556 /**
557 * Return true if the given view is the currently active view for the
558 * input method.
559 */
560 public boolean isActive(View view) {
561 checkFocus();
562 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700563 return (mServedView == view
564 || (mServedView != null
565 && mServedView.checkInputConnectionProxy(view)))
566 && mCurrentTextBoxAttribute != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 }
568 }
569
570 /**
571 * Return true if any view is currently active in the input method.
572 */
573 public boolean isActive() {
574 checkFocus();
575 synchronized (mH) {
576 return mServedView != null && mCurrentTextBoxAttribute != null;
577 }
578 }
579
580 /**
581 * Return true if the currently served view is accepting full text edits.
582 * If false, it has no input connection, so can only handle raw key events.
583 */
584 public boolean isAcceptingText() {
585 checkFocus();
586 return mServedInputConnection != null;
587 }
588
589 /**
590 * Reset all of the state associated with being bound to an input method.
591 */
592 void clearBindingLocked() {
593 clearConnectionLocked();
594 mBindSequence = -1;
595 mCurId = null;
596 mCurMethod = null;
597 }
598
599 /**
600 * Reset all of the state associated with a served view being connected
601 * to an input method
602 */
603 void clearConnectionLocked() {
604 mCurrentTextBoxAttribute = null;
605 mServedInputConnection = null;
606 }
607
608 /**
609 * Disconnect any existing input connection, clearing the served view.
610 */
611 void finishInputLocked() {
612 mNextServedView = null;
613 if (mServedView != null) {
614 if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
615
616 if (mCurrentTextBoxAttribute != null) {
617 try {
618 mService.finishInput(mClient);
619 } catch (RemoteException e) {
620 }
621 }
622
623 if (mServedInputConnection != null) {
624 // We need to tell the previously served view that it is no
625 // longer the input target, so it can reset its state. Schedule
626 // this call on its window's Handler so it will be on the correct
627 // thread and outside of our lock.
628 Handler vh = mServedView.getHandler();
629 if (vh != null) {
630 // This will result in a call to reportFinishInputConnection()
631 // below.
632 vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION,
633 mServedInputConnection));
634 }
635 }
636
637 mServedView = null;
638 mCompletions = null;
639 mServedConnecting = false;
640 clearConnectionLocked();
641 }
642 }
643
644 /**
645 * Called from the FINISH_INPUT_CONNECTION message above.
646 * @hide
647 */
648 public void reportFinishInputConnection(InputConnection ic) {
649 if (mServedInputConnection != ic) {
650 ic.finishComposingText();
651 }
652 }
653
654 public void displayCompletions(View view, CompletionInfo[] completions) {
655 checkFocus();
656 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700657 if (mServedView != view && (mServedView == null
658 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 return;
660 }
661
662 mCompletions = completions;
663 if (mCurMethod != null) {
664 try {
665 mCurMethod.displayCompletions(mCompletions);
666 } catch (RemoteException e) {
667 }
668 }
669 }
670 }
671
672 public void updateExtractedText(View view, int token, ExtractedText text) {
673 checkFocus();
674 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700675 if (mServedView != view && (mServedView == null
676 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 return;
678 }
679
680 if (mCurMethod != null) {
681 try {
682 mCurMethod.updateExtractedText(token, text);
683 } catch (RemoteException e) {
684 }
685 }
686 }
687 }
688
689 /**
690 * Flag for {@link #showSoftInput} to indicate that this is an implicit
691 * request to show the input window, not as the result of a direct request
692 * by the user. The window may not be shown in this case.
693 */
694 public static final int SHOW_IMPLICIT = 0x0001;
695
696 /**
697 * Flag for {@link #showSoftInput} to indicate that the user has forced
698 * the input method open (such as by long-pressing menu) so it should
699 * not be closed until they explicitly do so.
700 */
701 public static final int SHOW_FORCED = 0x0002;
702
703 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -0800704 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
705 * a result receiver: explicitly request that the current input method's
706 * soft input area be shown to the user, if needed.
707 *
708 * @param view The currently focused view, which would like to receive
709 * soft keyboard input.
710 * @param flags Provides additional operating flags. Currently may be
711 * 0 or have the {@link #SHOW_IMPLICIT} bit set.
712 */
713 public boolean showSoftInput(View view, int flags) {
714 return showSoftInput(view, flags, null);
715 }
716
717 /**
718 * Flag for the {@link ResultReceiver} result code from
719 * {@link #showSoftInput(View, int, ResultReceiver)} and
720 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
721 * state of the soft input window was unchanged and remains shown.
722 */
723 public static final int RESULT_UNCHANGED_SHOWN = 0;
724
725 /**
726 * Flag for the {@link ResultReceiver} result code from
727 * {@link #showSoftInput(View, int, ResultReceiver)} and
728 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
729 * state of the soft input window was unchanged and remains hidden.
730 */
731 public static final int RESULT_UNCHANGED_HIDDEN = 1;
732
733 /**
734 * Flag for the {@link ResultReceiver} result code from
735 * {@link #showSoftInput(View, int, ResultReceiver)} and
736 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
737 * state of the soft input window changed from hidden to shown.
738 */
739 public static final int RESULT_SHOWN = 2;
740
741 /**
742 * Flag for the {@link ResultReceiver} result code from
743 * {@link #showSoftInput(View, int, ResultReceiver)} and
744 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
745 * state of the soft input window changed from shown to hidden.
746 */
747 public static final int RESULT_HIDDEN = 3;
748
749 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 * Explicitly request that the current input method's soft input area be
751 * shown to the user, if needed. Call this if the user interacts with
752 * your view in such a way that they have expressed they would like to
753 * start performing input into it.
754 *
755 * @param view The currently focused view, which would like to receive
756 * soft keyboard input.
757 * @param flags Provides additional operating flags. Currently may be
758 * 0 or have the {@link #SHOW_IMPLICIT} bit set.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800759 * @param resultReceiver If non-null, this will be called by the IME when
760 * it has processed your request to tell you what it has done. The result
761 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
762 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
763 * {@link #RESULT_HIDDEN}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 */
Gilles Debunnead8484b2011-02-17 17:37:51 -0800765 public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 checkFocus();
767 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700768 if (mServedView != view && (mServedView == null
769 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800770 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
772
773 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800774 return mService.showSoftInput(mClient, flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 } catch (RemoteException e) {
776 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800777
778 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 }
780 }
781
782 /** @hide */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800783 public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800785 mService.showSoftInput(mClient, flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 } catch (RemoteException e) {
787 }
788 }
789
790 /**
791 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
792 * input window should only be hidden if it was not explicitly shown
793 * by the user.
794 */
795 public static final int HIDE_IMPLICIT_ONLY = 0x0001;
796
797 /**
798 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
799 * input window should normally be hidden, unless it was originally
800 * shown with {@link #SHOW_FORCED}.
801 */
802 public static final int HIDE_NOT_ALWAYS = 0x0002;
803
804 /**
Gilles Debunne7c8c6d62011-01-24 14:48:14 -0800805 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}
The Android Open Source Project4df24232009-03-05 14:34:35 -0800806 * without a result: request to hide the soft input window from the
807 * context of the window that is currently accepting input.
808 *
809 * @param windowToken The token of the window that is making the request,
810 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
811 * @param flags Provides additional operating flags. Currently may be
812 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
813 */
814 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
815 return hideSoftInputFromWindow(windowToken, flags, null);
816 }
817
818 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 * Request to hide the soft input window from the context of the window
820 * that is currently accepting input. This should be called as a result
821 * of the user doing some actually than fairly explicitly requests to
822 * have the input window hidden.
823 *
824 * @param windowToken The token of the window that is making the request,
825 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
826 * @param flags Provides additional operating flags. Currently may be
827 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800828 * @param resultReceiver If non-null, this will be called by the IME when
829 * it has processed your request to tell you what it has done. The result
830 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
831 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
832 * {@link #RESULT_HIDDEN}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800834 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
835 ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 checkFocus();
837 synchronized (mH) {
838 if (mServedView == null || mServedView.getWindowToken() != windowToken) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800839 return false;
840 }
841
842 try {
843 return mService.hideSoftInput(mClient, flags, resultReceiver);
844 } catch (RemoteException e) {
845 }
846 return false;
847 }
848 }
849
850
851 /**
852 * This method toggles the input method window display.
853 * If the input window is already displayed, it gets hidden.
854 * If not the input window will be displayed.
855 * @param windowToken The token of the window that is making the request,
856 * as returned by {@link View#getWindowToken() View.getWindowToken()}.
857 * @param showFlags Provides additional operating flags. May be
858 * 0 or have the {@link #SHOW_IMPLICIT},
859 * {@link #SHOW_FORCED} bit set.
860 * @param hideFlags Provides additional operating flags. May be
861 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
862 * {@link #HIDE_NOT_ALWAYS} bit set.
863 **/
864 public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
865 synchronized (mH) {
866 if (mServedView == null || mServedView.getWindowToken() != windowToken) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 return;
868 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800869 if (mCurMethod != null) {
870 try {
871 mCurMethod.toggleSoftInput(showFlags, hideFlags);
872 } catch (RemoteException e) {
873 }
874 }
875 }
876 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877
The Android Open Source Project4df24232009-03-05 14:34:35 -0800878 /*
879 * This method toggles the input method window display.
880 * If the input window is already displayed, it gets hidden.
881 * If not the input window will be displayed.
882 * @param showFlags Provides additional operating flags. May be
883 * 0 or have the {@link #SHOW_IMPLICIT},
884 * {@link #SHOW_FORCED} bit set.
885 * @param hideFlags Provides additional operating flags. May be
886 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
887 * {@link #HIDE_NOT_ALWAYS} bit set.
888 * @hide
889 */
890 public void toggleSoftInput(int showFlags, int hideFlags) {
891 if (mCurMethod != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800893 mCurMethod.toggleSoftInput(showFlags, hideFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 } catch (RemoteException e) {
895 }
896 }
897 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800898
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 /**
900 * If the input method is currently connected to the given view,
901 * restart it with its new contents. You should call this when the text
902 * within your view changes outside of the normal input method or key
903 * input flow, such as when an application calls TextView.setText().
904 *
905 * @param view The view whose text has changed.
906 */
907 public void restartInput(View view) {
908 checkFocus();
909 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700910 if (mServedView != view && (mServedView == null
911 || !mServedView.checkInputConnectionProxy(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800912 return;
913 }
914
915 mServedConnecting = true;
916 }
917
918 startInputInner();
919 }
920
921 void startInputInner() {
922 final View view;
923 synchronized (mH) {
924 view = mServedView;
925
926 // Make sure we have a window token for the served view.
927 if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
928 if (view == null) {
929 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
930 return;
931 }
932 }
933
934 // Now we need to get an input connection from the served view.
935 // This is complicated in a couple ways: we can't be holding our lock
936 // when calling out to the view, and we need to make sure we call into
937 // the view on the same thread that is driving its view hierarchy.
938 Handler vh = view.getHandler();
939 if (vh == null) {
940 // If the view doesn't have a handler, something has changed out
941 // from under us, so just bail.
942 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
943 return;
944 }
945 if (vh.getLooper() != Looper.myLooper()) {
946 // The view is running on a different thread than our own, so
947 // we need to reschedule our work for over there.
948 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
949 vh.post(new Runnable() {
950 public void run() {
951 startInputInner();
952 }
953 });
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700954 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 }
956
957 // Okay we are now ready to call into the served view and have it
958 // do its stuff.
959 // Life is good: let's hook everything up!
960 EditorInfo tba = new EditorInfo();
961 tba.packageName = view.getContext().getPackageName();
962 tba.fieldId = view.getId();
963 InputConnection ic = view.onCreateInputConnection(tba);
964 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
965
966 synchronized (mH) {
967 // Now that we are locked again, validate that our state hasn't
968 // changed.
969 if (mServedView != view || !mServedConnecting) {
970 // Something else happened, so abort.
971 if (DEBUG) Log.v(TAG,
972 "Starting input: finished by someone else (view="
973 + mServedView + " conn=" + mServedConnecting + ")");
974 return;
975 }
976
977 // If we already have a text box, then this view is already
978 // connected so we want to restart it.
979 final boolean initial = mCurrentTextBoxAttribute == null;
980
981 // Hook 'em up and let 'er rip.
982 mCurrentTextBoxAttribute = tba;
983 mServedConnecting = false;
984 mServedInputConnection = ic;
985 IInputContext servedContext;
986 if (ic != null) {
987 mCursorSelStart = tba.initialSelStart;
988 mCursorSelEnd = tba.initialSelEnd;
989 mCursorCandStart = -1;
990 mCursorCandEnd = -1;
991 mCursorRect.setEmpty();
992 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
993 } else {
994 servedContext = null;
995 }
996
997 try {
998 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
999 + ic + " tba=" + tba + " initial=" + initial);
1000 InputBindResult res = mService.startInput(mClient,
1001 servedContext, tba, initial, mCurMethod == null);
1002 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
1003 if (res != null) {
1004 if (res.id != null) {
1005 mBindSequence = res.sequence;
1006 mCurMethod = res.method;
1007 } else {
1008 // This means there is no input method available.
1009 if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1010 return;
1011 }
1012 }
1013 if (mCurMethod != null && mCompletions != null) {
1014 try {
1015 mCurMethod.displayCompletions(mCompletions);
1016 } catch (RemoteException e) {
1017 }
1018 }
1019 } catch (RemoteException e) {
1020 Log.w(TAG, "IME died: " + mCurId, e);
1021 }
1022 }
1023 }
1024
1025 /**
1026 * When the focused window is dismissed, this method is called to finish the
1027 * input method started before.
1028 * @hide
1029 */
1030 public void windowDismissed(IBinder appWindowToken) {
1031 checkFocus();
1032 synchronized (mH) {
1033 if (mServedView != null &&
1034 mServedView.getWindowToken() == appWindowToken) {
1035 finishInputLocked();
1036 }
1037 }
1038 }
1039
1040 /**
1041 * Call this when a view receives focus.
1042 * @hide
1043 */
1044 public void focusIn(View view) {
1045 synchronized (mH) {
1046 focusInLocked(view);
1047 }
1048 }
1049
1050 void focusInLocked(View view) {
1051 if (DEBUG) Log.v(TAG, "focusIn: " + view);
1052
1053 if (mCurRootView != view.getRootView()) {
1054 // This is a request from a window that isn't in the window with
1055 // IME focus, so ignore it.
1056 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
1057 return;
1058 }
1059
1060 mNextServedView = view;
1061 scheduleCheckFocusLocked(view);
1062 }
1063
1064 /**
1065 * Call this when a view loses focus.
1066 * @hide
1067 */
1068 public void focusOut(View view) {
1069 synchronized (mH) {
1070 if (DEBUG) Log.v(TAG, "focusOut: " + view
1071 + " mServedView=" + mServedView
1072 + " winFocus=" + view.hasWindowFocus());
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001073 if (mServedView != view) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 // The following code would auto-hide the IME if we end up
1075 // with no more views with focus. This can happen, however,
1076 // whenever we go into touch mode, so it ends up hiding
1077 // at times when we don't really want it to. For now it
1078 // seems better to just turn it all off.
1079 if (false && view.hasWindowFocus()) {
1080 mNextServedView = null;
1081 scheduleCheckFocusLocked(view);
1082 }
1083 }
1084 }
1085 }
1086
1087 void scheduleCheckFocusLocked(View view) {
1088 Handler vh = view.getHandler();
1089 if (vh != null && !vh.hasMessages(ViewRoot.CHECK_FOCUS)) {
1090 // This will result in a call to checkFocus() below.
1091 vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS));
1092 }
1093 }
1094
1095 /**
1096 * @hide
1097 */
1098 public void checkFocus() {
1099 // This is called a lot, so short-circuit before locking.
1100 if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1101 return;
1102 }
1103
1104 InputConnection ic = null;
1105 synchronized (mH) {
1106 if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1107 return;
1108 }
1109 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1110 + " next=" + mNextServedView
1111 + " restart=" + mNextServedNeedsStart);
1112
1113 mNextServedNeedsStart = false;
1114 if (mNextServedView == null) {
1115 finishInputLocked();
1116 // In this case, we used to have a focused view on the window,
1117 // but no longer do. We should make sure the input method is
1118 // no longer shown, since it serves no purpose.
1119 closeCurrentInput();
1120 return;
1121 }
1122
1123 ic = mServedInputConnection;
1124
1125 mServedView = mNextServedView;
1126 mCurrentTextBoxAttribute = null;
1127 mCompletions = null;
1128 mServedConnecting = true;
1129 }
1130
1131 if (ic != null) {
1132 ic.finishComposingText();
1133 }
1134
1135 startInputInner();
1136 }
1137
1138 void closeCurrentInput() {
1139 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001140 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 } catch (RemoteException e) {
1142 }
1143 }
1144
1145 /**
1146 * Called by ViewRoot when its window gets input focus.
1147 * @hide
1148 */
1149 public void onWindowFocus(View rootView, View focusedView, int softInputMode,
1150 boolean first, int windowFlags) {
1151 synchronized (mH) {
1152 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1153 + " softInputMode=" + softInputMode
1154 + " first=" + first + " flags=#"
1155 + Integer.toHexString(windowFlags));
1156 if (mHasBeenInactive) {
1157 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh");
1158 mHasBeenInactive = false;
1159 mNextServedNeedsStart = true;
1160 }
1161 focusInLocked(focusedView != null ? focusedView : rootView);
1162 }
1163
1164 checkFocus();
1165
1166 synchronized (mH) {
1167 try {
1168 final boolean isTextEditor = focusedView != null &&
1169 focusedView.onCheckIsTextEditor();
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001170 mService.windowGainedFocus(mClient, rootView.getWindowToken(),
1171 focusedView != null, isTextEditor, softInputMode, first,
1172 windowFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 } catch (RemoteException e) {
1174 }
1175 }
1176 }
1177
1178 /** @hide */
1179 public void startGettingWindowFocus(View rootView) {
1180 synchronized (mH) {
1181 mCurRootView = rootView;
1182 }
1183 }
1184
1185 /**
1186 * Report the current selection range.
1187 */
1188 public void updateSelection(View view, int selStart, int selEnd,
1189 int candidatesStart, int candidatesEnd) {
1190 checkFocus();
1191 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001192 if ((mServedView != view && (mServedView == null
1193 || !mServedView.checkInputConnectionProxy(view)))
1194 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 return;
1196 }
1197
1198 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1199 || mCursorCandStart != candidatesStart
1200 || mCursorCandEnd != candidatesEnd) {
1201 if (DEBUG) Log.d(TAG, "updateSelection");
1202
1203 try {
1204 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
1205 mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
1206 selStart, selEnd, candidatesStart, candidatesEnd);
1207 mCursorSelStart = selStart;
1208 mCursorSelEnd = selEnd;
1209 mCursorCandStart = candidatesStart;
1210 mCursorCandEnd = candidatesEnd;
1211 } catch (RemoteException e) {
1212 Log.w(TAG, "IME died: " + mCurId, e);
1213 }
1214 }
1215 }
1216 }
1217
1218 /**
1219 * Returns true if the current input method wants to watch the location
1220 * of the input editor's cursor in its window.
1221 */
1222 public boolean isWatchingCursor(View view) {
1223 return false;
1224 }
1225
1226 /**
1227 * Report the current cursor location in its window.
1228 */
1229 public void updateCursor(View view, int left, int top, int right, int bottom) {
1230 checkFocus();
1231 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001232 if ((mServedView != view && (mServedView == null
1233 || !mServedView.checkInputConnectionProxy(view)))
1234 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235 return;
1236 }
1237
1238 mTmpCursorRect.set(left, top, right, bottom);
1239 if (!mCursorRect.equals(mTmpCursorRect)) {
1240 if (DEBUG) Log.d(TAG, "updateCursor");
1241
1242 try {
1243 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1244 mCurMethod.updateCursor(mTmpCursorRect);
1245 mCursorRect.set(mTmpCursorRect);
1246 } catch (RemoteException e) {
1247 Log.w(TAG, "IME died: " + mCurId, e);
1248 }
1249 }
1250 }
1251 }
1252
1253 /**
1254 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1255 * InputMethodSession.appPrivateCommand()} on the current Input Method.
1256 * @param view Optional View that is sending the command, or null if
1257 * you want to send the command regardless of the view that is attached
1258 * to the input method.
1259 * @param action Name of the command to be performed. This <em>must</em>
1260 * be a scoped name, i.e. prefixed with a package name you own, so that
1261 * different developers will not create conflicting commands.
1262 * @param data Any data to include with the command.
1263 */
1264 public void sendAppPrivateCommand(View view, String action, Bundle data) {
1265 checkFocus();
1266 synchronized (mH) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001267 if ((mServedView != view && (mServedView == null
1268 || !mServedView.checkInputConnectionProxy(view)))
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1270 return;
1271 }
1272 try {
1273 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1274 mCurMethod.appPrivateCommand(action, data);
1275 } catch (RemoteException e) {
1276 Log.w(TAG, "IME died: " + mCurId, e);
1277 }
1278 }
1279 }
satok28203512010-11-24 11:06:49 +09001280
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281 /**
satok28203512010-11-24 11:06:49 +09001282 * Force switch to a new input method component. This can only be called
1283 * 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 -08001284 * @param token Supplies the identifying token given to an input method
1285 * when it was started, which allows it to perform this operation on
1286 * itself.
1287 * @param id The unique identifier for the new input method to be switched to.
1288 */
1289 public void setInputMethod(IBinder token, String id) {
1290 try {
1291 mService.setInputMethod(token, id);
1292 } catch (RemoteException e) {
1293 throw new RuntimeException(e);
1294 }
1295 }
satok28203512010-11-24 11:06:49 +09001296
1297 /**
1298 * Force switch to a new input method and subtype. This can only be called
1299 * from an application or a service which has a token of the currently active input method.
1300 * @param token Supplies the identifying token given to an input method
1301 * when it was started, which allows it to perform this operation on
1302 * itself.
1303 * @param id The unique identifier for the new input method to be switched to.
1304 * @param subtype The new subtype of the new input method to be switched to.
1305 */
1306 public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
1307 try {
1308 mService.setInputMethodAndSubtype(token, id, subtype);
1309 } catch (RemoteException e) {
1310 throw new RuntimeException(e);
1311 }
1312 }
1313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 /**
1315 * Close/hide the input method's soft input area, so the user no longer
1316 * sees it or can interact with it. This can only be called
1317 * from the currently active input method, as validated by the given token.
1318 *
1319 * @param token Supplies the identifying token given to an input method
1320 * when it was started, which allows it to perform this operation on
1321 * itself.
1322 * @param flags Provides additional operating flags. Currently may be
The Android Open Source Project4df24232009-03-05 14:34:35 -08001323 * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1324 * {@link #HIDE_NOT_ALWAYS} bit set.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 */
1326 public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1327 try {
1328 mService.hideMySoftInput(token, flags);
1329 } catch (RemoteException e) {
1330 throw new RuntimeException(e);
1331 }
1332 }
1333
1334 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -08001335 * Show the input method's soft input area, so the user
1336 * sees the input method window and can interact with it.
1337 * This can only be called from the currently active input method,
1338 * as validated by the given token.
1339 *
1340 * @param token Supplies the identifying token given to an input method
1341 * when it was started, which allows it to perform this operation on
1342 * itself.
1343 * @param flags Provides additional operating flags. Currently may be
1344 * 0 or have the {@link #SHOW_IMPLICIT} or
1345 * {@link #SHOW_FORCED} bit set.
1346 */
1347 public void showSoftInputFromInputMethod(IBinder token, int flags) {
1348 try {
1349 mService.showMySoftInput(token, flags);
1350 } catch (RemoteException e) {
1351 throw new RuntimeException(e);
1352 }
1353 }
1354
1355 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 * @hide
1357 */
1358 public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
1359 IInputMethodCallback callback) {
1360 synchronized (mH) {
1361 if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
1362
1363 if (mCurMethod == null) {
1364 try {
1365 callback.finishedEvent(seq, false);
1366 } catch (RemoteException e) {
1367 }
1368 return;
1369 }
1370
1371 if (key.getAction() == KeyEvent.ACTION_DOWN
1372 && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
1373 showInputMethodPicker();
1374 try {
1375 callback.finishedEvent(seq, true);
1376 } catch (RemoteException e) {
1377 }
1378 return;
1379 }
1380 try {
1381 if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
1382 mCurMethod.dispatchKeyEvent(seq, key, callback);
1383 } catch (RemoteException e) {
1384 Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
1385 try {
1386 callback.finishedEvent(seq, false);
1387 } catch (RemoteException ex) {
1388 }
1389 }
1390 }
1391 }
1392
1393 /**
1394 * @hide
1395 */
1396 void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
1397 IInputMethodCallback callback) {
1398 synchronized (mH) {
1399 if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
1400
1401 if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
1402 try {
1403 callback.finishedEvent(seq, false);
1404 } catch (RemoteException e) {
1405 }
1406 return;
1407 }
1408
1409 try {
1410 if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
1411 mCurMethod.dispatchTrackballEvent(seq, motion, callback);
1412 } catch (RemoteException e) {
1413 Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
1414 try {
1415 callback.finishedEvent(seq, false);
1416 } catch (RemoteException ex) {
1417 }
1418 }
1419 }
1420 }
satokab751aa2010-09-14 19:17:36 +09001421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001422 public void showInputMethodPicker() {
1423 synchronized (mH) {
1424 try {
1425 mService.showInputMethodPickerFromClient(mClient);
1426 } catch (RemoteException e) {
1427 Log.w(TAG, "IME died: " + mCurId, e);
1428 }
1429 }
1430 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001431
satok47a44912010-10-06 16:03:58 +09001432 public void showInputMethodAndSubtypeEnabler(String topId) {
1433 synchronized (mH) {
1434 try {
1435 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, topId);
1436 } catch (RemoteException e) {
1437 Log.w(TAG, "IME died: " + mCurId, e);
1438 }
1439 }
1440 }
1441
satok04d50202010-10-25 22:20:12 +09001442 public InputMethodSubtype getCurrentInputMethodSubtype() {
1443 synchronized (mH) {
1444 try {
1445 return mService.getCurrentInputMethodSubtype();
1446 } catch (RemoteException e) {
1447 Log.w(TAG, "IME died: " + mCurId, e);
1448 return null;
1449 }
1450 }
1451 }
1452
satokb66d2872010-11-10 01:04:04 +09001453 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
1454 synchronized (mH) {
1455 try {
1456 return mService.setCurrentInputMethodSubtype(subtype);
1457 } catch (RemoteException e) {
1458 Log.w(TAG, "IME died: " + mCurId, e);
1459 return false;
1460 }
1461 }
1462 }
1463
satokf3db1af2010-11-23 13:34:33 +09001464 public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
satok4e4569d2010-11-19 18:45:53 +09001465 synchronized (mH) {
satokf3db1af2010-11-23 13:34:33 +09001466 HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
1467 new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
satok4e4569d2010-11-19 18:45:53 +09001468 try {
1469 // TODO: We should change the return type from List<Object> to List<Parcelable>
1470 List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
satokf3db1af2010-11-23 13:34:33 +09001471 // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
1472 ArrayList<InputMethodSubtype> subtypes = null;
1473 final int N = info.size();
1474 if (info != null && N > 0) {
1475 for (int i = 0; i < N; ++i) {
1476 Object o = info.get(i);
1477 if (o instanceof InputMethodInfo) {
1478 if (ret.containsKey(o)) {
1479 Log.e(TAG, "IMI list already contains the same InputMethod.");
1480 break;
satok4e4569d2010-11-19 18:45:53 +09001481 }
satokf3db1af2010-11-23 13:34:33 +09001482 subtypes = new ArrayList<InputMethodSubtype>();
1483 ret.put((InputMethodInfo)o, subtypes);
1484 } else if (subtypes != null && o instanceof InputMethodSubtype) {
1485 subtypes.add((InputMethodSubtype)o);
satok4e4569d2010-11-19 18:45:53 +09001486 }
satok4e4569d2010-11-19 18:45:53 +09001487 }
1488 }
1489 } catch (RemoteException e) {
1490 Log.w(TAG, "IME died: " + mCurId, e);
1491 }
1492 return ret;
1493 }
1494 }
satokf3db1af2010-11-23 13:34:33 +09001495
satok735cf382010-11-11 20:40:09 +09001496 public boolean switchToLastInputMethod(IBinder imeToken) {
1497 synchronized (mH) {
1498 try {
1499 return mService.switchToLastInputMethod(imeToken);
1500 } catch (RemoteException e) {
1501 Log.w(TAG, "IME died: " + mCurId, e);
1502 return false;
1503 }
1504 }
1505 }
1506
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1508 final Printer p = new PrintWriterPrinter(fout);
1509 p.println("Input method client state for " + this + ":");
1510
1511 p.println(" mService=" + mService);
1512 p.println(" mMainLooper=" + mMainLooper);
1513 p.println(" mIInputContext=" + mIInputContext);
1514 p.println(" mActive=" + mActive
1515 + " mHasBeenInactive=" + mHasBeenInactive
1516 + " mBindSequence=" + mBindSequence
1517 + " mCurId=" + mCurId);
1518 p.println(" mCurMethod=" + mCurMethod);
1519 p.println(" mCurRootView=" + mCurRootView);
1520 p.println(" mServedView=" + mServedView);
1521 p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart
1522 + " mNextServedView=" + mNextServedView);
1523 p.println(" mServedConnecting=" + mServedConnecting);
1524 if (mCurrentTextBoxAttribute != null) {
1525 p.println(" mCurrentTextBoxAttribute:");
1526 mCurrentTextBoxAttribute.dump(p, " ");
1527 } else {
1528 p.println(" mCurrentTextBoxAttribute: null");
1529 }
1530 p.println(" mServedInputConnection=" + mServedInputConnection);
1531 p.println(" mCompletions=" + mCompletions);
1532 p.println(" mCursorRect=" + mCursorRect);
1533 p.println(" mCursorSelStart=" + mCursorSelStart
1534 + " mCursorSelEnd=" + mCursorSelEnd
1535 + " mCursorCandStart=" + mCursorCandStart
1536 + " mCursorCandEnd=" + mCursorCandEnd);
1537 }
1538}