blob: 4881d1433489765e5ff1637d5aa7763547779181 [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();
384 mInputStarted = false;
385 mInputBinding = null;
386 mInputConnection = null;
387 }
388
389 public void startInput(InputConnection ic, EditorInfo attribute) {
390 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
391 doStartInput(ic, attribute, false);
392 }
393
394 public void restartInput(InputConnection ic, EditorInfo attribute) {
395 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
396 doStartInput(ic, attribute, true);
397 }
398
399 /**
400 * Handle a request by the system to hide the soft input area.
401 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800402 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 if (DEBUG) Log.v(TAG, "hideSoftInput()");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800404 boolean wasVis = isInputViewShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 mShowInputFlags = 0;
406 mShowInputRequested = false;
407 mShowInputForced = false;
satok2f913d92012-05-10 01:48:03 +0900408 doHideWindow();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800409 if (resultReceiver != null) {
410 resultReceiver.send(wasVis != isInputViewShown()
411 ? InputMethodManager.RESULT_HIDDEN
412 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
413 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
414 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 }
416
417 /**
418 * Handle a request by the system to show the soft input area.
419 */
The Android Open Source Project4df24232009-03-05 14:34:35 -0800420 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700421 if (DEBUG) Log.v(TAG, "showSoftInput()");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800422 boolean wasVis = isInputViewShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 mShowInputFlags = 0;
424 if (onShowInputRequested(flags, false)) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700425 try {
426 showWindow(true);
427 } catch (BadTokenException e) {
428 if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
429 mWindowVisible = false;
Craig Mautnerf403b1f2013-03-19 16:54:35 -0700430 mWindowAdded = false;
Craig Mautnere4bbb1c2013-03-15 11:38:44 -0700431 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 }
satok865b9772011-01-21 02:45:06 +0900433 // If user uses hard keyboard, IME button should always be shown.
jungheang.lee217fd292013-02-26 16:53:22 +0900434 boolean showing = isInputViewShown();
Joe Onorato857fd9b2011-01-27 15:08:35 -0800435 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
436 mBackDisposition);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800437 if (resultReceiver != null) {
438 resultReceiver.send(wasVis != isInputViewShown()
439 ? InputMethodManager.RESULT_SHOWN
440 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
441 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
442 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 }
satokab751aa2010-09-14 19:17:36 +0900444
445 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
446 onCurrentInputMethodSubtypeChanged(subtype);
447 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 }
satokab751aa2010-09-14 19:17:36 +0900449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 /**
451 * Concrete implementation of
452 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
453 * all of the standard behavior for an input method session.
454 */
455 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
456 public void finishInput() {
457 if (!isEnabled()) {
458 return;
459 }
460 if (DEBUG) Log.v(TAG, "finishInput() in " + this);
461 doFinishInput();
462 }
463
464 /**
465 * Call {@link InputMethodService#onDisplayCompletions
466 * InputMethodService.onDisplayCompletions()}.
467 */
468 public void displayCompletions(CompletionInfo[] completions) {
469 if (!isEnabled()) {
470 return;
471 }
472 mCurCompletions = completions;
473 onDisplayCompletions(completions);
474 }
475
476 /**
477 * Call {@link InputMethodService#onUpdateExtractedText
478 * InputMethodService.onUpdateExtractedText()}.
479 */
480 public void updateExtractedText(int token, ExtractedText text) {
481 if (!isEnabled()) {
482 return;
483 }
484 onUpdateExtractedText(token, text);
485 }
486
487 /**
488 * Call {@link InputMethodService#onUpdateSelection
489 * InputMethodService.onUpdateSelection()}.
490 */
491 public void updateSelection(int oldSelStart, int oldSelEnd,
492 int newSelStart, int newSelEnd,
493 int candidatesStart, int candidatesEnd) {
494 if (!isEnabled()) {
495 return;
496 }
497 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
498 newSelStart, newSelEnd, candidatesStart, candidatesEnd);
499 }
satok863fcd62011-06-21 17:38:02 +0900500
501 @Override
502 public void viewClicked(boolean focusChanged) {
503 if (!isEnabled()) {
504 return;
505 }
506 InputMethodService.this.onViewClicked(focusChanged);
507 }
508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 /**
510 * Call {@link InputMethodService#onUpdateCursor
511 * InputMethodService.onUpdateCursor()}.
512 */
513 public void updateCursor(Rect newCursor) {
514 if (!isEnabled()) {
515 return;
516 }
517 InputMethodService.this.onUpdateCursor(newCursor);
518 }
519
520 /**
521 * Call {@link InputMethodService#onAppPrivateCommand
522 * InputMethodService.onAppPrivateCommand()}.
523 */
524 public void appPrivateCommand(String action, Bundle data) {
525 if (!isEnabled()) {
526 return;
527 }
528 InputMethodService.this.onAppPrivateCommand(action, data);
529 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800530
531 /**
532 *
533 */
534 public void toggleSoftInput(int showFlags, int hideFlags) {
535 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
536 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 }
538
539 /**
540 * Information about where interesting parts of the input method UI appear.
541 */
542 public static final class Insets {
543 /**
544 * This is the top part of the UI that is the main content. It is
545 * used to determine the basic space needed, to resize/pan the
546 * application behind. It is assumed that this inset does not
547 * change very much, since any change will cause a full resize/pan
548 * of the application behind. This value is relative to the top edge
549 * of the input method window.
550 */
551 public int contentTopInsets;
552
553 /**
554 * This is the top part of the UI that is visibly covering the
555 * application behind it. This provides finer-grained control over
556 * visibility, allowing you to change it relatively frequently (such
557 * as hiding or showing candidates) without disrupting the underlying
558 * UI too much. For example, this will never resize the application
559 * UI, will only pan if needed to make the current focus visible, and
560 * will not aggressively move the pan position when this changes unless
561 * needed to make the focus visible. This value is relative to the top edge
562 * of the input method window.
563 */
564 public int visibleTopInsets;
Jeff Brownfbf09772011-01-16 14:06:57 -0800565
566 /**
567 * This is the region of the UI that is touchable. It is used when
568 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
569 * The region should be specified relative to the origin of the window frame.
570 */
571 public final Region touchableRegion = new Region();
572
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 /**
574 * Option for {@link #touchableInsets}: the entire window frame
575 * can be touched.
576 */
577 public static final int TOUCHABLE_INSETS_FRAME
578 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
579
580 /**
581 * Option for {@link #touchableInsets}: the area inside of
582 * the content insets can be touched.
583 */
584 public static final int TOUCHABLE_INSETS_CONTENT
585 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
586
587 /**
588 * Option for {@link #touchableInsets}: the area inside of
589 * the visible insets can be touched.
590 */
591 public static final int TOUCHABLE_INSETS_VISIBLE
592 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -0800593
594 /**
595 * Option for {@link #touchableInsets}: the region specified by
596 * {@link #touchableRegion} can be touched.
597 */
598 public static final int TOUCHABLE_INSETS_REGION
599 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
600
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 /**
602 * Determine which area of the window is touchable by the user. May
603 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
Jeff Brownfbf09772011-01-16 14:06:57 -0800604 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
605 * or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 */
607 public int touchableInsets;
608 }
satok865b9772011-01-21 02:45:06 +0900609
The Android Open Source Project10592532009-03-18 17:39:46 -0700610 /**
611 * You can call this to customize the theme used by your IME's window.
612 * This theme should typically be one that derives from
613 * {@link android.R.style#Theme_InputMethod}, which is the default theme
614 * you will get. This must be set before {@link #onCreate}, so you
615 * will typically call it in your constructor with the resource ID
616 * of your custom theme.
617 */
satokab751aa2010-09-14 19:17:36 +0900618 @Override
The Android Open Source Project10592532009-03-18 17:39:46 -0700619 public void setTheme(int theme) {
620 if (mWindow != null) {
621 throw new IllegalStateException("Must be called before onCreate()");
622 }
623 mTheme = theme;
624 }
satok865b9772011-01-21 02:45:06 +0900625
Dianne Hackborn836531b2012-08-01 19:00:38 -0700626 /**
627 * You can call this to try to enable hardware accelerated drawing for
628 * your IME. This must be set before {@link #onCreate}, so you
629 * will typically call it in your constructor. It is not always possible
630 * to use hardware acclerated drawing in an IME (for example on low-end
631 * devices that do not have the resources to support this), so the call
632 * returns true if it succeeds otherwise false if you will need to draw
633 * in software. You must be able to handle either case.
634 */
635 public boolean enableHardwareAcceleration() {
636 if (mWindow != null) {
637 throw new IllegalStateException("Must be called before onCreate()");
638 }
Jeff Brown98365d72012-08-19 20:30:52 -0700639 if (ActivityManager.isHighEndGfx()) {
Dianne Hackborn836531b2012-08-01 19:00:38 -0700640 mHardwareAccelerated = true;
641 return true;
642 }
643 return false;
644 }
645
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 @Override public void onCreate() {
Dianne Hackbornd922ae02011-01-14 11:43:24 -0800647 mTheme = Resources.selectSystemTheme(mTheme,
648 getApplicationInfo().targetSdkVersion,
Adam Powell6e90a362011-08-14 16:48:32 -0700649 android.R.style.Theme_InputMethod,
Adam Powell36a97e402011-09-07 11:27:49 -0700650 android.R.style.Theme_Holo_InputMethod,
Adam Powell6e90a362011-08-14 16:48:32 -0700651 android.R.style.Theme_DeviceDefault_InputMethod);
The Android Open Source Project10592532009-03-18 17:39:46 -0700652 super.setTheme(mTheme);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 super.onCreate();
654 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
655 mInflater = (LayoutInflater)getSystemService(
656 Context.LAYOUT_INFLATER_SERVICE);
Dianne Hackborn83fe3f52009-09-12 23:38:30 -0700657 mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
Dianne Hackborn836531b2012-08-01 19:00:38 -0700658 if (mHardwareAccelerated) {
659 mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
660 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 initViews();
Romain Guy980a9382010-01-08 15:06:28 -0800662 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 }
satokab751aa2010-09-14 19:17:36 +0900664
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 /**
666 * This is a hook that subclasses can use to perform initialization of
667 * their interface. It is called for you prior to any of your UI objects
668 * being created, both after the service is first created and after a
669 * configuration change happens.
670 */
671 public void onInitializeInterface() {
Gilles Debunne34703b62011-09-08 11:16:25 -0700672 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 }
satokab751aa2010-09-14 19:17:36 +0900674
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 void initialize() {
676 if (!mInitialized) {
677 mInitialized = true;
678 onInitializeInterface();
679 }
680 }
satokab751aa2010-09-14 19:17:36 +0900681
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 void initViews() {
683 mInitialized = false;
684 mWindowCreated = false;
685 mShowInputRequested = false;
686 mShowInputForced = false;
687
The Android Open Source Project10592532009-03-18 17:39:46 -0700688 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 mRootView = mInflater.inflate(
690 com.android.internal.R.layout.input_method, null);
691 mWindow.setContentView(mRootView);
692 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
Jeff Sharkey6e2bee72012-10-01 13:39:08 -0700693 if (Settings.Global.getInt(getContentResolver(),
694 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 mWindow.getWindow().setWindowAnimations(
696 com.android.internal.R.style.Animation_InputMethodFancy);
697 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700698 mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
699 mExtractViewHidden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
701 mExtractView = null;
702 mExtractEditText = null;
703 mExtractAccessories = null;
704 mExtractAction = null;
705 mFullscreenApplied = false;
706
707 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
708 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
709 mInputView = null;
710 mIsInputViewShown = false;
711
712 mExtractFrame.setVisibility(View.GONE);
713 mCandidatesVisibility = getCandidatesHiddenVisibility();
714 mCandidatesFrame.setVisibility(mCandidatesVisibility);
715 mInputFrame.setVisibility(View.GONE);
716 }
satokab751aa2010-09-14 19:17:36 +0900717
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 @Override public void onDestroy() {
719 super.onDestroy();
720 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
721 mInsetsComputer);
satokf17db9f2011-09-14 18:55:58 +0900722 finishViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 if (mWindowAdded) {
satokc0c87652011-09-12 12:08:05 +0900724 // Disable exit animation for the current IME window
725 // to avoid the race condition between the exit and enter animations
726 // when the current IME is being switched to another one.
727 mWindow.getWindow().setWindowAnimations(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 mWindow.dismiss();
729 }
730 }
satokf17db9f2011-09-14 18:55:58 +0900731
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 /**
733 * Take care of handling configuration changes. Subclasses of
734 * InputMethodService generally don't need to deal directly with
735 * this on their own; the standard implementation here takes care of
736 * regenerating the input method UI as a result of the configuration
737 * change, so you can rely on your {@link #onCreateInputView} and
738 * other methods being called as appropriate due to a configuration change.
739 *
740 * <p>When a configuration change does happen,
741 * {@link #onInitializeInterface()} is guaranteed to be called the next
742 * time prior to any of the other input or UI creation callbacks. The
743 * following will be called immediately depending if appropriate for current
744 * state: {@link #onStartInput} if input is active, and
745 * {@link #onCreateInputView} and {@link #onStartInputView} and related
746 * appropriate functions if the UI is displayed.
747 */
748 @Override public void onConfigurationChanged(Configuration newConfig) {
749 super.onConfigurationChanged(newConfig);
750
751 boolean visible = mWindowVisible;
752 int showFlags = mShowInputFlags;
753 boolean showingInput = mShowInputRequested;
754 CompletionInfo[] completions = mCurCompletions;
755 initViews();
756 mInputViewStarted = false;
757 mCandidatesViewStarted = false;
758 if (mInputStarted) {
759 doStartInput(getCurrentInputConnection(),
760 getCurrentInputEditorInfo(), true);
761 }
762 if (visible) {
763 if (showingInput) {
764 // If we were last showing the soft keyboard, try to do so again.
765 if (onShowInputRequested(showFlags, true)) {
766 showWindow(true);
767 if (completions != null) {
768 mCurCompletions = completions;
769 onDisplayCompletions(completions);
770 }
771 } else {
satok2f913d92012-05-10 01:48:03 +0900772 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 }
774 } else if (mCandidatesVisibility == View.VISIBLE) {
775 // If the candidates are currently visible, make sure the
776 // window is shown for them.
777 showWindow(false);
778 } else {
779 // Otherwise hide the window.
satok2f913d92012-05-10 01:48:03 +0900780 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 }
satok865b9772011-01-21 02:45:06 +0900782 // If user uses hard keyboard, IME button should always be shown.
Joe Onorato857fd9b2011-01-27 15:08:35 -0800783 boolean showing = onEvaluateInputViewShown();
784 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
785 mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 }
787 }
788
789 /**
790 * Implement to return our standard {@link InputMethodImpl}. Subclasses
791 * can override to provide their own customized version.
792 */
satokab751aa2010-09-14 19:17:36 +0900793 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 public AbstractInputMethodImpl onCreateInputMethodInterface() {
795 return new InputMethodImpl();
796 }
797
798 /**
799 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
800 * can override to provide their own customized version.
801 */
satokab751aa2010-09-14 19:17:36 +0900802 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
804 return new InputMethodSessionImpl();
805 }
806
807 public LayoutInflater getLayoutInflater() {
808 return mInflater;
809 }
810
811 public Dialog getWindow() {
812 return mWindow;
813 }
814
Joe Onorato857fd9b2011-01-27 15:08:35 -0800815 public void setBackDisposition(int disposition) {
816 mBackDisposition = disposition;
817 }
818
819 public int getBackDisposition() {
820 return mBackDisposition;
821 }
822
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 /**
824 * Return the maximum width, in pixels, available the input method.
825 * Input methods are positioned at the bottom of the screen and, unless
826 * running in fullscreen, will generally want to be as short as possible
827 * so should compute their height based on their contents. However, they
828 * can stretch as much as needed horizontally. The function returns to
829 * you the maximum amount of space available horizontally, which you can
830 * use if needed for UI placement.
831 *
832 * <p>In many cases this is not needed, you can just rely on the normal
833 * view layout mechanisms to position your views within the full horizontal
834 * space given to the input method.
835 *
836 * <p>Note that this value can change dynamically, in particular when the
837 * screen orientation changes.
838 */
839 public int getMaxWidth() {
840 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
841 return wm.getDefaultDisplay().getWidth();
842 }
843
844 /**
845 * Return the currently active InputBinding for the input method, or
846 * null if there is none.
847 */
848 public InputBinding getCurrentInputBinding() {
849 return mInputBinding;
850 }
851
852 /**
853 * Retrieve the currently active InputConnection that is bound to
854 * the input method, or null if there is none.
855 */
856 public InputConnection getCurrentInputConnection() {
857 InputConnection ic = mStartedInputConnection;
858 if (ic != null) {
859 return ic;
860 }
861 return mInputConnection;
862 }
863
864 public boolean getCurrentInputStarted() {
865 return mInputStarted;
866 }
867
868 public EditorInfo getCurrentInputEditorInfo() {
869 return mInputEditorInfo;
870 }
871
872 /**
873 * Re-evaluate whether the input method should be running in fullscreen
874 * mode, and update its UI if this has changed since the last time it
875 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to
876 * determine whether it should currently run in fullscreen mode. You
877 * can use {@link #isFullscreenMode()} to determine if the input method
878 * is currently running in fullscreen mode.
879 */
880 public void updateFullscreenMode() {
881 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
882 boolean changed = mLastShowInputRequested != mShowInputRequested;
883 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
884 changed = true;
885 mIsFullscreen = isFullscreen;
886 InputConnection ic = getCurrentInputConnection();
887 if (ic != null) ic.reportFullscreenMode(isFullscreen);
888 mFullscreenApplied = true;
889 initialize();
The Android Open Source Project10592532009-03-18 17:39:46 -0700890 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
891 mFullscreenArea.getLayoutParams();
892 if (isFullscreen) {
893 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
894 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
895 lp.height = 0;
896 lp.weight = 1;
897 } else {
898 mFullscreenArea.setBackgroundDrawable(null);
899 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
900 lp.weight = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700902 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
903 mFullscreenArea, lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 if (isFullscreen) {
905 if (mExtractView == null) {
906 View v = onCreateExtractTextView();
907 if (v != null) {
908 setExtractView(v);
909 }
910 }
911 startExtractingText(false);
912 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700913 updateExtractFrameVisibility();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
915
916 if (changed) {
Gilles Debunne34703b62011-09-08 11:16:25 -0700917 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 mLastShowInputRequested = mShowInputRequested;
919 }
920 }
921
922 /**
923 * Update the given window's parameters for the given mode. This is called
924 * when the window is first displayed and each time the fullscreen or
925 * candidates only mode changes.
926 *
927 * <p>The default implementation makes the layout for the window
Romain Guy980a9382010-01-08 15:06:28 -0800928 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
929 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 *
931 * @param win The input method's window.
932 * @param isFullscreen If true, the window is running in fullscreen mode
933 * and intended to cover the entire application display.
934 * @param isCandidatesOnly If true, the window is only showing the
935 * candidates view and none of the rest of its UI. This is mutually
936 * exclusive with fullscreen mode.
937 */
938 public void onConfigureWindow(Window win, boolean isFullscreen,
939 boolean isCandidatesOnly) {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +0900940 final int currentHeight = mWindow.getWindow().getAttributes().height;
941 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
942 if (mIsInputViewShown && currentHeight != newHeight) {
943 Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: "
944 + currentHeight + " -> " + newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 }
Satoshi Kataoka8b117c82012-11-06 18:59:23 +0900946 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
948
949 /**
950 * Return whether the input method is <em>currently</em> running in
951 * fullscreen mode. This is the mode that was last determined and
952 * applied by {@link #updateFullscreenMode()}.
953 */
954 public boolean isFullscreenMode() {
955 return mIsFullscreen;
956 }
957
958 /**
959 * Override this to control when the input method should run in
960 * fullscreen mode. The default implementation runs in fullsceen only
961 * when the screen is in landscape mode. If you change what
962 * this returns, you will need to call {@link #updateFullscreenMode()}
963 * yourself whenever the returned value may have changed to have it
964 * re-evaluated and applied.
965 */
966 public boolean onEvaluateFullscreenMode() {
967 Configuration config = getResources().getConfiguration();
Leon Scroggins2edd6822010-01-12 11:36:13 -0500968 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
969 return false;
970 }
971 if (mInputEditorInfo != null
972 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
973 return false;
974 }
975 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 }
Gilles Debunne34703b62011-09-08 11:16:25 -0700977
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 /**
The Android Open Source Project10592532009-03-18 17:39:46 -0700979 * Controls the visibility of the extracted text area. This only applies
980 * when the input method is in fullscreen mode, and thus showing extracted
981 * text. When false, the extracted text will not be shown, allowing some
982 * of the application to be seen behind. This is normally set for you
983 * by {@link #onUpdateExtractingVisibility}. This controls the visibility
984 * of both the extracted text and candidate view; the latter since it is
985 * not useful if there is no text to see.
986 */
987 public void setExtractViewShown(boolean shown) {
988 if (mExtractViewHidden == shown) {
989 mExtractViewHidden = !shown;
990 updateExtractFrameVisibility();
991 }
992 }
993
994 /**
995 * Return whether the fullscreen extract view is shown. This will only
996 * return true if {@link #isFullscreenMode()} returns true, and in that
997 * case its value depends on the last call to
998 * {@link #setExtractViewShown(boolean)}. This effectively lets you
999 * determine if the application window is entirely covered (when this
1000 * returns true) or if some part of it may be shown (if this returns
1001 * false, though if {@link #isFullscreenMode()} returns true in that case
1002 * then it is probably only a sliver of the application).
1003 */
1004 public boolean isExtractViewShown() {
1005 return mIsFullscreen && !mExtractViewHidden;
1006 }
1007
1008 void updateExtractFrameVisibility() {
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001009 final int vis;
The Android Open Source Project10592532009-03-18 17:39:46 -07001010 if (isFullscreenMode()) {
1011 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
Satoshi Kataoka8b117c82012-11-06 18:59:23 +09001012 // "vis" should be applied for the extract frame as well in the fullscreen mode.
1013 mExtractFrame.setVisibility(vis);
The Android Open Source Project10592532009-03-18 17:39:46 -07001014 } else {
1015 vis = View.VISIBLE;
1016 mExtractFrame.setVisibility(View.GONE);
1017 }
1018 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
1019 if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
1020 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
1021 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
1022 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
1023 0);
1024 if (animRes != 0) {
1025 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
1026 this, animRes));
1027 }
1028 }
1029 mFullscreenArea.setVisibility(vis);
1030 }
1031
1032 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 * Compute the interesting insets into your UI. The default implementation
1034 * uses the top of the candidates frame for the visible insets, and the
1035 * top of the input frame for the content insets. The default touchable
1036 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
1037 *
The Android Open Source Project10592532009-03-18 17:39:46 -07001038 * <p>Note that this method is not called when
1039 * {@link #isExtractViewShown} returns true, since
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 * in that case the application is left as-is behind the input method and
1041 * not impacted by anything in its UI.
1042 *
1043 * @param outInsets Fill in with the current UI insets.
1044 */
1045 public void onComputeInsets(Insets outInsets) {
1046 int[] loc = mTmpLocation;
1047 if (mInputFrame.getVisibility() == View.VISIBLE) {
1048 mInputFrame.getLocationInWindow(loc);
1049 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001050 View decor = getWindow().getWindow().getDecorView();
1051 loc[1] = decor.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001053 if (isFullscreenMode()) {
1054 // In fullscreen mode, we never resize the underlying window.
1055 View decor = getWindow().getWindow().getDecorView();
1056 outInsets.contentTopInsets = decor.getHeight();
1057 } else {
1058 outInsets.contentTopInsets = loc[1];
1059 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
1061 mCandidatesFrame.getLocationInWindow(loc);
1062 }
1063 outInsets.visibleTopInsets = loc[1];
1064 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
Jeff Brownfbf09772011-01-16 14:06:57 -08001065 outInsets.touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 }
1067
1068 /**
1069 * Re-evaluate whether the soft input area should currently be shown, and
1070 * update its UI if this has changed since the last time it
1071 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to
1072 * determine whether the input view should currently be shown. You
1073 * can use {@link #isInputViewShown()} to determine if the input view
1074 * is currently shown.
1075 */
1076 public void updateInputViewShown() {
1077 boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
1078 if (mIsInputViewShown != isShown && mWindowVisible) {
1079 mIsInputViewShown = isShown;
1080 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
1081 if (mInputView == null) {
1082 initialize();
1083 View v = onCreateInputView();
1084 if (v != null) {
1085 setInputView(v);
1086 }
1087 }
1088 }
1089 }
1090
1091 /**
1092 * Returns true if we have been asked to show our input view.
1093 */
1094 public boolean isShowInputRequested() {
1095 return mShowInputRequested;
1096 }
1097
1098 /**
1099 * Return whether the soft input view is <em>currently</em> shown to the
1100 * user. This is the state that was last determined and
1101 * applied by {@link #updateInputViewShown()}.
1102 */
1103 public boolean isInputViewShown() {
1104 return mIsInputViewShown && mWindowVisible;
1105 }
1106
1107 /**
1108 * Override this to control when the soft input area should be shown to
1109 * the user. The default implementation only shows the input view when
1110 * there is no hard keyboard or the keyboard is hidden. If you change what
1111 * this returns, you will need to call {@link #updateInputViewShown()}
1112 * yourself whenever the returned value may have changed to have it
Gilles Debunne8cbb4c62011-01-24 12:33:56 -08001113 * re-evaluated and applied.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 */
1115 public boolean onEvaluateInputViewShown() {
1116 Configuration config = getResources().getConfiguration();
1117 return config.keyboard == Configuration.KEYBOARD_NOKEYS
Ken Wakasa8710e762011-01-30 11:02:09 +09001118 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 }
1120
1121 /**
1122 * Controls the visibility of the candidates display area. By default
1123 * it is hidden.
1124 */
1125 public void setCandidatesViewShown(boolean shown) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001126 updateCandidatesVisibility(shown);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 if (!mShowInputRequested && mWindowVisible != shown) {
1128 // If we are being asked to show the candidates view while the app
1129 // has not asked for the input view to be shown, then we need
1130 // to update whether the window is shown.
1131 if (shown) {
1132 showWindow(false);
1133 } else {
satok2f913d92012-05-10 01:48:03 +09001134 doHideWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 }
1136 }
1137 }
1138
The Android Open Source Project10592532009-03-18 17:39:46 -07001139 void updateCandidatesVisibility(boolean shown) {
1140 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
1141 if (mCandidatesVisibility != vis) {
1142 mCandidatesFrame.setVisibility(vis);
1143 mCandidatesVisibility = vis;
1144 }
1145 }
1146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 /**
1148 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
1149 * or {@link View#GONE View.GONE}) of the candidates view when it is not
The Android Open Source Project10592532009-03-18 17:39:46 -07001150 * shown. The default implementation returns GONE when
1151 * {@link #isExtractViewShown} returns true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 * otherwise VISIBLE. Be careful if you change this to return GONE in
1153 * other situations -- if showing or hiding the candidates view causes
1154 * your window to resize, this can cause temporary drawing artifacts as
1155 * the resize takes place.
1156 */
1157 public int getCandidatesHiddenVisibility() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001158 return isExtractViewShown() ? View.GONE : View.INVISIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159 }
1160
1161 public void showStatusIcon(int iconResId) {
1162 mStatusIcon = iconResId;
1163 mImm.showStatusIcon(mToken, getPackageName(), iconResId);
1164 }
1165
1166 public void hideStatusIcon() {
1167 mStatusIcon = 0;
1168 mImm.hideStatusIcon(mToken);
1169 }
1170
1171 /**
1172 * Force switch to a new input method, as identified by <var>id</var>. This
1173 * input method will be destroyed, and the requested one started on the
1174 * current input field.
1175 *
1176 * @param id Unique identifier of the new input method ot start.
1177 */
1178 public void switchInputMethod(String id) {
1179 mImm.setInputMethod(mToken, id);
1180 }
1181
1182 public void setExtractView(View view) {
1183 mExtractFrame.removeAllViews();
1184 mExtractFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001185 ViewGroup.LayoutParams.MATCH_PARENT,
1186 ViewGroup.LayoutParams.MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 mExtractView = view;
1188 if (view != null) {
1189 mExtractEditText = (ExtractEditText)view.findViewById(
1190 com.android.internal.R.id.inputExtractEditText);
1191 mExtractEditText.setIME(this);
1192 mExtractAction = (Button)view.findViewById(
1193 com.android.internal.R.id.inputExtractAction);
1194 if (mExtractAction != null) {
1195 mExtractAccessories = (ViewGroup)view.findViewById(
1196 com.android.internal.R.id.inputExtractAccessories);
1197 }
1198 startExtractingText(false);
1199 } else {
1200 mExtractEditText = null;
1201 mExtractAccessories = null;
1202 mExtractAction = null;
1203 }
1204 }
1205
1206 /**
1207 * Replaces the current candidates view with a new one. You only need to
1208 * call this when dynamically changing the view; normally, you should
1209 * implement {@link #onCreateCandidatesView()} and create your view when
1210 * first needed by the input method.
1211 */
1212 public void setCandidatesView(View view) {
1213 mCandidatesFrame.removeAllViews();
1214 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001215 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 ViewGroup.LayoutParams.WRAP_CONTENT));
1217 }
1218
1219 /**
1220 * Replaces the current input view with a new one. You only need to
1221 * call this when dynamically changing the view; normally, you should
1222 * implement {@link #onCreateInputView()} and create your view when
1223 * first needed by the input method.
1224 */
1225 public void setInputView(View view) {
1226 mInputFrame.removeAllViews();
1227 mInputFrame.addView(view, new FrameLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001228 ViewGroup.LayoutParams.MATCH_PARENT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 ViewGroup.LayoutParams.WRAP_CONTENT));
1230 mInputView = view;
1231 }
1232
1233 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001234 * Called by the framework to create the layout for showing extacted text.
1235 * Only called when in fullscreen mode. The returned view hierarchy must
1236 * have an {@link ExtractEditText} whose ID is
1237 * {@link android.R.id#inputExtractEditText}.
1238 */
1239 public View onCreateExtractTextView() {
1240 return mInflater.inflate(
1241 com.android.internal.R.layout.input_method_extract_view, null);
1242 }
1243
1244 /**
1245 * Create and return the view hierarchy used to show candidates. This will
1246 * be called once, when the candidates are first displayed. You can return
1247 * null to have no candidates view; the default implementation returns null.
1248 *
1249 * <p>To control when the candidates view is displayed, use
1250 * {@link #setCandidatesViewShown(boolean)}.
1251 * To change the candidates view after the first one is created by this
1252 * function, use {@link #setCandidatesView(View)}.
1253 */
1254 public View onCreateCandidatesView() {
1255 return null;
1256 }
1257
1258 /**
1259 * Create and return the view hierarchy used for the input area (such as
1260 * a soft keyboard). This will be called once, when the input area is
1261 * first displayed. You can return null to have no input area; the default
1262 * implementation returns null.
1263 *
1264 * <p>To control when the input view is displayed, implement
1265 * {@link #onEvaluateInputViewShown()}.
1266 * To change the input view after the first one is created by this
1267 * function, use {@link #setInputView(View)}.
1268 */
1269 public View onCreateInputView() {
1270 return null;
1271 }
1272
1273 /**
1274 * Called when the input view is being shown and input has started on
1275 * a new editor. This will always be called after {@link #onStartInput},
1276 * allowing you to do your general setup there and just view-specific
1277 * setup here. You are guaranteed that {@link #onCreateInputView()} will
1278 * have been called some time before this function is called.
1279 *
1280 * @param info Description of the type of text being edited.
1281 * @param restarting Set to true if we are restarting input on the
1282 * same text field as before.
1283 */
1284 public void onStartInputView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001285 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287
1288 /**
1289 * Called when the input view is being hidden from the user. This will
1290 * be called either prior to hiding the window, or prior to switching to
1291 * another target for editing.
1292 *
1293 * <p>The default
1294 * implementation uses the InputConnection to clear any active composing
1295 * text; you can override this (not calling the base class implementation)
1296 * to perform whatever behavior you would like.
1297 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001298 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 * called immediately after.
1300 */
1301 public void onFinishInputView(boolean finishingInput) {
1302 if (!finishingInput) {
1303 InputConnection ic = getCurrentInputConnection();
1304 if (ic != null) {
1305 ic.finishComposingText();
1306 }
1307 }
1308 }
1309
1310 /**
1311 * Called when only the candidates view has been shown for showing
1312 * processing as the user enters text through a hard keyboard.
1313 * This will always be called after {@link #onStartInput},
1314 * allowing you to do your general setup there and just view-specific
1315 * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
1316 * will have been called some time before this function is called.
1317 *
1318 * <p>Note that this will <em>not</em> be called when the input method
1319 * is running in full editing mode, and thus receiving
1320 * {@link #onStartInputView} to initiate that operation. This is only
1321 * for the case when candidates are being shown while the input method
1322 * editor is hidden but wants to show its candidates UI as text is
1323 * entered through some other mechanism.
1324 *
1325 * @param info Description of the type of text being edited.
1326 * @param restarting Set to true if we are restarting input on the
1327 * same text field as before.
1328 */
1329 public void onStartCandidatesView(EditorInfo info, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001330 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001331 }
1332
1333 /**
1334 * Called when the candidates view is being hidden from the user. This will
1335 * be called either prior to hiding the window, or prior to switching to
1336 * another target for editing.
1337 *
1338 * <p>The default
1339 * implementation uses the InputConnection to clear any active composing
1340 * text; you can override this (not calling the base class implementation)
1341 * to perform whatever behavior you would like.
1342 *
The Android Open Source Project4df24232009-03-05 14:34:35 -08001343 * @param finishingInput If true, {@link #onFinishInput} will be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 * called immediately after.
1345 */
1346 public void onFinishCandidatesView(boolean finishingInput) {
1347 if (!finishingInput) {
1348 InputConnection ic = getCurrentInputConnection();
1349 if (ic != null) {
1350 ic.finishComposingText();
1351 }
1352 }
1353 }
1354
1355 /**
1356 * The system has decided that it may be time to show your input method.
1357 * This is called due to a corresponding call to your
The Android Open Source Project4df24232009-03-05 14:34:35 -08001358 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 * method. The default implementation uses
1360 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
1361 * and the current configuration to decide whether the input view should
1362 * be shown at this point.
1363 *
1364 * @param flags Provides additional information about the show request,
The Android Open Source Project4df24232009-03-05 14:34:35 -08001365 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366 * @param configChange This is true if we are re-showing due to a
1367 * configuration change.
1368 * @return Returns true to indicate that the window should be shown.
1369 */
1370 public boolean onShowInputRequested(int flags, boolean configChange) {
1371 if (!onEvaluateInputViewShown()) {
1372 return false;
1373 }
1374 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
1375 if (!configChange && onEvaluateFullscreenMode()) {
1376 // Don't show if this is not explicitly requested by the user and
1377 // the input method is fullscreen. That would be too disruptive.
1378 // However, we skip this change for a config change, since if
1379 // the IME is already shown we do want to go into fullscreen
1380 // mode at this point.
1381 return false;
1382 }
1383 Configuration config = getResources().getConfiguration();
1384 if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
1385 // And if the device has a hard keyboard, even if it is
1386 // currently hidden, don't show the input method implicitly.
1387 // These kinds of devices don't need it that much.
1388 return false;
1389 }
1390 }
1391 if ((flags&InputMethod.SHOW_FORCED) != 0) {
1392 mShowInputForced = true;
1393 }
1394 return true;
1395 }
1396
1397 public void showWindow(boolean showInput) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07001398 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 + " mShowInputRequested=" + mShowInputRequested
1400 + " mWindowAdded=" + mWindowAdded
1401 + " mWindowCreated=" + mWindowCreated
1402 + " mWindowVisible=" + mWindowVisible
1403 + " mInputStarted=" + mInputStarted);
The Android Open Source Project10592532009-03-18 17:39:46 -07001404
1405 if (mInShowWindow) {
1406 Log.w(TAG, "Re-entrance in to showWindow");
1407 return;
1408 }
1409
1410 try {
1411 mWindowWasVisible = mWindowVisible;
1412 mInShowWindow = true;
1413 showWindowInner(showInput);
1414 } finally {
1415 mWindowWasVisible = true;
1416 mInShowWindow = false;
1417 }
1418 }
satok06487a52010-10-29 11:37:18 +09001419
The Android Open Source Project10592532009-03-18 17:39:46 -07001420 void showWindowInner(boolean showInput) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001421 boolean doShowInput = false;
1422 boolean wasVisible = mWindowVisible;
1423 mWindowVisible = true;
1424 if (!mShowInputRequested) {
1425 if (mInputStarted) {
1426 if (showInput) {
1427 doShowInput = true;
1428 mShowInputRequested = true;
1429 }
1430 }
1431 } else {
1432 showInput = true;
1433 }
satok06487a52010-10-29 11:37:18 +09001434
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 if (DEBUG) Log.v(TAG, "showWindow: updating UI");
1436 initialize();
1437 updateFullscreenMode();
1438 updateInputViewShown();
1439
1440 if (!mWindowAdded || !mWindowCreated) {
1441 mWindowAdded = true;
1442 mWindowCreated = true;
1443 initialize();
1444 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
1445 View v = onCreateCandidatesView();
1446 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
1447 if (v != null) {
1448 setCandidatesView(v);
1449 }
1450 }
1451 if (mShowInputRequested) {
1452 if (!mInputViewStarted) {
1453 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1454 mInputViewStarted = true;
1455 onStartInputView(mInputEditorInfo, false);
1456 }
1457 } else if (!mCandidatesViewStarted) {
1458 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1459 mCandidatesViewStarted = true;
1460 onStartCandidatesView(mInputEditorInfo, false);
1461 }
1462
1463 if (doShowInput) {
1464 startExtractingText(false);
1465 }
satok06487a52010-10-29 11:37:18 +09001466
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001467 if (!wasVisible) {
1468 if (DEBUG) Log.v(TAG, "showWindow: showing!");
Joe Onorato857fd9b2011-01-27 15:08:35 -08001469 mImm.setImeWindowStatus(mToken, IME_ACTIVE, mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 onWindowShown();
1471 mWindow.show();
1472 }
1473 }
satok06487a52010-10-29 11:37:18 +09001474
satokf17db9f2011-09-14 18:55:58 +09001475 private void finishViews() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 if (mInputViewStarted) {
1477 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1478 onFinishInputView(false);
1479 } else if (mCandidatesViewStarted) {
1480 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1481 onFinishCandidatesView(false);
1482 }
1483 mInputViewStarted = false;
1484 mCandidatesViewStarted = false;
satokf17db9f2011-09-14 18:55:58 +09001485 }
1486
satok2f913d92012-05-10 01:48:03 +09001487 private void doHideWindow() {
1488 mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
1489 hideWindow();
1490 }
1491
satokf17db9f2011-09-14 18:55:58 +09001492 public void hideWindow() {
1493 finishViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 if (mWindowVisible) {
1495 mWindow.hide();
1496 mWindowVisible = false;
1497 onWindowHidden();
The Android Open Source Project10592532009-03-18 17:39:46 -07001498 mWindowWasVisible = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 }
1500 }
satok06487a52010-10-29 11:37:18 +09001501
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 /**
1503 * Called when the input method window has been shown to the user, after
1504 * previously not being visible. This is done after all of the UI setup
1505 * for the window has occurred (creating its views etc).
1506 */
1507 public void onWindowShown() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001508 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001509 }
1510
1511 /**
1512 * Called when the input method window has been hidden from the user,
1513 * after previously being visible.
1514 */
1515 public void onWindowHidden() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001516 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 }
1518
1519 /**
1520 * Called when a new client has bound to the input method. This
1521 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
1522 * and {@link #onFinishInput()} calls as the user navigates through its
1523 * UI. Upon this call you know that {@link #getCurrentInputBinding}
1524 * and {@link #getCurrentInputConnection} return valid objects.
1525 */
1526 public void onBindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001527 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001528 }
1529
1530 /**
1531 * Called when the previous bound client is no longer associated
1532 * with the input method. After returning {@link #getCurrentInputBinding}
1533 * and {@link #getCurrentInputConnection} will no longer return
1534 * valid objects.
1535 */
1536 public void onUnbindInput() {
Gilles Debunne34703b62011-09-08 11:16:25 -07001537 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001538 }
1539
1540 /**
1541 * Called to inform the input method that text input has started in an
1542 * editor. You should use this callback to initialize the state of your
1543 * input to match the state of the editor given to it.
1544 *
1545 * @param attribute The attributes of the editor that input is starting
1546 * in.
1547 * @param restarting Set to true if input is restarting in the same
1548 * editor such as because the application has changed the text in
1549 * the editor. Otherwise will be false, indicating this is a new
1550 * session with the editor.
1551 */
1552 public void onStartInput(EditorInfo attribute, boolean restarting) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001553 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 }
1555
1556 void doFinishInput() {
1557 if (mInputViewStarted) {
1558 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
1559 onFinishInputView(true);
1560 } else if (mCandidatesViewStarted) {
1561 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
1562 onFinishCandidatesView(true);
1563 }
1564 mInputViewStarted = false;
1565 mCandidatesViewStarted = false;
1566 if (mInputStarted) {
1567 if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
1568 onFinishInput();
1569 }
1570 mInputStarted = false;
1571 mStartedInputConnection = null;
1572 mCurCompletions = null;
1573 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001575 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
1576 if (!restarting) {
1577 doFinishInput();
1578 }
1579 mInputStarted = true;
1580 mStartedInputConnection = ic;
1581 mInputEditorInfo = attribute;
1582 initialize();
1583 if (DEBUG) Log.v(TAG, "CALL: onStartInput");
1584 onStartInput(attribute, restarting);
1585 if (mWindowVisible) {
1586 if (mShowInputRequested) {
1587 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
1588 mInputViewStarted = true;
1589 onStartInputView(mInputEditorInfo, restarting);
1590 startExtractingText(true);
1591 } else if (mCandidatesVisibility == View.VISIBLE) {
1592 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
1593 mCandidatesViewStarted = true;
1594 onStartCandidatesView(mInputEditorInfo, restarting);
1595 }
1596 }
1597 }
1598
1599 /**
1600 * Called to inform the input method that text input has finished in
1601 * the last editor. At this point there may be a call to
1602 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
1603 * new editor, or the input method may be left idle. This method is
1604 * <em>not</em> called when input restarts in the same editor.
1605 *
1606 * <p>The default
1607 * implementation uses the InputConnection to clear any active composing
1608 * text; you can override this (not calling the base class implementation)
1609 * to perform whatever behavior you would like.
1610 */
1611 public void onFinishInput() {
1612 InputConnection ic = getCurrentInputConnection();
1613 if (ic != null) {
1614 ic.finishComposingText();
1615 }
1616 }
1617
1618 /**
1619 * Called when the application has reported auto-completion candidates that
1620 * it would like to have the input method displayed. Typically these are
1621 * only used when an input method is running in full-screen mode, since
1622 * otherwise the user can see and interact with the pop-up window of
1623 * completions shown by the application.
1624 *
1625 * <p>The default implementation here does nothing.
1626 */
1627 public void onDisplayCompletions(CompletionInfo[] completions) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001628 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 }
1630
1631 /**
1632 * Called when the application has reported new extracted text to be shown
1633 * due to changes in its current text state. The default implementation
1634 * here places the new text in the extract edit text, when the input
1635 * method is running in fullscreen mode.
1636 */
1637 public void onUpdateExtractedText(int token, ExtractedText text) {
1638 if (mExtractedToken != token) {
1639 return;
1640 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001641 if (text != null) {
1642 if (mExtractEditText != null) {
1643 mExtractedText = text;
1644 mExtractEditText.setExtractedText(text);
1645 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646 }
1647 }
1648
1649 /**
1650 * Called when the application has reported a new selection region of
1651 * the text. This is called whether or not the input method has requested
1652 * extracted text updates, although if so it will not receive this call
1653 * if the extracted text has changed as well.
1654 *
1655 * <p>The default implementation takes care of updating the cursor in
1656 * the extract text, if it is being shown.
1657 */
1658 public void onUpdateSelection(int oldSelStart, int oldSelEnd,
1659 int newSelStart, int newSelEnd,
1660 int candidatesStart, int candidatesEnd) {
1661 final ExtractEditText eet = mExtractEditText;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001662 if (eet != null && isFullscreenMode() && mExtractedText != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 final int off = mExtractedText.startOffset;
1664 eet.startInternalChanges();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001665 newSelStart -= off;
1666 newSelEnd -= off;
1667 final int len = eet.getText().length();
1668 if (newSelStart < 0) newSelStart = 0;
1669 else if (newSelStart > len) newSelStart = len;
1670 if (newSelEnd < 0) newSelEnd = 0;
1671 else if (newSelEnd > len) newSelEnd = len;
1672 eet.setSelection(newSelStart, newSelEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 eet.finishInternalChanges();
1674 }
1675 }
1676
1677 /**
satok863fcd62011-06-21 17:38:02 +09001678 * Called when the user tapped or clicked a text view.
1679 * IMEs can't rely on this method being called because this was not part of the original IME
1680 * protocol, so applications with custom text editing written before this method appeared will
1681 * not call to inform the IME of this interaction.
1682 * @param focusChanged true if the user changed the focused view by this click.
1683 */
1684 public void onViewClicked(boolean focusChanged) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001685 // Intentionally empty
satok863fcd62011-06-21 17:38:02 +09001686 }
1687
1688 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001689 * Called when the application has reported a new location of its text
1690 * cursor. This is only called if explicitly requested by the input method.
1691 * The default implementation does nothing.
1692 */
1693 public void onUpdateCursor(Rect newCursor) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001694 // Intentionally empty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 }
1696
1697 /**
1698 * Close this input method's soft input area, removing it from the display.
1699 * The input method will continue running, but the user can no longer use
1700 * it to generate input by touching the screen.
1701 * @param flags Provides additional operating flags. Currently may be
1702 * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
1703 * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
1704 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001705 public void requestHideSelf(int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001706 mImm.hideSoftInputFromInputMethod(mToken, flags);
1707 }
1708
1709 /**
The Android Open Source Project4df24232009-03-05 14:34:35 -08001710 * Show the input method. This is a call back to the
1711 * IMF to handle showing the input method.
The Android Open Source Project4df24232009-03-05 14:34:35 -08001712 * @param flags Provides additional operating flags. Currently may be
1713 * 0 or have the {@link InputMethodManager#SHOW_FORCED
1714 * InputMethodManager.} bit set.
1715 */
1716 private void requestShowSelf(int flags) {
1717 mImm.showSoftInputFromInputMethod(mToken, flags);
1718 }
1719
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001720 private boolean handleBack(boolean doIt) {
1721 if (mShowInputRequested) {
Gilles Debunne34703b62011-09-08 11:16:25 -07001722 if (isExtractViewShown() && mExtractView instanceof ExtractEditLayout) {
1723 ExtractEditLayout extractEditLayout = (ExtractEditLayout) mExtractView;
1724 if (extractEditLayout.isActionModeStarted()) {
1725 if (doIt) extractEditLayout.finishActionMode();
1726 return true;
1727 }
1728 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001729 // If the soft input area is shown, back closes it and we
1730 // consume the back key.
1731 if (doIt) requestHideSelf(0);
1732 return true;
1733 } else if (mWindowVisible) {
1734 if (mCandidatesVisibility == View.VISIBLE) {
1735 // If we are showing candidates even if no input area, then
1736 // hide them.
1737 if (doIt) setCandidatesViewShown(false);
1738 } else {
1739 // If we have the window visible for some other reason --
1740 // most likely to show candidates -- then just get rid
1741 // of it. This really shouldn't happen, but just in case...
satok2f913d92012-05-10 01:48:03 +09001742 if (doIt) doHideWindow();
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001743 }
1744 return true;
1745 }
1746 return false;
1747 }
1748
The Android Open Source Project4df24232009-03-05 14:34:35 -08001749 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 * Override this to intercept key down events before they are processed by the
Quddus Chongee71b1f2012-04-12 11:49:37 -07001751 * application. If you return true, the application will not
1752 * process the event itself. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753 * will occur as if the IME had not seen the event at all.
1754 *
1755 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001756 * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
1757 * possibly hide it when the key goes up (if not canceled or long pressed). In
1758 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001759 * events to move the cursor in the extracted text view, not allowing
1760 * them to perform navigation in the underlying application.
1761 */
1762 public boolean onKeyDown(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001763 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
1764 if (handleBack(false)) {
1765 event.startTracking();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 }
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001768 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 return doMovementKey(keyCode, event, MOVEMENT_DOWN);
1771 }
1772
1773 /**
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001774 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
1775 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
1776 * the event).
1777 */
1778 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1779 return false;
1780 }
1781
1782 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001783 * Override this to intercept special key multiple events before they are
1784 * processed by the
1785 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07001786 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 * will occur as if the IME had not seen the event at all.
1788 *
1789 * <p>The default implementation always returns false, except when
1790 * in fullscreen mode, where it will consume DPAD movement
1791 * events to move the cursor in the extracted text view, not allowing
1792 * them to perform navigation in the underlying application.
1793 */
1794 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1795 return doMovementKey(keyCode, event, count);
1796 }
1797
1798 /**
1799 * Override this to intercept key up events before they are processed by the
1800 * application. If you return true, the application will not itself
Victoria Leaseb38070c2012-08-24 13:46:02 -07001801 * process the event. If you return false, the normal application processing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 * will occur as if the IME had not seen the event at all.
1803 *
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001804 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
1805 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
1806 * addition, in fullscreen mode only, it will consume DPAD movement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 * events to move the cursor in the extracted text view, not allowing
1808 * them to perform navigation in the underlying application.
1809 */
1810 public boolean onKeyUp(int keyCode, KeyEvent event) {
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001811 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isTracking()
1812 && !event.isCanceled()) {
1813 return handleBack(true);
1814 }
1815
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 return doMovementKey(keyCode, event, MOVEMENT_UP);
1817 }
1818
Victoria Leaseb38070c2012-08-24 13:46:02 -07001819 /**
1820 * Override this to intercept trackball motion events before they are
1821 * processed by the application.
1822 * If you return true, the application will not itself process the event.
1823 * If you return false, the normal application processing will occur as if
1824 * the IME had not seen the event at all.
1825 */
satokab751aa2010-09-14 19:17:36 +09001826 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001827 public boolean onTrackballEvent(MotionEvent event) {
Victoria Leaseb38070c2012-08-24 13:46:02 -07001828 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
1829 return false;
1830 }
1831
1832 /**
1833 * Override this to intercept generic motion events before they are
1834 * processed by the application.
1835 * If you return true, the application will not itself process the event.
1836 * If you return false, the normal application processing will occur as if
1837 * the IME had not seen the event at all.
1838 */
1839 @Override
1840 public boolean onGenericMotionEvent(MotionEvent event) {
1841 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 return false;
1843 }
1844
1845 public void onAppPrivateCommand(String action, Bundle data) {
1846 }
1847
The Android Open Source Project4df24232009-03-05 14:34:35 -08001848 /**
1849 * Handle a request by the system to toggle the soft input area.
1850 */
1851 private void onToggleSoftInput(int showFlags, int hideFlags) {
1852 if (DEBUG) Log.v(TAG, "toggleSoftInput()");
1853 if (isInputViewShown()) {
1854 requestHideSelf(hideFlags);
1855 } else {
1856 requestShowSelf(showFlags);
1857 }
1858 }
1859
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 static final int MOVEMENT_DOWN = -1;
1861 static final int MOVEMENT_UP = -2;
1862
1863 void reportExtractedMovement(int keyCode, int count) {
1864 int dx = 0, dy = 0;
1865 switch (keyCode) {
1866 case KeyEvent.KEYCODE_DPAD_LEFT:
1867 dx = -count;
1868 break;
1869 case KeyEvent.KEYCODE_DPAD_RIGHT:
1870 dx = count;
1871 break;
1872 case KeyEvent.KEYCODE_DPAD_UP:
1873 dy = -count;
1874 break;
1875 case KeyEvent.KEYCODE_DPAD_DOWN:
1876 dy = count;
1877 break;
1878 }
satokab751aa2010-09-14 19:17:36 +09001879 onExtractedCursorMovement(dx, dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001880 }
1881
1882 boolean doMovementKey(int keyCode, KeyEvent event, int count) {
1883 final ExtractEditText eet = mExtractEditText;
The Android Open Source Project10592532009-03-18 17:39:46 -07001884 if (isExtractViewShown() && isInputViewShown() && eet != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 // If we are in fullscreen mode, the cursor will move around
1886 // the extract edit text, but should NOT cause focus to move
1887 // to other fields.
1888 MovementMethod movement = eet.getMovementMethod();
1889 Layout layout = eet.getLayout();
1890 if (movement != null && layout != null) {
1891 // We want our own movement method to handle the key, so the
1892 // cursor will properly move in our own word wrapping.
1893 if (count == MOVEMENT_DOWN) {
1894 if (movement.onKeyDown(eet,
1895 (Spannable)eet.getText(), keyCode, event)) {
1896 reportExtractedMovement(keyCode, 1);
1897 return true;
1898 }
1899 } else if (count == MOVEMENT_UP) {
1900 if (movement.onKeyUp(eet,
1901 (Spannable)eet.getText(), keyCode, event)) {
1902 return true;
1903 }
1904 } else {
1905 if (movement.onKeyOther(eet, (Spannable)eet.getText(), event)) {
1906 reportExtractedMovement(keyCode, count);
1907 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07001908 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001909 if (movement.onKeyDown(eet,
1910 (Spannable)eet.getText(), keyCode, down)) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001911 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001912 movement.onKeyUp(eet,
1913 (Spannable)eet.getText(), keyCode, up);
1914 while (--count > 0) {
1915 movement.onKeyDown(eet,
1916 (Spannable)eet.getText(), keyCode, down);
1917 movement.onKeyUp(eet,
1918 (Spannable)eet.getText(), keyCode, up);
1919 }
1920 reportExtractedMovement(keyCode, count);
1921 }
1922 }
1923 }
1924 }
1925 // Regardless of whether the movement method handled the key,
1926 // we never allow DPAD navigation to the application.
1927 switch (keyCode) {
1928 case KeyEvent.KEYCODE_DPAD_LEFT:
1929 case KeyEvent.KEYCODE_DPAD_RIGHT:
1930 case KeyEvent.KEYCODE_DPAD_UP:
1931 case KeyEvent.KEYCODE_DPAD_DOWN:
1932 return true;
1933 }
1934 }
1935
1936 return false;
1937 }
1938
1939 /**
1940 * Send the given key event code (as defined by {@link KeyEvent}) to the
1941 * current input connection is a key down + key up event pair. The sent
1942 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
1943 * set, so that the recipient can identify them as coming from a software
1944 * input method, and
1945 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
1946 * that they don't impact the current touch mode of the UI.
1947 *
Jean Chalard405bc512012-05-29 19:12:34 +09001948 * <p>Note that it's discouraged to send such key events in normal operation;
1949 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
1950 * text fields, or for non-rich input methods. A reasonably capable software
1951 * input method should use the
1952 * {@link android.view.inputmethod.InputConnection#commitText} family of methods
1953 * to send text to an application, rather than sending key events.</p>
1954 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 * @param keyEventCode The raw key code to send, as defined by
1956 * {@link KeyEvent}.
1957 */
1958 public void sendDownUpKeyEvents(int keyEventCode) {
1959 InputConnection ic = getCurrentInputConnection();
1960 if (ic == null) return;
1961 long eventTime = SystemClock.uptimeMillis();
1962 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
Jeff Brown6b53e8d2010-11-10 16:03:06 -08001963 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001964 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
Tadashi G. Takaokacb95cd62012-10-26 17:20:59 +09001965 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -08001966 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001967 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
1968 }
1969
1970 /**
1971 * Ask the input target to execute its default action via
1972 * {@link InputConnection#performEditorAction
1973 * InputConnection.performEditorAction()}.
1974 *
1975 * @param fromEnterKey If true, this will be executed as if the user had
1976 * pressed an enter key on the keyboard, that is it will <em>not</em>
1977 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
1978 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
1979 * sent regardless of how the editor has set that flag.
1980 *
1981 * @return Returns a boolean indicating whether an action has been sent.
1982 * If false, either the editor did not specify a default action or it
1983 * does not want an action from the enter key. If true, the action was
1984 * sent (or there was no input connection at all).
1985 */
1986 public boolean sendDefaultEditorAction(boolean fromEnterKey) {
1987 EditorInfo ei = getCurrentInputEditorInfo();
1988 if (ei != null &&
1989 (!fromEnterKey || (ei.imeOptions &
1990 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
1991 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
1992 EditorInfo.IME_ACTION_NONE) {
1993 // If the enter key was pressed, and the editor has a default
1994 // action associated with pressing enter, then send it that
1995 // explicit action instead of the key event.
1996 InputConnection ic = getCurrentInputConnection();
1997 if (ic != null) {
1998 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
1999 }
2000 return true;
2001 }
2002
2003 return false;
2004 }
2005
2006 /**
2007 * Send the given UTF-16 character to the current input connection. Most
2008 * characters will be delivered simply by calling
2009 * {@link InputConnection#commitText InputConnection.commitText()} with
2010 * the character; some, however, may be handled different. In particular,
2011 * the enter character ('\n') will either be delivered as an action code
Jean Chalard405bc512012-05-29 19:12:34 +09002012 * or a raw key event, as appropriate. Consider this as a convenience
2013 * method for IMEs that do not have a full implementation of actions; a
2014 * fully complying IME will decide of the right action for each event and
2015 * will likely never call this method except maybe to handle events coming
2016 * from an actual hardware keyboard.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002017 *
2018 * @param charCode The UTF-16 character code to send.
2019 */
2020 public void sendKeyChar(char charCode) {
2021 switch (charCode) {
2022 case '\n': // Apps may be listening to an enter key to perform an action
2023 if (!sendDefaultEditorAction(true)) {
2024 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
2025 }
2026 break;
2027 default:
2028 // Make sure that digits go through any text watcher on the client side.
2029 if (charCode >= '0' && charCode <= '9') {
2030 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
2031 } else {
2032 InputConnection ic = getCurrentInputConnection();
2033 if (ic != null) {
2034 ic.commitText(String.valueOf((char) charCode), 1);
2035 }
2036 }
2037 break;
2038 }
2039 }
2040
2041 /**
2042 * This is called when the user has moved the cursor in the extracted
2043 * text view, when running in fullsreen mode. The default implementation
2044 * performs the corresponding selection change on the underlying text
2045 * editor.
2046 */
2047 public void onExtractedSelectionChanged(int start, int end) {
2048 InputConnection conn = getCurrentInputConnection();
2049 if (conn != null) {
2050 conn.setSelection(start, end);
2051 }
2052 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002053
2054 /**
2055 * @hide
2056 */
2057 public void onExtractedDeleteText(int start, int end) {
2058 InputConnection conn = getCurrentInputConnection();
2059 if (conn != null) {
2060 conn.setSelection(start, start);
2061 conn.deleteSurroundingText(0, end-start);
2062 }
2063 }
2064
2065 /**
2066 * @hide
2067 */
2068 public void onExtractedReplaceText(int start, int end, CharSequence text) {
2069 InputConnection conn = getCurrentInputConnection();
2070 if (conn != null) {
2071 conn.setComposingRegion(start, end);
2072 conn.commitText(text, 1);
2073 }
2074 }
2075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002076 /**
Gilles Debunnee300be92011-12-06 10:15:56 -08002077 * @hide
2078 */
2079 public void onExtractedSetSpan(Object span, int start, int end, int flags) {
2080 InputConnection conn = getCurrentInputConnection();
2081 if (conn != null) {
2082 if (!conn.setSelection(start, end)) return;
2083 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
2084 if (text instanceof Spannable) {
2085 ((Spannable) text).setSpan(span, 0, text.length(), flags);
2086 conn.setComposingRegion(start, end);
2087 conn.commitText(text, 1);
2088 }
2089 }
2090 }
2091
2092 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 * This is called when the user has clicked on the extracted text view,
2094 * when running in fullscreen mode. The default implementation hides
2095 * the candidates view when this happens, but only if the extracted text
2096 * editor has a vertical scroll bar because its text doesn't fit.
2097 * Re-implement this to provide whatever behavior you want.
2098 */
2099 public void onExtractedTextClicked() {
2100 if (mExtractEditText == null) {
2101 return;
2102 }
2103 if (mExtractEditText.hasVerticalScrollBar()) {
2104 setCandidatesViewShown(false);
2105 }
2106 }
Gilles Debunne39ba6d92011-11-09 05:26:26 +01002107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002108 /**
2109 * This is called when the user has performed a cursor movement in the
2110 * extracted text view, when it is running in fullscreen mode. The default
2111 * implementation hides the candidates view when a vertical movement
2112 * happens, but only if the extracted text editor has a vertical scroll bar
2113 * because its text doesn't fit.
2114 * Re-implement this to provide whatever behavior you want.
2115 * @param dx The amount of cursor movement in the x dimension.
2116 * @param dy The amount of cursor movement in the y dimension.
2117 */
2118 public void onExtractedCursorMovement(int dx, int dy) {
2119 if (mExtractEditText == null || dy == 0) {
2120 return;
2121 }
2122 if (mExtractEditText.hasVerticalScrollBar()) {
2123 setCandidatesViewShown(false);
2124 }
2125 }
2126
2127 /**
2128 * This is called when the user has selected a context menu item from the
2129 * extracted text view, when running in fullscreen mode. The default
2130 * implementation sends this action to the current InputConnection's
2131 * {@link InputConnection#performContextMenuAction(int)}, for it
2132 * to be processed in underlying "real" editor. Re-implement this to
2133 * provide whatever behavior you want.
2134 */
2135 public boolean onExtractTextContextMenuItem(int id) {
2136 InputConnection ic = getCurrentInputConnection();
2137 if (ic != null) {
2138 ic.performContextMenuAction(id);
2139 }
2140 return true;
2141 }
2142
2143 /**
2144 * Return text that can be used as a button label for the given
2145 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
2146 * if there is no action requested. Note that there is no guarantee that
2147 * the returned text will be relatively short, so you probably do not
2148 * want to use it as text on a soft keyboard key label.
2149 *
2150 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
2151 *
2152 * @return Returns a label to use, or null if there is no action.
2153 */
2154 public CharSequence getTextForImeAction(int imeOptions) {
2155 switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
2156 case EditorInfo.IME_ACTION_NONE:
2157 return null;
2158 case EditorInfo.IME_ACTION_GO:
2159 return getText(com.android.internal.R.string.ime_action_go);
2160 case EditorInfo.IME_ACTION_SEARCH:
2161 return getText(com.android.internal.R.string.ime_action_search);
2162 case EditorInfo.IME_ACTION_SEND:
2163 return getText(com.android.internal.R.string.ime_action_send);
2164 case EditorInfo.IME_ACTION_NEXT:
2165 return getText(com.android.internal.R.string.ime_action_next);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002166 case EditorInfo.IME_ACTION_DONE:
2167 return getText(com.android.internal.R.string.ime_action_done);
Dianne Hackborndea3ef72010-10-28 14:24:22 -07002168 case EditorInfo.IME_ACTION_PREVIOUS:
2169 return getText(com.android.internal.R.string.ime_action_previous);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002170 default:
2171 return getText(com.android.internal.R.string.ime_action_default);
2172 }
2173 }
2174
2175 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07002176 * Called when the fullscreen-mode extracting editor info has changed,
2177 * to determine whether the extracting (extract text and candidates) portion
2178 * of the UI should be shown. The standard implementation hides or shows
2179 * the extract area depending on whether it makes sense for the
2180 * current editor. In particular, a {@link InputType#TYPE_NULL}
2181 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
2182 * turn off the extract area since there is no text to be shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002183 */
The Android Open Source Project10592532009-03-18 17:39:46 -07002184 public void onUpdateExtractingVisibility(EditorInfo ei) {
2185 if (ei.inputType == InputType.TYPE_NULL ||
2186 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
2187 // No reason to show extract UI!
2188 setExtractViewShown(false);
2189 return;
2190 }
2191
2192 setExtractViewShown(true);
2193 }
2194
2195 /**
2196 * Called when the fullscreen-mode extracting editor info has changed,
2197 * to update the state of its UI such as the action buttons shown.
2198 * You do not need to deal with this if you are using the standard
2199 * full screen extract UI. If replacing it, you will need to re-implement
2200 * this to put the appropriate action button in your own UI and handle it,
2201 * and perform any other changes.
2202 *
2203 * <p>The standard implementation turns on or off its accessory area
2204 * depending on whether there is an action button, and hides or shows
2205 * the entire extract area depending on whether it makes sense for the
2206 * current editor. In particular, a {@link InputType#TYPE_NULL} or
2207 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
2208 * extract area since there is no text to be shown.
2209 */
2210 public void onUpdateExtractingViews(EditorInfo ei) {
2211 if (!isExtractViewShown()) {
2212 return;
2213 }
2214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002215 if (mExtractAccessories == null) {
2216 return;
2217 }
2218 final boolean hasAction = ei.actionLabel != null || (
2219 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
The Android Open Source Project10592532009-03-18 17:39:46 -07002220 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
2221 ei.inputType != InputType.TYPE_NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002222 if (hasAction) {
2223 mExtractAccessories.setVisibility(View.VISIBLE);
Steve Kondik59eb6912009-09-07 22:53:34 -04002224 if (mExtractAction != null) {
2225 if (ei.actionLabel != null) {
2226 mExtractAction.setText(ei.actionLabel);
2227 } else {
2228 mExtractAction.setText(getTextForImeAction(ei.imeOptions));
2229 }
2230 mExtractAction.setOnClickListener(mActionClickListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002231 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002232 } else {
2233 mExtractAccessories.setVisibility(View.GONE);
Steve Kondik59eb6912009-09-07 22:53:34 -04002234 if (mExtractAction != null) {
2235 mExtractAction.setOnClickListener(null);
2236 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002237 }
2238 }
2239
2240 /**
2241 * This is called when, while currently displayed in extract mode, the
2242 * current input target changes. The default implementation will
2243 * auto-hide the IME if the new target is not a full editor, since this
Ken Wakasaf76a50c2012-03-09 19:56:35 +09002244 * can be a confusing experience for the user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002245 */
2246 public void onExtractingInputChanged(EditorInfo ei) {
2247 if (ei.inputType == InputType.TYPE_NULL) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002248 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002249 }
2250 }
2251
2252 void startExtractingText(boolean inputChanged) {
2253 final ExtractEditText eet = mExtractEditText;
2254 if (eet != null && getCurrentInputStarted()
2255 && isFullscreenMode()) {
2256 mExtractedToken++;
2257 ExtractedTextRequest req = new ExtractedTextRequest();
2258 req.token = mExtractedToken;
2259 req.flags = InputConnection.GET_TEXT_WITH_STYLES;
2260 req.hintMaxLines = 10;
2261 req.hintMaxChars = 10000;
Amith Yamasaniba4d93f2009-08-19 18:27:56 -07002262 InputConnection ic = getCurrentInputConnection();
2263 mExtractedText = ic == null? null
2264 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
Amith Yamasania8b00c82010-03-05 15:41:31 -08002265 if (mExtractedText == null || ic == null) {
2266 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
2267 + mExtractedText + ", input connection = " + ic);
2268 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002269 final EditorInfo ei = getCurrentInputEditorInfo();
2270
2271 try {
2272 eet.startInternalChanges();
The Android Open Source Project10592532009-03-18 17:39:46 -07002273 onUpdateExtractingVisibility(ei);
2274 onUpdateExtractingViews(ei);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002275 int inputType = ei.inputType;
2276 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
2277 == EditorInfo.TYPE_CLASS_TEXT) {
2278 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
2279 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2280 }
2281 }
2282 eet.setInputType(inputType);
2283 eet.setHint(ei.hintText);
2284 if (mExtractedText != null) {
2285 eet.setEnabled(true);
2286 eet.setExtractedText(mExtractedText);
2287 } else {
2288 eet.setEnabled(false);
2289 eet.setText("");
2290 }
2291 } finally {
2292 eet.finishInternalChanges();
2293 }
2294
2295 if (inputChanged) {
2296 onExtractingInputChanged(ei);
2297 }
2298 }
2299 }
satokab751aa2010-09-14 19:17:36 +09002300
2301 // TODO: Handle the subtype change event
2302 /**
2303 * Called when the subtype was changed.
2304 * @param newSubtype the subtype which is being changed to.
2305 */
2306 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
2307 if (DEBUG) {
2308 int nameResId = newSubtype.getNameResId();
satok9ef02832010-11-04 21:17:48 +09002309 String mode = newSubtype.getMode();
satokab751aa2010-09-14 19:17:36 +09002310 String output = "changeInputMethodSubtype:"
2311 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
satok9ef02832010-11-04 21:17:48 +09002312 + mode + ","
satokab751aa2010-09-14 19:17:36 +09002313 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
2314 Log.v(TAG, "--- " + output);
2315 }
2316 }
2317
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002318 /**
2319 * Performs a dump of the InputMethodService's internal state. Override
2320 * to add your own information to the dump.
2321 */
2322 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2323 final Printer p = new PrintWriterPrinter(fout);
2324 p.println("Input method service state for " + this + ":");
2325 p.println(" mWindowCreated=" + mWindowCreated
The Android Open Source Project10592532009-03-18 17:39:46 -07002326 + " mWindowAdded=" + mWindowAdded);
2327 p.println(" mWindowVisible=" + mWindowVisible
2328 + " mWindowWasVisible=" + mWindowWasVisible
2329 + " mInShowWindow=" + mInShowWindow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002330 p.println(" Configuration=" + getResources().getConfiguration());
2331 p.println(" mToken=" + mToken);
2332 p.println(" mInputBinding=" + mInputBinding);
2333 p.println(" mInputConnection=" + mInputConnection);
2334 p.println(" mStartedInputConnection=" + mStartedInputConnection);
2335 p.println(" mInputStarted=" + mInputStarted
2336 + " mInputViewStarted=" + mInputViewStarted
2337 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
2338
2339 if (mInputEditorInfo != null) {
2340 p.println(" mInputEditorInfo:");
2341 mInputEditorInfo.dump(p, " ");
2342 } else {
2343 p.println(" mInputEditorInfo: null");
2344 }
2345
2346 p.println(" mShowInputRequested=" + mShowInputRequested
2347 + " mLastShowInputRequested=" + mLastShowInputRequested
2348 + " mShowInputForced=" + mShowInputForced
2349 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
2350 p.println(" mCandidatesVisibility=" + mCandidatesVisibility
2351 + " mFullscreenApplied=" + mFullscreenApplied
The Android Open Source Project10592532009-03-18 17:39:46 -07002352 + " mIsFullscreen=" + mIsFullscreen
2353 + " mExtractViewHidden=" + mExtractViewHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002354
2355 if (mExtractedText != null) {
2356 p.println(" mExtractedText:");
2357 p.println(" text=" + mExtractedText.text.length() + " chars"
2358 + " startOffset=" + mExtractedText.startOffset);
2359 p.println(" selectionStart=" + mExtractedText.selectionStart
2360 + " selectionEnd=" + mExtractedText.selectionEnd
2361 + " flags=0x" + Integer.toHexString(mExtractedText.flags));
2362 } else {
2363 p.println(" mExtractedText: null");
2364 }
2365 p.println(" mExtractedToken=" + mExtractedToken);
2366 p.println(" mIsInputViewShown=" + mIsInputViewShown
2367 + " mStatusIcon=" + mStatusIcon);
2368 p.println("Last computed insets:");
2369 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
2370 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
Jeff Brownfbf09772011-01-16 14:06:57 -08002371 + " touchableInsets=" + mTmpInsets.touchableInsets
2372 + " touchableRegion=" + mTmpInsets.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002373 }
2374}