blob: 7f82ce3508eb05f557c2a8d9e21b64c50f80524d [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007-2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package android.inputmethodservice;
18
Romain Guy980a9382010-01-08 15:06:28 -080019import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21
Dianne Hackborn836531b2012-08-01 19:00:38 -070022import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.app.Dialog;
24import android.content.Context;
25import android.content.res.Configuration;
Dianne Hackbornd922ae02011-01-14 11:43:24 -080026import android.content.res.Resources;
The Android Open Source Project10592532009-03-18 17:39:46 -070027import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.graphics.Rect;
Jeff Brownfbf09772011-01-16 14:06:57 -080029import android.graphics.Region;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.os.Bundle;
31import android.os.IBinder;
The Android Open Source Project4df24232009-03-05 14:34:35 -080032import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.os.SystemClock;
34import android.provider.Settings;
35import android.text.InputType;
36import android.text.Layout;
37import android.text.Spannable;
38import android.text.method.MovementMethod;
39import android.util.Log;
40import android.util.PrintWriterPrinter;
41import android.util.Printer;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080042import android.view.KeyCharacterMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.view.KeyEvent;
44import android.view.LayoutInflater;
45import android.view.MotionEvent;
46import android.view.View;
47import android.view.ViewGroup;
48import android.view.ViewTreeObserver;
49import android.view.Window;
50import android.view.WindowManager;
Craig Mautnere4bbb1c2013-03-15 11:38:44 -070051import android.view.WindowManager.BadTokenException;
The Android Open Source Project10592532009-03-18 17:39:46 -070052import android.view.animation.AnimationUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.view.inputmethod.CompletionInfo;
Gilles Debunne8cbb4c62011-01-24 12:33:56 -080054import android.view.inputmethod.EditorInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.view.inputmethod.ExtractedText;
56import android.view.inputmethod.ExtractedTextRequest;
57import android.view.inputmethod.InputBinding;
58import android.view.inputmethod.InputConnection;
59import android.view.inputmethod.InputMethod;
60import android.view.inputmethod.InputMethodManager;
satokab751aa2010-09-14 19:17:36 +090061import android.view.inputmethod.InputMethodSubtype;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.widget.Button;
63import android.widget.FrameLayout;
The Android Open Source Project10592532009-03-18 17:39:46 -070064import android.widget.LinearLayout;
65
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import java.io.FileDescriptor;
67import java.io.PrintWriter;
68
69/**
70 * InputMethodService provides a standard implementation of an InputMethod,
71 * which final implementations can derive from and customize. See the
72 * base class {@link AbstractInputMethodService} and the {@link InputMethod}
73 * interface for more information on the basics of writing input methods.
74 *
75 * <p>In addition to the normal Service lifecycle methods, this class
76 * introduces some new specific callbacks that most subclasses will want
77 * to make use of:</p>
78 * <ul>
79 * <li> {@link #onInitializeInterface()} for user-interface initialization,
80 * in particular to deal with configuration changes while the service is
81 * running.
82 * <li> {@link #onBindInput} to find out about switching to a new client.
83 * <li> {@link #onStartInput} to deal with an input session starting with
84 * the client.
85 * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
86 * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
87 * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
88 * starting within the input area of the IME.
89 * </ul>
90 *
91 * <p>An input method has significant discretion in how it goes about its
92 * work: the {@link android.inputmethodservice.InputMethodService} provides
93 * a basic framework for standard UI elements (input view, candidates view,
94 * and running in fullscreen mode), but it is up to a particular implementor
95 * to decide how to use them. For example, one input method could implement
96 * an input area with a keyboard, another could allow the user to draw text,
97 * while a third could have no input area (and thus not be visible to the
98 * user) but instead listen to audio and perform text to speech conversion.</p>
99 *
100 * <p>In the implementation provided here, all of these elements are placed
101 * together in a single window managed by the InputMethodService. It will
102 * execute callbacks as it needs information about them, and provides APIs for
103 * programmatic control over them. They layout of these elements is explicitly
104 * defined:</p>
105 *
106 * <ul>
107 * <li>The soft input view, if available, is placed at the bottom of the
108 * screen.
109 * <li>The candidates view, if currently shown, is placed above the soft
110 * input view.
111 * <li>If not running fullscreen, the application is moved or resized to be
112 * above these views; if running fullscreen, the window will completely cover
113 * the application and its top part will contain the extract text of what is
114 * currently being edited by the application.
115 * </ul>
116 *
117 *
118 * <a name="SoftInputView"></a>
119 * <h3>Soft Input View</h3>
120 *
121 * <p>Central to most input methods is the soft input view. This is where most
122 * user interaction occurs: pressing on soft keys, drawing characters, or
123 * however else your input method wants to generate text. Most implementations
124 * will simply have their own view doing all of this work, and return a new
125 * instance of it when {@link #onCreateInputView()} is called. At that point,
126 * as long as the input view is visible, you will see user interaction in
127 * that view and can call back on the InputMethodService to interact with the
128 * application as appropriate.</p>
129 *
130 * <p>There are some situations where you want to decide whether or not your
131 * soft input view should be shown to the user. This is done by implementing
132 * the {@link #onEvaluateInputViewShown()} to return true or false based on
133 * whether it should be shown in the current environment. If any of your
134 * state has changed that may impact this, call
135 * {@link #updateInputViewShown()} to have it re-evaluated. The default
136 * implementation always shows the input view unless there is a hard
137 * keyboard available, which is the appropriate behavior for most input
138 * methods.</p>
139 *
140 *
141 * <a name="CandidatesView"></a>
142 * <h3>Candidates View</h3>
143 *
144 * <p>Often while the user is generating raw text, an input method wants to
145 * provide them with a list of possible interpretations of that text that can
146 * be selected for use. This is accomplished with the candidates view, and
147 * like the soft input view you implement {@link #onCreateCandidatesView()}
148 * to instantiate your own view implementing your candidates UI.</p>
149 *
150 * <p>Management of the candidates view is a little different than the input
151 * view, because the candidates view tends to be more transient, being shown
152 * only when there are possible candidates for the current text being entered
153 * by the user. To control whether the candidates view is shown, you use
154 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate
155 * view tends to be shown and hidden a lot, it does not impact the application
156 * UI in the same way as the soft input view: it will never cause application
157 * windows to resize, only cause them to be panned if needed for the user to
158 * see the current focus.</p>
159 *
160 *
161 * <a name="FullscreenMode"></a>
162 * <h3>Fullscreen Mode</h3>
163 *
164 * <p>Sometimes your input method UI is too large to integrate with the
165 * application UI, so you just want to take over the screen. This is
166 * accomplished by switching to full-screen mode, causing the input method
167 * window to fill the entire screen and add its own "extracted text" editor
168 * showing the user the text that is being typed. Unlike the other UI elements,
169 * there is a standard implementation for the extract editor that you should
170 * not need to change. The editor is placed at the top of the IME, above the
171 * input and candidates views.</p>
172 *
173 * <p>Similar to the input view, you control whether the IME is running in
174 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
175 * to return true or false based on
176 * whether it should be fullscreen in the current environment. If any of your
177 * state has changed that may impact this, call
178 * {@link #updateFullscreenMode()} to have it re-evaluated. The default
179 * implementation selects fullscreen mode when the screen is in a landscape
180 * orientation, which is appropriate behavior for most input methods that have
181 * a significant input area.</p>
182 *
183 * <p>When in fullscreen mode, you have some special requirements because the
184 * user can not see the application UI. In particular, you should implement
185 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
186 * generated by your application, typically in your candidates view like you
187 * would normally show candidates.
188 *
189 *
190 * <a name="GeneratingText"></a>
191 * <h3>Generating Text</h3>
192 *
193 * <p>The key part of an IME is of course generating text for the application.
194 * This is done through calls to the
195 * {@link android.view.inputmethod.InputConnection} interface to the
196 * application, which can be retrieved from {@link #getCurrentInputConnection()}.
197 * This interface allows you to generate raw key events or, if the target
198 * supports it, directly edit in strings of candidates and committed text.</p>
199 *
200 * <p>Information about what the target is expected and supports can be found
201 * through the {@link android.view.inputmethod.EditorInfo} class, which is
202 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most
203 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
204 * EditorInfo.inputType}; in particular, if this is
205 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
206 * then the target does not support complex edits and you need to only deliver
207 * raw key events to it. An input method will also want to look at other
208 * values here, to for example detect password mode, auto complete text views,
209 * phone number entry, etc.</p>
210 *
211 * <p>When the user switches between input targets, you will receive calls to
212 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
213 * You can use these to reset and initialize your input state for the current
214 * target. For example, you will often want to clear any input state, and
215 * update a soft keyboard to be appropriate for the new inputType.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -0700216 *
217 * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
218 * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
219 * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 */
221public class InputMethodService extends AbstractInputMethodService {
222 static final String TAG = "InputMethodService";
223 static final boolean DEBUG = false;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800224
225 /**
226 * The back button will close the input window.
227 */
228 public static final int BACK_DISPOSITION_DEFAULT = 0; // based on window
229
230 /**
231 * This input method will not consume the back key.
232 */
233 public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // back
234
235 /**
236 * This input method will consume the back key.
237 */
238 public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // down
239
240 /**
241 * @hide
242 * The IME is active. It may or may not be visible.
243 */
244 public static final int IME_ACTIVE = 0x1;
245
246 /**
247 * @hide
248 * The IME is visible.
249 */
250 public static final int IME_VISIBLE = 0x2;
251
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 InputMethodManager mImm;
253
Dianne Hackbornd922ae02011-01-14 11:43:24 -0800254 int mTheme = 0;
Dianne Hackborn836531b2012-08-01 19:00:38 -0700255 boolean mHardwareAccelerated = false;
The Android Open Source Project10592532009-03-18 17:39:46 -0700256
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 LayoutInflater mInflater;
The Android Open Source Project10592532009-03-18 17:39:46 -0700258 TypedArray mThemeAttrs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 View mRootView;
260 SoftInputWindow mWindow;
261 boolean mInitialized;
262 boolean mWindowCreated;
263 boolean mWindowAdded;
264 boolean mWindowVisible;
The Android Open Source Project10592532009-03-18 17:39:46 -0700265 boolean mWindowWasVisible;
266 boolean mInShowWindow;
267 ViewGroup mFullscreenArea;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 FrameLayout mExtractFrame;
269 FrameLayout mCandidatesFrame;
270 FrameLayout mInputFrame;
271
272 IBinder mToken;
273
274 InputBinding mInputBinding;
275 InputConnection mInputConnection;
276 boolean mInputStarted;
277 boolean mInputViewStarted;
278 boolean mCandidatesViewStarted;
279 InputConnection mStartedInputConnection;
280 EditorInfo mInputEditorInfo;
281
282 int mShowInputFlags;
283 boolean mShowInputRequested;
284 boolean mLastShowInputRequested;
285 int mCandidatesVisibility;
286 CompletionInfo[] mCurCompletions;
287
288 boolean mShowInputForced;
289
290 boolean mFullscreenApplied;
291 boolean mIsFullscreen;
292 View mExtractView;
The Android Open Source Project10592532009-03-18 17:39:46 -0700293 boolean mExtractViewHidden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 ExtractEditText mExtractEditText;
295 ViewGroup mExtractAccessories;
296 Button mExtractAction;
297 ExtractedText mExtractedText;
298 int mExtractedToken;
299
300 View mInputView;
301 boolean mIsInputViewShown;
302
303 int mStatusIcon;
Joe Onorato857fd9b2011-01-27 15:08:35 -0800304 int mBackDisposition;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305
306 final Insets mTmpInsets = new Insets();
307 final int[] mTmpLocation = new int[2];
satokab751aa2010-09-14 19:17:36 +0900308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
310 new ViewTreeObserver.OnComputeInternalInsetsListener() {
311 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700312 if (isExtractViewShown()) {
313 // In true fullscreen mode, we just say the window isn't covering
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 // any content so we don't impact whatever is behind.
315 View decor = getWindow().getWindow().getDecorView();
316 info.contentInsets.top = info.visibleInsets.top
317 = decor.getHeight();
Jeff Brownfbf09772011-01-16 14:06:57 -0800318 info.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
320 } else {
321 onComputeInsets(mTmpInsets);
322 info.contentInsets.top = mTmpInsets.contentTopInsets;
323 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -0800324 info.touchableRegion.set(mTmpInsets.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 info.setTouchableInsets(mTmpInsets.touchableInsets);
326 }
327 }
328 };
329
330 final View.OnClickListener mActionClickListener = new View.OnClickListener() {
331 public void onClick(View v) {
332 final EditorInfo ei = getCurrentInputEditorInfo();
333 final InputConnection ic = getCurrentInputConnection();
334 if (ei != null && ic != null) {
335 if (ei.actionId != 0) {
336 ic.performEditorAction(ei.actionId);
337 } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION)
338 != EditorInfo.IME_ACTION_NONE) {
339 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
340 }
341 }
342 }
343 };
344
345 /**
346 * Concrete implementation of
347 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
348 * all of the standard behavior for an input method.
349 */
350 public class InputMethodImpl extends AbstractInputMethodImpl {
351 /**
352 * Take care of attaching the given window token provided by the system.
353 */
354 public void attachToken(IBinder token) {
355 if (mToken == null) {
356 mToken = token;
357 mWindow.setToken(token);
358 }
359 }
360
361 /**
362 * Handle a new input binding, calling
363 * {@link InputMethodService#onBindInput InputMethodService.onBindInput()}
364 * when done.
365 */
366 public void bindInput(InputBinding binding) {
367 mInputBinding = binding;
368 mInputConnection = binding.getConnection();
369 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
370 + " ic=" + mInputConnection);
371 InputConnection ic = getCurrentInputConnection();
372 if (ic != null) ic.reportFullscreenMode(mIsFullscreen);
373 initialize();
374 onBindInput();
375 }
376
377 /**
378 * Clear the current input binding.
379 */
380 public void unbindInput() {
381 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
382 + " ic=" + mInputConnection);
383 onUnbindInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 mInputBinding = null;
385 mInputConnection = null;
386 }
387
388 public void startInput(InputConnection ic, EditorInfo attribute) {
389 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
390 doStartInput(ic, attribute, false);
391 }
392
393 public void restartInput(InputConnection ic, EditorInfo attribute) {
394 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
395 doStartInput(ic, attribute, true);
396 }
397
398 /**
399 * Handle a request by the system to hide the soft input area.
400 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800401 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 if (DEBUG) Log.v(TAG, "hideSoftInput()");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800403 boolean wasVis = isInputViewShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 mShowInputFlags = 0;
405 mShowInputRequested = false;
406 mShowInputForced = false;
satok2f913d92012-05-10 01:48:03 +0900407 doHideWindow();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800408 if (resultReceiver != null) {
409 resultReceiver.send(wasVis != isInputViewShown()
410 ? InputMethodManager.RESULT_HIDDEN
411 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
412 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
413 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 }
415
416 /**
417 * Handle a request by the system to show the soft input area.
418 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800419 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700420 if (DEBUG) Log.v(TAG, "showSoftInput()");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800421 boolean wasVis = isInputViewShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 mShowInputFlags = 0;
423 if (onShowInputRequested(flags, false)) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700424 try {
425 showWindow(true);
426 } catch (BadTokenException e) {
427 if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
428 mWindowVisible = false;
Craig Mautnerf403b1f2013-03-19 16:54:35 -0700429 mWindowAdded = false;
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700430 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 }
satok865b9772011-01-21 02:45:06 +0900432 // If user uses hard keyboard, IME button should always be shown.
jungheang.lee217fd292013-02-26 16:53:22 +0900433 boolean showing = isInputViewShown();
Joe Onorato857fd9b2011-01-27 15:08:35 -0800434 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
435 mBackDisposition);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800436 if (resultReceiver != null) {
437 resultReceiver.send(wasVis != isInputViewShown()
438 ? InputMethodManager.RESULT_SHOWN
439 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
440 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 }
satokab751aa2010-09-14 19:17:36 +0900443
444 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
445 onCurrentInputMethodSubtypeChanged(subtype);
446 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 }
satokab751aa2010-09-14 19:17:36 +0900448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 /**
450 * Concrete implementation of
451 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
452 * all of the standard behavior for an input method session.
453 */
454 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
455 public void finishInput() {
456 if (!isEnabled()) {
457 return;
458 }
459 if (DEBUG) Log.v(TAG, "finishInput() in " + this);
460 doFinishInput();
461 }
462
463 /**
464 * Call {@link InputMethodService#onDisplayCompletions
465 * InputMethodService.onDisplayCompletions()}.
466 */
467 public void displayCompletions(CompletionInfo[] completions) {
468 if (!isEnabled()) {
469 return;
470 }
471 mCurCompletions = completions;
472 onDisplayCompletions(completions);
473 }
474
475 /**
476 * Call {@link InputMethodService#onUpdateExtractedText
477 * InputMethodService.onUpdateExtractedText()}.
478 */
479 public void updateExtractedText(int token, ExtractedText text) {
480 if (!isEnabled()) {
481 return;
482 }
483 onUpdateExtractedText(token, text);
484 }
485
486 /**
487 * Call {@link InputMethodService#onUpdateSelection
488 * InputMethodService.onUpdateSelection()}.
489 */
490 public void updateSelection(int oldSelStart, int oldSelEnd,
491 int newSelStart, int newSelEnd,
492 int candidatesStart, int candidatesEnd) {
493 if (!isEnabled()) {
494 return;
495 }
496 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
497 newSelStart, newSelEnd, candidatesStart, candidatesEnd);
498 }
satok863fcd62011-06-21 17:38:02 +0900499
500 @Override
501 public void viewClicked(boolean focusChanged) {
502 if (!isEnabled()) {
503 return;
504 }
505 InputMethodService.this.onViewClicked(focusChanged);
506 }
507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 /**
509 * Call {@link InputMethodService#onUpdateCursor
510 * InputMethodService.onUpdateCursor()}.
511 */
512 public void updateCursor(Rect newCursor) {
513 if (!isEnabled()) {
514 return;
515 }
516 InputMethodService.this.onUpdateCursor(newCursor);
517 }
518
519 /**
520 * Call {@link InputMethodService#onAppPrivateCommand
521 * InputMethodService.onAppPrivateCommand()}.
522 */
523 public void appPrivateCommand(String action, Bundle data) {
524 if (!isEnabled()) {
525 return;
526 }
527 InputMethodService.this.onAppPrivateCommand(action, data);
528 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800529
530 /**
531 *
532 */
533 public void toggleSoftInput(int showFlags, int hideFlags) {
534 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 }
537
538 /**
539 * Information about where interesting parts of the input method UI appear.
540 */
541 public static final class Insets {
542 /**
543 * This is the top part of the UI that is the main content. It is
544 * used to determine the basic space needed, to resize/pan the
545 * application behind. It is assumed that this inset does not
546 * change very much, since any change will cause a full resize/pan
547 * of the application behind. This value is relative to the top edge
548 * of the input method window.
549 */
550 public int contentTopInsets;
551
552 /**
553 * This is the top part of the UI that is visibly covering the
554 * application behind it. This provides finer-grained control over
555 * visibility, allowing you to change it relatively frequently (such
556 * as hiding or showing candidates) without disrupting the underlying
557 * UI too much. For example, this will never resize the application
558 * UI, will only pan if needed to make the current focus visible, and
559 * will not aggressively move the pan position when this changes unless
560 * needed to make the focus visible. This value is relative to the top edge
561 * of the input method window.
562 */
563 public int visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -0800564
565 /**
566 * This is the region of the UI that is touchable. It is used when
567 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
568 * The region should be specified relative to the origin of the window frame.
569 */
570 public final Region touchableRegion = new Region();
571
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 /**
573 * Option for {@link #touchableInsets}: the entire window frame
574 * can be touched.
575 */
576 public static final int TOUCHABLE_INSETS_FRAME
577 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
578
579 /**
580 * Option for {@link #touchableInsets}: the area inside of
581 * the content insets can be touched.
582 */
583 public static final int TOUCHABLE_INSETS_CONTENT
584 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
585
586 /**
587 * Option for {@link #touchableInsets}: the area inside of
588 * the visible insets can be touched.
589 */
590 public static final int TOUCHABLE_INSETS_VISIBLE
591 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -0800592
593 /**
594 * Option for {@link #touchableInsets}: the region specified by
595 * {@link #touchableRegion} can be touched.
596 */
597 public static final int TOUCHABLE_INSETS_REGION
598 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
599
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 /**
601 * Determine which area of the window is touchable by the user. May
602 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
Jeff Brownfbf09772011-01-16 14:06:57 -0800603 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
604 * or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 */
606 public int touchableInsets;
607 }
satok865b9772011-01-21 02:45:06 +0900608
The Android Open Source Project10592532009-03-18 17:39:46 -0700609 /**
610 * You can call this to customize the theme used by your IME's window.
611 * This theme should typically be one that derives from
612 * {@link android.R.style#Theme_InputMethod}, which is the default theme
613 * you will get. This must be set before {@link #onCreate}, so you
614 * will typically call it in your constructor with the resource ID
615 * of your custom theme.
616 */
satokab751aa2010-09-14 19:17:36 +0900617 @Override
The Android Open Source Project10592532009-03-18 17:39:46 -0700618 public void setTheme(int theme) {
619 if (mWindow != null) {
620 throw new IllegalStateException("Must be called before onCreate()");
621 }
622 mTheme = theme;
623 }
satok865b9772011-01-21 02:45:06 +0900624
Dianne Hackborn836531b2012-08-01 19:00:38 -0700625 /**
626 * You can call this to try to enable hardware accelerated drawing for
627 * your IME. This must be set before {@link #onCreate}, so you
628 * will typically call it in your constructor. It is not always possible
629 * to use hardware acclerated drawing in an IME (for example on low-end
630 * devices that do not have the resources to support this), so the call
631 * returns true if it succeeds otherwise false if you will need to draw
632 * in software. You must be able to handle either case.
633 */
634 public boolean enableHardwareAcceleration() {
635 if (mWindow != null) {
636 throw new IllegalStateException("Must be called before onCreate()");
637 }
Jeff Brown98365d72012-08-19 20:30:52 -0700638 if (ActivityManager.isHighEndGfx()) {
Dianne Hackborn836531b2012-08-01 19:00:38 -0700639 mHardwareAccelerated = true;
640 return true;
641 }
642 return false;
643 }
644
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 @Override public void onCreate() {
Dianne Hackbornd922ae02011-01-14 11:43:24 -0800646 mTheme = Resources.selectSystemTheme(mTheme,
647 getApplicationInfo().targetSdkVersion,
Adam Powell6e90a362011-08-14 16:48:32 -0700648 android.R.style.Theme_InputMethod,
Adam Powell36a97e402011-09-07 11:27:49 -0700649 android.R.style.Theme_Holo_InputMethod,
Adam Powell6e90a362011-08-14 16:48:32 -0700650 android.R.style.Theme_DeviceDefault_InputMethod);
The Android Open Source Project10592532009-03-18 17:39:46 -0700651 super.setTheme(mTheme);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 super.onCreate();
653 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
654 mInflater = (LayoutInflater)getSystemService(
655 Context.LAYOUT_INFLATER_SERVICE);
Dianne Hackborn83fe3f52009-09-12 23:38:30 -0700656 mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
Dianne Hackborn836531b2012-08-01 19:00:38 -0700657 if (mHardwareAccelerated) {
658 mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
659 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 initViews();
Romain Guy980a9382010-01-08 15:06:28 -0800661 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 }
satokab751aa2010-09-14 19:17:36 +0900663
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 /**
665 * This is a hook that subclasses can use to perform initialization of
666 * their interface. It is called for you prior to any of your UI objects
667 * being created, both after the service is first created and after a
668 * configuration change happens.
669 */
670 public void onInitializeInterface() {
Gilles Debunne34703b62011-09-08 11:16:25 -0700671 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 }
satokab751aa2010-09-14 19:17:36 +0900673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 void initialize() {
675 if (!mInitialized) {
676 mInitialized = true;
677 onInitializeInterface();
678 }
679 }
satokab751aa2010-09-14 19:17:36 +0900680
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 void initViews() {
682 mInitialized = false;
683 mWindowCreated = false;
684 mShowInputRequested = false;
685 mShowInputForced = false;
686
The Android Open Source Project10592532009-03-18 17:39:46 -0700687 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 mRootView = mInflater.inflate(
689 com.android.internal.R.layout.input_method, null);
690 mWindow.setContentView(mRootView);
691 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
Jeff Sharkey6e2bee72012-10-01 13:39:08 -0700692 if (Settings.Global.getInt(getContentResolver(),
693 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 mWindow.getWindow().setWindowAnimations(
695 com.android.internal.R.style.Animation_InputMethodFancy);
696 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700697 mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
698 mExtractViewHidden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
700 mExtractView = null;
701 mExtractEditText = null;
702 mExtractAccessories = null;
703 mExtractAction = null;
704 mFullscreenApplied = false;
705
706 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
707 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
708 mInputView = null;
709 mIsInputViewShown = false;
710
711 mExtractFrame.setVisibility(View.GONE);
712 mCandidatesVisibility = getCandidatesHiddenVisibility();
713 mCandidatesFrame.setVisibility(mCandidatesVisibility);
714 mInputFrame.setVisibility(View.GONE);
715 }
satokab751aa2010-09-14 19:17:36 +0900716
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 @Override public void onDestroy() {
718 super.onDestroy();
719 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
720 mInsetsComputer);
Satoshi Kataokac56191f2013-05-30 13:14:47 +0900721 doFinishInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 if (mWindowAdded) {
satokc0c87652011-09-12 12:08:05 +0900723 // Disable exit animation for the current IME window
724 // to avoid the race condition between the exit and enter animations
725 // when the current IME is being switched to another one.
726 mWindow.getWindow().setWindowAnimations(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 mWindow.dismiss();
728 }
729 }
satokf17db9f2011-09-14 18:55:58 +0900730
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 /**
732 * Take care of handling configuration changes. Subclasses of
733 * InputMethodService generally don't need to deal directly with
734 * this on their own; the standard implementation here takes care of
735 * regenerating the input method UI as a result of the configuration
736 * change, so you can rely on your {@link #onCreateInputView} and
737 * other methods being called as appropriate due to a configuration change.
738 *
739 * <p>When a configuration change does happen,
740 * {@link #onInitializeInterface()} is guaranteed to be called the next
741 * time prior to any of the other input or UI creation callbacks. The
742 * following will be called immediately depending if appropriate for current
743 * state: {@link #onStartInput} if input is active, and
744 * {@link #onCreateInputView} and {@link #onStartInputView} and related
745 * appropriate functions if the UI is displayed.
746 */
747 @Override public void onConfigurationChanged(Configuration newConfig) {
748 super.onConfigurationChanged(newConfig);
749
750 boolean visible = mWindowVisible;
751 int showFlags = mShowInputFlags;
752 boolean showingInput = mShowInputRequested;
753 CompletionInfo[] completions = mCurCompletions;
754 initViews();
755 mInputViewStarted = false;
756 mCandidatesViewStarted = false;
757 if (mInputStarted) {
758 doStartInput(getCurrentInputConnection(),
759 getCurrentInputEditorInfo(), true);
760 }
761 if (visible) {
762 if (showingInput) {
763 // If we were last showing the soft keyboard, try to do so again.
764 if (onShowInputRequested(showFlags, true)) {
765 showWindow(true);
766 if (completions != null) {
767 mCurCompletions = completions;
768 onDisplayCompletions(completions);
769 }
770 } else {
satok2f913d92012-05-10 01:48:03 +0900771 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 }
773 } else if (mCandidatesVisibility == View.VISIBLE) {
774 // If the candidates are currently visible, make sure the
775 // window is shown for them.
776 showWindow(false);
777 } else {
778 // Otherwise hide the window.
satok2f913d92012-05-10 01:48:03 +0900779 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 }
satok865b9772011-01-21 02:45:06 +0900781 // If user uses hard keyboard, IME button should always be shown.
Joe Onorato857fd9b2011-01-27 15:08:35 -0800782 boolean showing = onEvaluateInputViewShown();
783 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
784 mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 }
786 }
787
788 /**
789 * Implement to return our standard {@link InputMethodImpl}. Subclasses
790 * can override to provide their own customized version.
791 */
satokab751aa2010-09-14 19:17:36 +0900792 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 public AbstractInputMethodImpl onCreateInputMethodInterface() {
794 return new InputMethodImpl();
795 }
796
797 /**
798 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
799 * can override to provide their own customized version.
800 */
satokab751aa2010-09-14 19:17:36 +0900801 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
803 return new InputMethodSessionImpl();
804 }
805
806 public LayoutInflater getLayoutInflater() {
807 return mInflater;
808 }
809
810 public Dialog getWindow() {
811 return mWindow;
812 }
813
Joe Onorato857fd9b2011-01-27 15:08:35 -0800814 public void setBackDisposition(int disposition) {
815 mBackDisposition = disposition;
816 }
817
818 public int getBackDisposition() {
819 return mBackDisposition;
820 }
821
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 /**
823 * Return the maximum width, in pixels, available the input method.
824 * Input methods are positioned at the bottom of the screen and, unless
825 * running in fullscreen, will generally want to be as short as possible
826 * so should compute their height based on their contents. However, they
827 * can stretch as much as needed horizontally. The function returns to
828 * you the maximum amount of space available horizontally, which you can
829 * use if needed for UI placement.
830 *
831 * <p>In many cases this is not needed, you can just rely on the normal
832 * view layout mechanisms to position your views within the full horizontal
833 * space given to the input method.
834 *
835 * <p>Note that this value can change dynamically, in particular when the
836 * screen orientation changes.
837 */
838 public int getMaxWidth() {
839 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
840 return wm.getDefaultDisplay().getWidth();
841 }
842
843 /**
844 * Return the currently active InputBinding for the input method, or
845 * null if there is none.
846 */
847 public InputBinding getCurrentInputBinding() {
848 return mInputBinding;
849 }
850
851 /**
852 * Retrieve the currently active InputConnection that is bound to
853 * the input method, or null if there is none.
854 */
855 public InputConnection getCurrentInputConnection() {
856 InputConnection ic = mStartedInputConnection;
857 if (ic != null) {
858 return ic;
859 }
860 return mInputConnection;
861 }
862
863 public boolean getCurrentInputStarted() {
864 return mInputStarted;
865 }
866
867 public EditorInfo getCurrentInputEditorInfo() {
868 return mInputEditorInfo;
869 }
870
871 /**
872 * Re-evaluate whether the input method should be running in fullscreen
873 * mode, and update its UI if this has changed since the last time it
874 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to
875 * determine whether it should currently run in fullscreen mode. You
876 * can use {@link #isFullscreenMode()} to determine if the input method
877 * is currently running in fullscreen mode.
878 */
879 public void updateFullscreenMode() {
880 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
881 boolean changed = mLastShowInputRequested != mShowInputRequested;
882 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
883 changed = true;
884 mIsFullscreen = isFullscreen;
885 InputConnection ic = getCurrentInputConnection();
886 if (ic != null) ic.reportFullscreenMode(isFullscreen);
887 mFullscreenApplied = true;
888 initialize();
The Android Open Source Project10592532009-03-18 17:39:46 -0700889 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
890 mFullscreenArea.getLayoutParams();
891 if (isFullscreen) {
892 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
893 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
894 lp.height = 0;
895 lp.weight = 1;
896 } else {
897 mFullscreenArea.setBackgroundDrawable(null);
898 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
899 lp.weight = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700901 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
902 mFullscreenArea, lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 if (isFullscreen) {
904 if (mExtractView == null) {
905 View v = onCreateExtractTextView();
906 if (v != null) {
907 setExtractView(v);
908 }
909 }
910 startExtractingText(false);
911 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700912 updateExtractFrameVisibility();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 }
914
915 if (changed) {
Gilles Debunne34703b62011-09-08 11:16:25 -0700916 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 mLastShowInputRequested = mShowInputRequested;
918 }
919 }
920
921 /**
922 * Update the given window's parameters for the given mode. This is called
923 * when the window is first displayed and each time the fullscreen or
924 * candidates only mode changes.
925 *
926 * <p>The default implementation makes the layout for the window
Romain Guy980a9382010-01-08 15:06:28 -0800927 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
928 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 *
930 * @param win The input method's window.
931 * @param isFullscreen If true, the window is running in fullscreen mode
932 * and intended to cover the entire application display.
933 * @param isCandidatesOnly If true, the window is only showing the
934 * candidates view and none of the rest of its UI. This is mutually
935 * exclusive with fullscreen mode.
936 */
937 public void onConfigureWindow(Window win, boolean isFullscreen,
938 boolean isCandidatesOnly) {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +0900939 final int currentHeight = mWindow.getWindow().getAttributes().height;
940 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
941 if (mIsInputViewShown && currentHeight != newHeight) {
942 Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: "
943 + currentHeight + " -> " + newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 }
Satoshi Kataoka8b117c82012-11-06 18:59:23 +0900945 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 }
947
948 /**
949 * Return whether the input method is <em>currently</em> running in
950 * fullscreen mode. This is the mode that was last determined and
951 * applied by {@link #updateFullscreenMode()}.
952 */
953 public boolean isFullscreenMode() {
954 return mIsFullscreen;
955 }
956
957 /**
958 * Override this to control when the input method should run in
959 * fullscreen mode. The default implementation runs in fullsceen only
960 * when the screen is in landscape mode. If you change what
961 * this returns, you will need to call {@link #updateFullscreenMode()}
962 * yourself whenever the returned value may have changed to have it
963 * re-evaluated and applied.
964 */
965 public boolean onEvaluateFullscreenMode() {
966 Configuration config = getResources().getConfiguration();
Leon Scroggins2edd6822010-01-12 11:36:13 -0500967 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
968 return false;
969 }
970 if (mInputEditorInfo != null
971 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
972 return false;
973 }
974 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 }
Gilles Debunne34703b62011-09-08 11:16:25 -0700976
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 /**
The Android Open Source Project10592532009-03-18 17:39:46 -0700978 * Controls the visibility of the extracted text area. This only applies
979 * when the input method is in fullscreen mode, and thus showing extracted
980 * text. When false, the extracted text will not be shown, allowing some
981 * of the application to be seen behind. This is normally set for you
982 * by {@link #onUpdateExtractingVisibility}. This controls the visibility
983 * of both the extracted text and candidate view; the latter since it is
984 * not useful if there is no text to see.
985 */
986 public void setExtractViewShown(boolean shown) {
987 if (mExtractViewHidden == shown) {
988 mExtractViewHidden = !shown;
989 updateExtractFrameVisibility();
990 }
991 }
992
993 /**
994 * Return whether the fullscreen extract view is shown. This will only
995 * return true if {@link #isFullscreenMode()} returns true, and in that
996 * case its value depends on the last call to
997 * {@link #setExtractViewShown(boolean)}. This effectively lets you
998 * determine if the application window is entirely covered (when this
999 * returns true) or if some part of it may be shown (if this returns
1000 * false, though if {@link #isFullscreenMode()} returns true in that case
1001 * then it is probably only a sliver of the application).
1002 */
1003 public boolean isExtractViewShown() {
1004 return mIsFullscreen && !mExtractViewHidden;
1005 }
1006
1007 void updateExtractFrameVisibility() {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001008 final int vis;
The Android Open Source Project10592532009-03-18 17:39:46 -07001009 if (isFullscreenMode()) {
1010 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001011 // "vis" should be applied for the extract frame as well in the fullscreen mode.
1012 mExtractFrame.setVisibility(vis);
The Android Open Source Project10592532009-03-18 17:39:46 -07001013 } else {
1014 vis = View.VISIBLE;
1015 mExtractFrame.setVisibility(View.GONE);
1016 }
1017 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
1018 if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
1019 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1020 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1021 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1022 0);
1023 if (animRes != 0) {
1024 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1025 this, animRes));
1026 }
1027 }
1028 mFullscreenArea.setVisibility(vis);
1029 }
1030
1031 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 * Compute the interesting insets into your UI. The default implementation
1033 * uses the top of the candidates frame for the visible insets, and the
1034 * top of the input frame for the content insets. The default touchable
1035 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1036 *
The Android Open Source Project10592532009-03-18 17:39:46 -07001037 * <p>Note that this method is not called when
1038 * {@link #isExtractViewShown} returns true, since
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 * in that case the application is left as-is behind the input method and
1040 * not impacted by anything in its UI.
1041 *
1042 * @param outInsets Fill in with the current UI insets.
1043 */
1044 public void onComputeInsets(Insets outInsets) {
1045 int[] loc = mTmpLocation;
1046 if (mInputFrame.getVisibility() == View.VISIBLE) {
1047 mInputFrame.getLocationInWindow(loc);
1048 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001049 View decor = getWindow().getWindow().getDecorView();
1050 loc[1] = decor.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001052 if (isFullscreenMode()) {
1053 // In fullscreen mode, we never resize the underlying window.
1054 View decor = getWindow().getWindow().getDecorView();
1055 outInsets.contentTopInsets = decor.getHeight();
1056 } else {
1057 outInsets.contentTopInsets = loc[1];
1058 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1060 mCandidatesFrame.getLocationInWindow(loc);
1061 }
1062 outInsets.visibleTopInsets = loc[1];
1063 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001064 outInsets.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 }
1066
1067 /**
1068 * Re-evaluate whether the soft input area should currently be shown, and
1069 * update its UI if this has changed since the last time it
1070 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to
1071 * determine whether the input view should currently be shown. You
1072 * can use {@link #isInputViewShown()} to determine if the input view
1073 * is currently shown.
1074 */
1075 public void updateInputViewShown() {
1076 boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
1077 if (mIsInputViewShown != isShown && mWindowVisible) {
1078 mIsInputViewShown = isShown;
1079 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1080 if (mInputView == null) {
1081 initialize();
1082 View v = onCreateInputView();
1083 if (v != null) {
1084 setInputView(v);
1085 }
1086 }
1087 }
1088 }
1089
1090 /**
1091 * Returns true if we have been asked to show our input view.
1092 */
1093 public boolean isShowInputRequested() {
1094 return mShowInputRequested;
1095 }
1096
1097 /**
1098 * Return whether the soft input view is <em>currently</em> shown to the
1099 * user. This is the state that was last determined and
1100 * applied by {@link #updateInputViewShown()}.
1101 */
1102 public boolean isInputViewShown() {
1103 return mIsInputViewShown && mWindowVisible;
1104 }
1105
1106 /**
1107 * Override this to control when the soft input area should be shown to
1108 * the user. The default implementation only shows the input view when
1109 * there is no hard keyboard or the keyboard is hidden. If you change what
1110 * this returns, you will need to call {@link #updateInputViewShown()}
1111 * yourself whenever the returned value may have changed to have it
Gilles Debunne8cbb4c62011-01-24 12:33:56 -08001112 * re-evaluated and applied.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 */
1114 public boolean onEvaluateInputViewShown() {
1115 Configuration config = getResources().getConfiguration();
1116 return config.keyboard == Configuration.KEYBOARD_NOKEYS
Ken Wakasa8710e762011-01-30 11:02:09 +09001117 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 }
1119
1120 /**
1121 * Controls the visibility of the candidates display area. By default
1122 * it is hidden.
1123 */
1124 public void setCandidatesViewShown(boolean shown) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001125 updateCandidatesVisibility(shown);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 if (!mShowInputRequested && mWindowVisible != shown) {
1127 // If we are being asked to show the candidates view while the app
1128 // has not asked for the input view to be shown, then we need
1129 // to update whether the window is shown.
1130 if (shown) {
1131 showWindow(false);
1132 } else {
satok2f913d92012-05-10 01:48:03 +09001133 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 }
1135 }
1136 }
1137
The Android Open Source Project10592532009-03-18 17:39:46 -07001138 void updateCandidatesVisibility(boolean shown) {
1139 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1140 if (mCandidatesVisibility != vis) {
1141 mCandidatesFrame.setVisibility(vis);
1142 mCandidatesVisibility = vis;
1143 }
1144 }
1145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 /**
1147 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1148 * or {@link View#GONE View.GONE}) of the candidates view when it is not
The Android Open Source Project10592532009-03-18 17:39:46 -07001149 * shown. The default implementation returns GONE when
1150 * {@link #isExtractViewShown} returns true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 * otherwise VISIBLE. Be careful if you change this to return GONE in
1152 * other situations -- if showing or hiding the candidates view causes
1153 * your window to resize, this can cause temporary drawing artifacts as
1154 * the resize takes place.
1155 */
1156 public int getCandidatesHiddenVisibility() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001157 return isExtractViewShown() ? View.GONE : View.INVISIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 }
1159
1160 public void showStatusIcon(int iconResId) {
1161 mStatusIcon = iconResId;
1162 mImm.showStatusIcon(mToken, getPackageName(), iconResId);
1163 }
1164
1165 public void hideStatusIcon() {
1166 mStatusIcon = 0;
1167 mImm.hideStatusIcon(mToken);
1168 }
1169
1170 /**
1171 * Force switch to a new input method, as identified by <var>id</var>. This
1172 * input method will be destroyed, and the requested one started on the
1173 * current input field.
1174 *
1175 * @param id Unique identifier of the new input method ot start.
1176 */
1177 public void switchInputMethod(String id) {
1178 mImm.setInputMethod(mToken, id);
1179 }
1180
1181 public void setExtractView(View view) {
1182 mExtractFrame.removeAllViews();
1183 mExtractFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001184 ViewGroup.LayoutParams.MATCH_PARENT,
1185 ViewGroup.LayoutParams.MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 mExtractView = view;
1187 if (view != null) {
1188 mExtractEditText = (ExtractEditText)view.findViewById(
1189 com.android.internal.R.id.inputExtractEditText);
1190 mExtractEditText.setIME(this);
1191 mExtractAction = (Button)view.findViewById(
1192 com.android.internal.R.id.inputExtractAction);
1193 if (mExtractAction != null) {
1194 mExtractAccessories = (ViewGroup)view.findViewById(
1195 com.android.internal.R.id.inputExtractAccessories);
1196 }
1197 startExtractingText(false);
1198 } else {
1199 mExtractEditText = null;
1200 mExtractAccessories = null;
1201 mExtractAction = null;
1202 }
1203 }
1204
1205 /**
1206 * Replaces the current candidates view with a new one. You only need to
1207 * call this when dynamically changing the view; normally, you should
1208 * implement {@link #onCreateCandidatesView()} and create your view when
1209 * first needed by the input method.
1210 */
1211 public void setCandidatesView(View view) {
1212 mCandidatesFrame.removeAllViews();
1213 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001214 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215 ViewGroup.LayoutParams.WRAP_CONTENT));
1216 }
1217
1218 /**
1219 * Replaces the current input view with a new one. You only need to
1220 * call this when dynamically changing the view; normally, you should
1221 * implement {@link #onCreateInputView()} and create your view when
1222 * first needed by the input method.
1223 */
1224 public void setInputView(View view) {
1225 mInputFrame.removeAllViews();
1226 mInputFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001227 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 ViewGroup.LayoutParams.WRAP_CONTENT));
1229 mInputView = view;
1230 }
1231
1232 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 * Called by the framework to create the layout for showing extacted text.
1234 * Only called when in fullscreen mode. The returned view hierarchy must
1235 * have an {@link ExtractEditText} whose ID is
1236 * {@link android.R.id#inputExtractEditText}.
1237 */
1238 public View onCreateExtractTextView() {
1239 return mInflater.inflate(
1240 com.android.internal.R.layout.input_method_extract_view, null);
1241 }
1242
1243 /**
1244 * Create and return the view hierarchy used to show candidates. This will
1245 * be called once, when the candidates are first displayed. You can return
1246 * null to have no candidates view; the default implementation returns null.
1247 *
1248 * <p>To control when the candidates view is displayed, use
1249 * {@link #setCandidatesViewShown(boolean)}.
1250 * To change the candidates view after the first one is created by this
1251 * function, use {@link #setCandidatesView(View)}.
1252 */
1253 public View onCreateCandidatesView() {
1254 return null;
1255 }
1256
1257 /**
1258 * Create and return the view hierarchy used for the input area (such as
1259 * a soft keyboard). This will be called once, when the input area is
1260 * first displayed. You can return null to have no input area; the default
1261 * implementation returns null.
1262 *
1263 * <p>To control when the input view is displayed, implement
1264 * {@link #onEvaluateInputViewShown()}.
1265 * To change the input view after the first one is created by this
1266 * function, use {@link #setInputView(View)}.
1267 */
1268 public View onCreateInputView() {
1269 return null;
1270 }
1271
1272 /**
1273 * Called when the input view is being shown and input has started on
1274 * a new editor. This will always be called after {@link #onStartInput},
1275 * allowing you to do your general setup there and just view-specific
1276 * setup here. You are guaranteed that {@link #onCreateInputView()} will
1277 * have been called some time before this function is called.
1278 *
1279 * @param info Description of the type of text being edited.
1280 * @param restarting Set to true if we are restarting input on the
1281 * same text field as before.
1282 */
1283 public void onStartInputView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001284 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 }
1286
1287 /**
1288 * Called when the input view is being hidden from the user. This will
1289 * be called either prior to hiding the window, or prior to switching to
1290 * another target for editing.
1291 *
1292 * <p>The default
1293 * implementation uses the InputConnection to clear any active composing
1294 * text; you can override this (not calling the base class implementation)
1295 * to perform whatever behavior you would like.
1296 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001297 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 * called immediately after.
1299 */
1300 public void onFinishInputView(boolean finishingInput) {
1301 if (!finishingInput) {
1302 InputConnection ic = getCurrentInputConnection();
1303 if (ic != null) {
1304 ic.finishComposingText();
1305 }
1306 }
1307 }
1308
1309 /**
1310 * Called when only the candidates view has been shown for showing
1311 * processing as the user enters text through a hard keyboard.
1312 * This will always be called after {@link #onStartInput},
1313 * allowing you to do your general setup there and just view-specific
1314 * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
1315 * will have been called some time before this function is called.
1316 *
1317 * <p>Note that this will <em>not</em> be called when the input method
1318 * is running in full editing mode, and thus receiving
1319 * {@link #onStartInputView} to initiate that operation. This is only
1320 * for the case when candidates are being shown while the input method
1321 * editor is hidden but wants to show its candidates UI as text is
1322 * entered through some other mechanism.
1323 *
1324 * @param info Description of the type of text being edited.
1325 * @param restarting Set to true if we are restarting input on the
1326 * same text field as before.
1327 */
1328 public void onStartCandidatesView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001329 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 }
1331
1332 /**
1333 * Called when the candidates view is being hidden from the user. This will
1334 * be called either prior to hiding the window, or prior to switching to
1335 * another target for editing.
1336 *
1337 * <p>The default
1338 * implementation uses the InputConnection to clear any active composing
1339 * text; you can override this (not calling the base class implementation)
1340 * to perform whatever behavior you would like.
1341 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001342 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 * called immediately after.
1344 */
1345 public void onFinishCandidatesView(boolean finishingInput) {
1346 if (!finishingInput) {
1347 InputConnection ic = getCurrentInputConnection();
1348 if (ic != null) {
1349 ic.finishComposingText();
1350 }
1351 }
1352 }
1353
1354 /**
1355 * The system has decided that it may be time to show your input method.
1356 * This is called due to a corresponding call to your
The Android Open Source Project4df24232009-03-05 14:34:35 -08001357 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 * method. The default implementation uses
1359 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
1360 * and the current configuration to decide whether the input view should
1361 * be shown at this point.
1362 *
1363 * @param flags Provides additional information about the show request,
The Android Open Source Project4df24232009-03-05 14:34:35 -08001364 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 * @param configChange This is true if we are re-showing due to a
1366 * configuration change.
1367 * @return Returns true to indicate that the window should be shown.
1368 */
1369 public boolean onShowInputRequested(int flags, boolean configChange) {
1370 if (!onEvaluateInputViewShown()) {
1371 return false;
1372 }
1373 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
1374 if (!configChange && onEvaluateFullscreenMode()) {
1375 // Don't show if this is not explicitly requested by the user and
1376 // the input method is fullscreen. That would be too disruptive.
1377 // However, we skip this change for a config change, since if
1378 // the IME is already shown we do want to go into fullscreen
1379 // mode at this point.
1380 return false;
1381 }
1382 Configuration config = getResources().getConfiguration();
1383 if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
1384 // And if the device has a hard keyboard, even if it is
1385 // currently hidden, don't show the input method implicitly.
1386 // These kinds of devices don't need it that much.
1387 return false;
1388 }
1389 }
1390 if ((flags&InputMethod.SHOW_FORCED) != 0) {
1391 mShowInputForced = true;
1392 }
1393 return true;
1394 }
1395
1396 public void showWindow(boolean showInput) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07001397 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 + " mShowInputRequested=" + mShowInputRequested
1399 + " mWindowAdded=" + mWindowAdded
1400 + " mWindowCreated=" + mWindowCreated
1401 + " mWindowVisible=" + mWindowVisible
1402 + " mInputStarted=" + mInputStarted);
The Android Open Source Project10592532009-03-18 17:39:46 -07001403
1404 if (mInShowWindow) {
1405 Log.w(TAG, "Re-entrance in to showWindow");
1406 return;
1407 }
1408
1409 try {
1410 mWindowWasVisible = mWindowVisible;
1411 mInShowWindow = true;
1412 showWindowInner(showInput);
1413 } finally {
1414 mWindowWasVisible = true;
1415 mInShowWindow = false;
1416 }
1417 }
satok06487a52010-10-29 11:37:18 +09001418
The Android Open Source Project10592532009-03-18 17:39:46 -07001419 void showWindowInner(boolean showInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 boolean doShowInput = false;
1421 boolean wasVisible = mWindowVisible;
1422 mWindowVisible = true;
1423 if (!mShowInputRequested) {
1424 if (mInputStarted) {
1425 if (showInput) {
1426 doShowInput = true;
1427 mShowInputRequested = true;
1428 }
1429 }
1430 } else {
1431 showInput = true;
1432 }
satok06487a52010-10-29 11:37:18 +09001433
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001434 if (DEBUG) Log.v(TAG, "showWindow: updating UI");
1435 initialize();
1436 updateFullscreenMode();
1437 updateInputViewShown();
1438
1439 if (!mWindowAdded || !mWindowCreated) {
1440 mWindowAdded = true;
1441 mWindowCreated = true;
1442 initialize();
1443 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
1444 View v = onCreateCandidatesView();
1445 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
1446 if (v != null) {
1447 setCandidatesView(v);
1448 }
1449 }
1450 if (mShowInputRequested) {
1451 if (!mInputViewStarted) {
1452 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1453 mInputViewStarted = true;
1454 onStartInputView(mInputEditorInfo, false);
1455 }
1456 } else if (!mCandidatesViewStarted) {
1457 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1458 mCandidatesViewStarted = true;
1459 onStartCandidatesView(mInputEditorInfo, false);
1460 }
1461
1462 if (doShowInput) {
1463 startExtractingText(false);
1464 }
satok06487a52010-10-29 11:37:18 +09001465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 if (!wasVisible) {
1467 if (DEBUG) Log.v(TAG, "showWindow: showing!");
Joe Onorato857fd9b2011-01-27 15:08:35 -08001468 mImm.setImeWindowStatus(mToken, IME_ACTIVE, mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 onWindowShown();
1470 mWindow.show();
1471 }
1472 }
satok06487a52010-10-29 11:37:18 +09001473
satokf17db9f2011-09-14 18:55:58 +09001474 private void finishViews() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 if (mInputViewStarted) {
1476 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1477 onFinishInputView(false);
1478 } else if (mCandidatesViewStarted) {
1479 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1480 onFinishCandidatesView(false);
1481 }
1482 mInputViewStarted = false;
1483 mCandidatesViewStarted = false;
satokf17db9f2011-09-14 18:55:58 +09001484 }
1485
satok2f913d92012-05-10 01:48:03 +09001486 private void doHideWindow() {
1487 mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
1488 hideWindow();
1489 }
1490
satokf17db9f2011-09-14 18:55:58 +09001491 public void hideWindow() {
1492 finishViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001493 if (mWindowVisible) {
1494 mWindow.hide();
1495 mWindowVisible = false;
1496 onWindowHidden();
The Android Open Source Project10592532009-03-18 17:39:46 -07001497 mWindowWasVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001498 }
1499 }
satok06487a52010-10-29 11:37:18 +09001500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 /**
1502 * Called when the input method window has been shown to the user, after
1503 * previously not being visible. This is done after all of the UI setup
1504 * for the window has occurred (creating its views etc).
1505 */
1506 public void onWindowShown() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001507 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 }
1509
1510 /**
1511 * Called when the input method window has been hidden from the user,
1512 * after previously being visible.
1513 */
1514 public void onWindowHidden() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001515 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001516 }
1517
1518 /**
1519 * Called when a new client has bound to the input method. This
1520 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
1521 * and {@link #onFinishInput()} calls as the user navigates through its
1522 * UI. Upon this call you know that {@link #getCurrentInputBinding}
1523 * and {@link #getCurrentInputConnection} return valid objects.
1524 */
1525 public void onBindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001526 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001527 }
1528
1529 /**
1530 * Called when the previous bound client is no longer associated
1531 * with the input method. After returning {@link #getCurrentInputBinding}
1532 * and {@link #getCurrentInputConnection} will no longer return
1533 * valid objects.
1534 */
1535 public void onUnbindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001536 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 }
1538
1539 /**
1540 * Called to inform the input method that text input has started in an
1541 * editor. You should use this callback to initialize the state of your
1542 * input to match the state of the editor given to it.
1543 *
1544 * @param attribute The attributes of the editor that input is starting
1545 * in.
1546 * @param restarting Set to true if input is restarting in the same
1547 * editor such as because the application has changed the text in
1548 * the editor. Otherwise will be false, indicating this is a new
1549 * session with the editor.
1550 */
1551 public void onStartInput(EditorInfo attribute, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001552 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001553 }
1554
1555 void doFinishInput() {
1556 if (mInputViewStarted) {
1557 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1558 onFinishInputView(true);
1559 } else if (mCandidatesViewStarted) {
1560 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1561 onFinishCandidatesView(true);
1562 }
1563 mInputViewStarted = false;
1564 mCandidatesViewStarted = false;
1565 if (mInputStarted) {
1566 if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
1567 onFinishInput();
1568 }
1569 mInputStarted = false;
1570 mStartedInputConnection = null;
1571 mCurCompletions = null;
1572 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001573
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001574 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
1575 if (!restarting) {
1576 doFinishInput();
1577 }
1578 mInputStarted = true;
1579 mStartedInputConnection = ic;
1580 mInputEditorInfo = attribute;
1581 initialize();
1582 if (DEBUG) Log.v(TAG, "CALL: onStartInput");
1583 onStartInput(attribute, restarting);
1584 if (mWindowVisible) {
1585 if (mShowInputRequested) {
1586 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1587 mInputViewStarted = true;
1588 onStartInputView(mInputEditorInfo, restarting);
1589 startExtractingText(true);
1590 } else if (mCandidatesVisibility == View.VISIBLE) {
1591 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1592 mCandidatesViewStarted = true;
1593 onStartCandidatesView(mInputEditorInfo, restarting);
1594 }
1595 }
1596 }
1597
1598 /**
1599 * Called to inform the input method that text input has finished in
1600 * the last editor. At this point there may be a call to
1601 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
1602 * new editor, or the input method may be left idle. This method is
1603 * <em>not</em> called when input restarts in the same editor.
1604 *
1605 * <p>The default
1606 * implementation uses the InputConnection to clear any active composing
1607 * text; you can override this (not calling the base class implementation)
1608 * to perform whatever behavior you would like.
1609 */
1610 public void onFinishInput() {
1611 InputConnection ic = getCurrentInputConnection();
1612 if (ic != null) {
1613 ic.finishComposingText();
1614 }
1615 }
1616
1617 /**
1618 * Called when the application has reported auto-completion candidates that
1619 * it would like to have the input method displayed. Typically these are
1620 * only used when an input method is running in full-screen mode, since
1621 * otherwise the user can see and interact with the pop-up window of
1622 * completions shown by the application.
1623 *
1624 * <p>The default implementation here does nothing.
1625 */
1626 public void onDisplayCompletions(CompletionInfo[] completions) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001627 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 }
1629
1630 /**
1631 * Called when the application has reported new extracted text to be shown
1632 * due to changes in its current text state. The default implementation
1633 * here places the new text in the extract edit text, when the input
1634 * method is running in fullscreen mode.
1635 */
1636 public void onUpdateExtractedText(int token, ExtractedText text) {
1637 if (mExtractedToken != token) {
1638 return;
1639 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001640 if (text != null) {
1641 if (mExtractEditText != null) {
1642 mExtractedText = text;
1643 mExtractEditText.setExtractedText(text);
1644 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645 }
1646 }
1647
1648 /**
1649 * Called when the application has reported a new selection region of
1650 * the text. This is called whether or not the input method has requested
1651 * extracted text updates, although if so it will not receive this call
1652 * if the extracted text has changed as well.
1653 *
1654 * <p>The default implementation takes care of updating the cursor in
1655 * the extract text, if it is being shown.
1656 */
1657 public void onUpdateSelection(int oldSelStart, int oldSelEnd,
1658 int newSelStart, int newSelEnd,
1659 int candidatesStart, int candidatesEnd) {
1660 final ExtractEditText eet = mExtractEditText;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001661 if (eet != null && isFullscreenMode() && mExtractedText != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001662 final int off = mExtractedText.startOffset;
1663 eet.startInternalChanges();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001664 newSelStart -= off;
1665 newSelEnd -= off;
1666 final int len = eet.getText().length();
1667 if (newSelStart < 0) newSelStart = 0;
1668 else if (newSelStart > len) newSelStart = len;
1669 if (newSelEnd < 0) newSelEnd = 0;
1670 else if (newSelEnd > len) newSelEnd = len;
1671 eet.setSelection(newSelStart, newSelEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001672 eet.finishInternalChanges();
1673 }
1674 }
1675
1676 /**
satok863fcd62011-06-21 17:38:02 +09001677 * Called when the user tapped or clicked a text view.
1678 * IMEs can't rely on this method being called because this was not part of the original IME
1679 * protocol, so applications with custom text editing written before this method appeared will
1680 * not call to inform the IME of this interaction.
1681 * @param focusChanged true if the user changed the focused view by this click.
1682 */
1683 public void onViewClicked(boolean focusChanged) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001684 // Intentionally empty
satok863fcd62011-06-21 17:38:02 +09001685 }
1686
1687 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 * Called when the application has reported a new location of its text
1689 * cursor. This is only called if explicitly requested by the input method.
1690 * The default implementation does nothing.
1691 */
1692 public void onUpdateCursor(Rect newCursor) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001693 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001694 }
1695
1696 /**
1697 * Close this input method's soft input area, removing it from the display.
1698 * The input method will continue running, but the user can no longer use
1699 * it to generate input by touching the screen.
1700 * @param flags Provides additional operating flags. Currently may be
1701 * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
1702 * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
1703 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001704 public void requestHideSelf(int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001705 mImm.hideSoftInputFromInputMethod(mToken, flags);
1706 }
1707
1708 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -08001709 * Show the input method. This is a call back to the
1710 * IMF to handle showing the input method.
The Android Open Source Project4df24232009-03-05 14:34:35 -08001711 * @param flags Provides additional operating flags. Currently may be
1712 * 0 or have the {@link InputMethodManager#SHOW_FORCED
1713 * InputMethodManager.} bit set.
1714 */
1715 private void requestShowSelf(int flags) {
1716 mImm.showSoftInputFromInputMethod(mToken, flags);
1717 }
1718
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001719 private boolean handleBack(boolean doIt) {
1720 if (mShowInputRequested) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001721 if (isExtractViewShown() && mExtractView instanceof ExtractEditLayout) {
1722 ExtractEditLayout extractEditLayout = (ExtractEditLayout) mExtractView;
1723 if (extractEditLayout.isActionModeStarted()) {
1724 if (doIt) extractEditLayout.finishActionMode();
1725 return true;
1726 }
1727 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001728 // If the soft input area is shown, back closes it and we
1729 // consume the back key.
1730 if (doIt) requestHideSelf(0);
1731 return true;
1732 } else if (mWindowVisible) {
1733 if (mCandidatesVisibility == View.VISIBLE) {
1734 // If we are showing candidates even if no input area, then
1735 // hide them.
1736 if (doIt) setCandidatesViewShown(false);
1737 } else {
1738 // If we have the window visible for some other reason --
1739 // most likely to show candidates -- then just get rid
1740 // of it. This really shouldn't happen, but just in case...
satok2f913d92012-05-10 01:48:03 +09001741 if (doIt) doHideWindow();
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001742 }
1743 return true;
1744 }
1745 return false;
1746 }
1747
The Android Open Source Project4df24232009-03-05 14:34:35 -08001748 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001749 * Override this to intercept key down events before they are processed by the
Quddus Chongee71b1f2012-04-12 11:49:37 -07001750 * application. If you return true, the application will not
1751 * process the event itself. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 * will occur as if the IME had not seen the event at all.
1753 *
1754 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001755 * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
1756 * possibly hide it when the key goes up (if not canceled or long pressed). In
1757 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758 * events to move the cursor in the extracted text view, not allowing
1759 * them to perform navigation in the underlying application.
1760 */
1761 public boolean onKeyDown(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001762 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
1763 if (handleBack(false)) {
1764 event.startTracking();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001765 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001767 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001768 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 return doMovementKey(keyCode, event, MOVEMENT_DOWN);
1770 }
1771
1772 /**
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001773 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
1774 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
1775 * the event).
1776 */
1777 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1778 return false;
1779 }
1780
1781 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 * Override this to intercept special key multiple events before they are
1783 * processed by the
1784 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07001785 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001786 * will occur as if the IME had not seen the event at all.
1787 *
1788 * <p>The default implementation always returns false, except when
1789 * in fullscreen mode, where it will consume DPAD movement
1790 * events to move the cursor in the extracted text view, not allowing
1791 * them to perform navigation in the underlying application.
1792 */
1793 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1794 return doMovementKey(keyCode, event, count);
1795 }
1796
1797 /**
1798 * Override this to intercept key up events before they are processed by the
1799 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07001800 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 * will occur as if the IME had not seen the event at all.
1802 *
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001803 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
1804 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
1805 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001806 * events to move the cursor in the extracted text view, not allowing
1807 * them to perform navigation in the underlying application.
1808 */
1809 public boolean onKeyUp(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001810 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isTracking()
1811 && !event.isCanceled()) {
1812 return handleBack(true);
1813 }
1814
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 return doMovementKey(keyCode, event, MOVEMENT_UP);
1816 }
1817
Victoria Leaseb38070c2012-08-24 13:46:02 -07001818 /**
1819 * Override this to intercept trackball motion events before they are
1820 * processed by the application.
1821 * If you return true, the application will not itself process the event.
1822 * If you return false, the normal application processing will occur as if
1823 * the IME had not seen the event at all.
1824 */
satokab751aa2010-09-14 19:17:36 +09001825 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826 public boolean onTrackballEvent(MotionEvent event) {
Victoria Leaseb38070c2012-08-24 13:46:02 -07001827 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
1828 return false;
1829 }
1830
1831 /**
1832 * Override this to intercept generic motion events before they are
1833 * processed by the application.
1834 * If you return true, the application will not itself process the event.
1835 * If you return false, the normal application processing will occur as if
1836 * the IME had not seen the event at all.
1837 */
1838 @Override
1839 public boolean onGenericMotionEvent(MotionEvent event) {
1840 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 return false;
1842 }
1843
1844 public void onAppPrivateCommand(String action, Bundle data) {
1845 }
1846
The Android Open Source Project4df24232009-03-05 14:34:35 -08001847 /**
1848 * Handle a request by the system to toggle the soft input area.
1849 */
1850 private void onToggleSoftInput(int showFlags, int hideFlags) {
1851 if (DEBUG) Log.v(TAG, "toggleSoftInput()");
1852 if (isInputViewShown()) {
1853 requestHideSelf(hideFlags);
1854 } else {
1855 requestShowSelf(showFlags);
1856 }
1857 }
1858
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 static final int MOVEMENT_DOWN = -1;
1860 static final int MOVEMENT_UP = -2;
1861
1862 void reportExtractedMovement(int keyCode, int count) {
1863 int dx = 0, dy = 0;
1864 switch (keyCode) {
1865 case KeyEvent.KEYCODE_DPAD_LEFT:
1866 dx = -count;
1867 break;
1868 case KeyEvent.KEYCODE_DPAD_RIGHT:
1869 dx = count;
1870 break;
1871 case KeyEvent.KEYCODE_DPAD_UP:
1872 dy = -count;
1873 break;
1874 case KeyEvent.KEYCODE_DPAD_DOWN:
1875 dy = count;
1876 break;
1877 }
satokab751aa2010-09-14 19:17:36 +09001878 onExtractedCursorMovement(dx, dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001879 }
1880
1881 boolean doMovementKey(int keyCode, KeyEvent event, int count) {
1882 final ExtractEditText eet = mExtractEditText;
The Android Open Source Project10592532009-03-18 17:39:46 -07001883 if (isExtractViewShown() && isInputViewShown() && eet != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001884 // If we are in fullscreen mode, the cursor will move around
1885 // the extract edit text, but should NOT cause focus to move
1886 // to other fields.
1887 MovementMethod movement = eet.getMovementMethod();
1888 Layout layout = eet.getLayout();
1889 if (movement != null && layout != null) {
1890 // We want our own movement method to handle the key, so the
1891 // cursor will properly move in our own word wrapping.
1892 if (count == MOVEMENT_DOWN) {
1893 if (movement.onKeyDown(eet,
1894 (Spannable)eet.getText(), keyCode, event)) {
1895 reportExtractedMovement(keyCode, 1);
1896 return true;
1897 }
1898 } else if (count == MOVEMENT_UP) {
1899 if (movement.onKeyUp(eet,
1900 (Spannable)eet.getText(), keyCode, event)) {
1901 return true;
1902 }
1903 } else {
1904 if (movement.onKeyOther(eet, (Spannable)eet.getText(), event)) {
1905 reportExtractedMovement(keyCode, count);
1906 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001907 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001908 if (movement.onKeyDown(eet,
1909 (Spannable)eet.getText(), keyCode, down)) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001910 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001911 movement.onKeyUp(eet,
1912 (Spannable)eet.getText(), keyCode, up);
1913 while (--count > 0) {
1914 movement.onKeyDown(eet,
1915 (Spannable)eet.getText(), keyCode, down);
1916 movement.onKeyUp(eet,
1917 (Spannable)eet.getText(), keyCode, up);
1918 }
1919 reportExtractedMovement(keyCode, count);
1920 }
1921 }
1922 }
1923 }
1924 // Regardless of whether the movement method handled the key,
1925 // we never allow DPAD navigation to the application.
1926 switch (keyCode) {
1927 case KeyEvent.KEYCODE_DPAD_LEFT:
1928 case KeyEvent.KEYCODE_DPAD_RIGHT:
1929 case KeyEvent.KEYCODE_DPAD_UP:
1930 case KeyEvent.KEYCODE_DPAD_DOWN:
1931 return true;
1932 }
1933 }
1934
1935 return false;
1936 }
1937
1938 /**
1939 * Send the given key event code (as defined by {@link KeyEvent}) to the
1940 * current input connection is a key down + key up event pair. The sent
1941 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
1942 * set, so that the recipient can identify them as coming from a software
1943 * input method, and
1944 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
1945 * that they don't impact the current touch mode of the UI.
1946 *
Jean Chalard405bc512012-05-29 19:12:34 +09001947 * <p>Note that it's discouraged to send such key events in normal operation;
1948 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
1949 * text fields, or for non-rich input methods. A reasonably capable software
1950 * input method should use the
1951 * {@link android.view.inputmethod.InputConnection#commitText} family of methods
1952 * to send text to an application, rather than sending key events.</p>
1953 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001954 * @param keyEventCode The raw key code to send, as defined by
1955 * {@link KeyEvent}.
1956 */
1957 public void sendDownUpKeyEvents(int keyEventCode) {
1958 InputConnection ic = getCurrentInputConnection();
1959 if (ic == null) return;
1960 long eventTime = SystemClock.uptimeMillis();
1961 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
Jeff Brown6b53e8d2010-11-10 16:03:06 -08001962 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
Tadashi G. Takaokacb95cd62012-10-26 17:20:59 +09001964 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -08001965 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001966 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
1967 }
1968
1969 /**
1970 * Ask the input target to execute its default action via
1971 * {@link InputConnection#performEditorAction
1972 * InputConnection.performEditorAction()}.
1973 *
1974 * @param fromEnterKey If true, this will be executed as if the user had
1975 * pressed an enter key on the keyboard, that is it will <em>not</em>
1976 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
1977 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
1978 * sent regardless of how the editor has set that flag.
1979 *
1980 * @return Returns a boolean indicating whether an action has been sent.
1981 * If false, either the editor did not specify a default action or it
1982 * does not want an action from the enter key. If true, the action was
1983 * sent (or there was no input connection at all).
1984 */
1985 public boolean sendDefaultEditorAction(boolean fromEnterKey) {
1986 EditorInfo ei = getCurrentInputEditorInfo();
1987 if (ei != null &&
1988 (!fromEnterKey || (ei.imeOptions &
1989 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
1990 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
1991 EditorInfo.IME_ACTION_NONE) {
1992 // If the enter key was pressed, and the editor has a default
1993 // action associated with pressing enter, then send it that
1994 // explicit action instead of the key event.
1995 InputConnection ic = getCurrentInputConnection();
1996 if (ic != null) {
1997 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
1998 }
1999 return true;
2000 }
2001
2002 return false;
2003 }
2004
2005 /**
2006 * Send the given UTF-16 character to the current input connection. Most
2007 * characters will be delivered simply by calling
2008 * {@link InputConnection#commitText InputConnection.commitText()} with
2009 * the character; some, however, may be handled different. In particular,
2010 * the enter character ('\n') will either be delivered as an action code
Jean Chalard405bc512012-05-29 19:12:34 +09002011 * or a raw key event, as appropriate. Consider this as a convenience
2012 * method for IMEs that do not have a full implementation of actions; a
2013 * fully complying IME will decide of the right action for each event and
2014 * will likely never call this method except maybe to handle events coming
2015 * from an actual hardware keyboard.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016 *
2017 * @param charCode The UTF-16 character code to send.
2018 */
2019 public void sendKeyChar(char charCode) {
2020 switch (charCode) {
2021 case '\n': // Apps may be listening to an enter key to perform an action
2022 if (!sendDefaultEditorAction(true)) {
2023 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2024 }
2025 break;
2026 default:
2027 // Make sure that digits go through any text watcher on the client side.
2028 if (charCode >= '0' && charCode <= '9') {
2029 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2030 } else {
2031 InputConnection ic = getCurrentInputConnection();
2032 if (ic != null) {
2033 ic.commitText(String.valueOf((char) charCode), 1);
2034 }
2035 }
2036 break;
2037 }
2038 }
2039
2040 /**
2041 * This is called when the user has moved the cursor in the extracted
2042 * text view, when running in fullsreen mode. The default implementation
2043 * performs the corresponding selection change on the underlying text
2044 * editor.
2045 */
2046 public void onExtractedSelectionChanged(int start, int end) {
2047 InputConnection conn = getCurrentInputConnection();
2048 if (conn != null) {
2049 conn.setSelection(start, end);
2050 }
2051 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002052
2053 /**
2054 * @hide
2055 */
2056 public void onExtractedDeleteText(int start, int end) {
2057 InputConnection conn = getCurrentInputConnection();
2058 if (conn != null) {
2059 conn.setSelection(start, start);
2060 conn.deleteSurroundingText(0, end-start);
2061 }
2062 }
2063
2064 /**
2065 * @hide
2066 */
2067 public void onExtractedReplaceText(int start, int end, CharSequence text) {
2068 InputConnection conn = getCurrentInputConnection();
2069 if (conn != null) {
2070 conn.setComposingRegion(start, end);
2071 conn.commitText(text, 1);
2072 }
2073 }
2074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002075 /**
Gilles Debunnee300be92011-12-06 10:15:56 -08002076 * @hide
2077 */
2078 public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2079 InputConnection conn = getCurrentInputConnection();
2080 if (conn != null) {
2081 if (!conn.setSelection(start, end)) return;
2082 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2083 if (text instanceof Spannable) {
2084 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2085 conn.setComposingRegion(start, end);
2086 conn.commitText(text, 1);
2087 }
2088 }
2089 }
2090
2091 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 * This is called when the user has clicked on the extracted text view,
2093 * when running in fullscreen mode. The default implementation hides
2094 * the candidates view when this happens, but only if the extracted text
2095 * editor has a vertical scroll bar because its text doesn't fit.
2096 * Re-implement this to provide whatever behavior you want.
2097 */
2098 public void onExtractedTextClicked() {
2099 if (mExtractEditText == null) {
2100 return;
2101 }
2102 if (mExtractEditText.hasVerticalScrollBar()) {
2103 setCandidatesViewShown(false);
2104 }
2105 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002107 /**
2108 * This is called when the user has performed a cursor movement in the
2109 * extracted text view, when it is running in fullscreen mode. The default
2110 * implementation hides the candidates view when a vertical movement
2111 * happens, but only if the extracted text editor has a vertical scroll bar
2112 * because its text doesn't fit.
2113 * Re-implement this to provide whatever behavior you want.
2114 * @param dx The amount of cursor movement in the x dimension.
2115 * @param dy The amount of cursor movement in the y dimension.
2116 */
2117 public void onExtractedCursorMovement(int dx, int dy) {
2118 if (mExtractEditText == null || dy == 0) {
2119 return;
2120 }
2121 if (mExtractEditText.hasVerticalScrollBar()) {
2122 setCandidatesViewShown(false);
2123 }
2124 }
2125
2126 /**
2127 * This is called when the user has selected a context menu item from the
2128 * extracted text view, when running in fullscreen mode. The default
2129 * implementation sends this action to the current InputConnection's
2130 * {@link InputConnection#performContextMenuAction(int)}, for it
2131 * to be processed in underlying "real" editor. Re-implement this to
2132 * provide whatever behavior you want.
2133 */
2134 public boolean onExtractTextContextMenuItem(int id) {
2135 InputConnection ic = getCurrentInputConnection();
2136 if (ic != null) {
2137 ic.performContextMenuAction(id);
2138 }
2139 return true;
2140 }
2141
2142 /**
2143 * Return text that can be used as a button label for the given
2144 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
2145 * if there is no action requested. Note that there is no guarantee that
2146 * the returned text will be relatively short, so you probably do not
2147 * want to use it as text on a soft keyboard key label.
2148 *
2149 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
2150 *
2151 * @return Returns a label to use, or null if there is no action.
2152 */
2153 public CharSequence getTextForImeAction(int imeOptions) {
2154 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2155 case EditorInfo.IME_ACTION_NONE:
2156 return null;
2157 case EditorInfo.IME_ACTION_GO:
2158 return getText(com.android.internal.R.string.ime_action_go);
2159 case EditorInfo.IME_ACTION_SEARCH:
2160 return getText(com.android.internal.R.string.ime_action_search);
2161 case EditorInfo.IME_ACTION_SEND:
2162 return getText(com.android.internal.R.string.ime_action_send);
2163 case EditorInfo.IME_ACTION_NEXT:
2164 return getText(com.android.internal.R.string.ime_action_next);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002165 case EditorInfo.IME_ACTION_DONE:
2166 return getText(com.android.internal.R.string.ime_action_done);
Dianne Hackborndea3ef72010-10-28 14:24:22 -07002167 case EditorInfo.IME_ACTION_PREVIOUS:
2168 return getText(com.android.internal.R.string.ime_action_previous);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002169 default:
2170 return getText(com.android.internal.R.string.ime_action_default);
2171 }
2172 }
2173
2174 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07002175 * Called when the fullscreen-mode extracting editor info has changed,
2176 * to determine whether the extracting (extract text and candidates) portion
2177 * of the UI should be shown. The standard implementation hides or shows
2178 * the extract area depending on whether it makes sense for the
2179 * current editor. In particular, a {@link InputType#TYPE_NULL}
2180 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
2181 * turn off the extract area since there is no text to be shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002182 */
The Android Open Source Project10592532009-03-18 17:39:46 -07002183 public void onUpdateExtractingVisibility(EditorInfo ei) {
2184 if (ei.inputType == InputType.TYPE_NULL ||
2185 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
2186 // No reason to show extract UI!
2187 setExtractViewShown(false);
2188 return;
2189 }
2190
2191 setExtractViewShown(true);
2192 }
2193
2194 /**
2195 * Called when the fullscreen-mode extracting editor info has changed,
2196 * to update the state of its UI such as the action buttons shown.
2197 * You do not need to deal with this if you are using the standard
2198 * full screen extract UI. If replacing it, you will need to re-implement
2199 * this to put the appropriate action button in your own UI and handle it,
2200 * and perform any other changes.
2201 *
2202 * <p>The standard implementation turns on or off its accessory area
2203 * depending on whether there is an action button, and hides or shows
2204 * the entire extract area depending on whether it makes sense for the
2205 * current editor. In particular, a {@link InputType#TYPE_NULL} or
2206 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
2207 * extract area since there is no text to be shown.
2208 */
2209 public void onUpdateExtractingViews(EditorInfo ei) {
2210 if (!isExtractViewShown()) {
2211 return;
2212 }
2213
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002214 if (mExtractAccessories == null) {
2215 return;
2216 }
2217 final boolean hasAction = ei.actionLabel != null || (
2218 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
The Android Open Source Project10592532009-03-18 17:39:46 -07002219 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
2220 ei.inputType != InputType.TYPE_NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002221 if (hasAction) {
2222 mExtractAccessories.setVisibility(View.VISIBLE);
Steve Kondik59eb6912009-09-07 22:53:34 -04002223 if (mExtractAction != null) {
2224 if (ei.actionLabel != null) {
2225 mExtractAction.setText(ei.actionLabel);
2226 } else {
2227 mExtractAction.setText(getTextForImeAction(ei.imeOptions));
2228 }
2229 mExtractAction.setOnClickListener(mActionClickListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002230 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002231 } else {
2232 mExtractAccessories.setVisibility(View.GONE);
Steve Kondik59eb6912009-09-07 22:53:34 -04002233 if (mExtractAction != null) {
2234 mExtractAction.setOnClickListener(null);
2235 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002236 }
2237 }
2238
2239 /**
2240 * This is called when, while currently displayed in extract mode, the
2241 * current input target changes. The default implementation will
2242 * auto-hide the IME if the new target is not a full editor, since this
Ken Wakasaf76a50c2012-03-09 19:56:35 +09002243 * can be a confusing experience for the user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002244 */
2245 public void onExtractingInputChanged(EditorInfo ei) {
2246 if (ei.inputType == InputType.TYPE_NULL) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002247 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002248 }
2249 }
2250
2251 void startExtractingText(boolean inputChanged) {
2252 final ExtractEditText eet = mExtractEditText;
2253 if (eet != null && getCurrentInputStarted()
2254 && isFullscreenMode()) {
2255 mExtractedToken++;
2256 ExtractedTextRequest req = new ExtractedTextRequest();
2257 req.token = mExtractedToken;
2258 req.flags = InputConnection.GET_TEXT_WITH_STYLES;
2259 req.hintMaxLines = 10;
2260 req.hintMaxChars = 10000;
Amith Yamasaniba4d93f2009-08-19 18:27:56 -07002261 InputConnection ic = getCurrentInputConnection();
2262 mExtractedText = ic == null? null
2263 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
Amith Yamasania8b00c82010-03-05 15:41:31 -08002264 if (mExtractedText == null || ic == null) {
2265 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
2266 + mExtractedText + ", input connection = " + ic);
2267 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002268 final EditorInfo ei = getCurrentInputEditorInfo();
2269
2270 try {
2271 eet.startInternalChanges();
The Android Open Source Project10592532009-03-18 17:39:46 -07002272 onUpdateExtractingVisibility(ei);
2273 onUpdateExtractingViews(ei);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002274 int inputType = ei.inputType;
2275 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
2276 == EditorInfo.TYPE_CLASS_TEXT) {
2277 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
2278 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2279 }
2280 }
2281 eet.setInputType(inputType);
2282 eet.setHint(ei.hintText);
2283 if (mExtractedText != null) {
2284 eet.setEnabled(true);
2285 eet.setExtractedText(mExtractedText);
2286 } else {
2287 eet.setEnabled(false);
2288 eet.setText("");
2289 }
2290 } finally {
2291 eet.finishInternalChanges();
2292 }
2293
2294 if (inputChanged) {
2295 onExtractingInputChanged(ei);
2296 }
2297 }
2298 }
satokab751aa2010-09-14 19:17:36 +09002299
2300 // TODO: Handle the subtype change event
2301 /**
2302 * Called when the subtype was changed.
2303 * @param newSubtype the subtype which is being changed to.
2304 */
2305 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
2306 if (DEBUG) {
2307 int nameResId = newSubtype.getNameResId();
satok9ef02832010-11-04 21:17:48 +09002308 String mode = newSubtype.getMode();
satokab751aa2010-09-14 19:17:36 +09002309 String output = "changeInputMethodSubtype:"
2310 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
satok9ef02832010-11-04 21:17:48 +09002311 + mode + ","
satokab751aa2010-09-14 19:17:36 +09002312 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
2313 Log.v(TAG, "--- " + output);
2314 }
2315 }
2316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002317 /**
2318 * Performs a dump of the InputMethodService's internal state. Override
2319 * to add your own information to the dump.
2320 */
2321 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2322 final Printer p = new PrintWriterPrinter(fout);
2323 p.println("Input method service state for " + this + ":");
2324 p.println(" mWindowCreated=" + mWindowCreated
The Android Open Source Project10592532009-03-18 17:39:46 -07002325 + " mWindowAdded=" + mWindowAdded);
2326 p.println(" mWindowVisible=" + mWindowVisible
2327 + " mWindowWasVisible=" + mWindowWasVisible
2328 + " mInShowWindow=" + mInShowWindow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002329 p.println(" Configuration=" + getResources().getConfiguration());
2330 p.println(" mToken=" + mToken);
2331 p.println(" mInputBinding=" + mInputBinding);
2332 p.println(" mInputConnection=" + mInputConnection);
2333 p.println(" mStartedInputConnection=" + mStartedInputConnection);
2334 p.println(" mInputStarted=" + mInputStarted
2335 + " mInputViewStarted=" + mInputViewStarted
2336 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
2337
2338 if (mInputEditorInfo != null) {
2339 p.println(" mInputEditorInfo:");
2340 mInputEditorInfo.dump(p, " ");
2341 } else {
2342 p.println(" mInputEditorInfo: null");
2343 }
2344
2345 p.println(" mShowInputRequested=" + mShowInputRequested
2346 + " mLastShowInputRequested=" + mLastShowInputRequested
2347 + " mShowInputForced=" + mShowInputForced
2348 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
2349 p.println(" mCandidatesVisibility=" + mCandidatesVisibility
2350 + " mFullscreenApplied=" + mFullscreenApplied
The Android Open Source Project10592532009-03-18 17:39:46 -07002351 + " mIsFullscreen=" + mIsFullscreen
2352 + " mExtractViewHidden=" + mExtractViewHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002353
2354 if (mExtractedText != null) {
2355 p.println(" mExtractedText:");
2356 p.println(" text=" + mExtractedText.text.length() + " chars"
2357 + " startOffset=" + mExtractedText.startOffset);
2358 p.println(" selectionStart=" + mExtractedText.selectionStart
2359 + " selectionEnd=" + mExtractedText.selectionEnd
2360 + " flags=0x" + Integer.toHexString(mExtractedText.flags));
2361 } else {
2362 p.println(" mExtractedText: null");
2363 }
2364 p.println(" mExtractedToken=" + mExtractedToken);
2365 p.println(" mIsInputViewShown=" + mIsInputViewShown
2366 + " mStatusIcon=" + mStatusIcon);
2367 p.println("Last computed insets:");
2368 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
2369 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
Jeff Brownfbf09772011-01-16 14:06:57 -08002370 + " touchableInsets=" + mTmpInsets.touchableInsets
2371 + " touchableRegion=" + mTmpInsets.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002372 }
2373}