1 /* 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 17 package android.inputmethodservice; 18 19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 21 22 import android.annotation.CallSuper; 23 import android.annotation.DrawableRes; 24 import android.annotation.IntDef; 25 import android.annotation.MainThread; 26 import android.app.ActivityManager; 27 import android.app.Dialog; 28 import android.content.Context; 29 import android.content.res.Configuration; 30 import android.content.res.Resources; 31 import android.content.res.TypedArray; 32 import android.database.ContentObserver; 33 import android.graphics.Rect; 34 import android.graphics.Region; 35 import android.net.Uri; 36 import android.os.Bundle; 37 import android.os.Handler; 38 import android.os.IBinder; 39 import android.os.ResultReceiver; 40 import android.os.SystemClock; 41 import android.provider.Settings; 42 import android.text.InputType; 43 import android.text.Layout; 44 import android.text.Spannable; 45 import android.text.method.MovementMethod; 46 import android.util.Log; 47 import android.util.PrintWriterPrinter; 48 import android.util.Printer; 49 import android.view.Gravity; 50 import android.view.KeyCharacterMap; 51 import android.view.KeyEvent; 52 import android.view.LayoutInflater; 53 import android.view.MotionEvent; 54 import android.view.View; 55 import android.view.ViewGroup; 56 import android.view.ViewTreeObserver; 57 import android.view.Window; 58 import android.view.WindowManager; 59 import android.view.WindowManager.BadTokenException; 60 import android.view.animation.AnimationUtils; 61 import android.view.inputmethod.CompletionInfo; 62 import android.view.inputmethod.CursorAnchorInfo; 63 import android.view.inputmethod.EditorInfo; 64 import android.view.inputmethod.ExtractedText; 65 import android.view.inputmethod.ExtractedTextRequest; 66 import android.view.inputmethod.InputBinding; 67 import android.view.inputmethod.InputConnection; 68 import android.view.inputmethod.InputMethod; 69 import android.view.inputmethod.InputMethodManager; 70 import android.view.inputmethod.InputMethodSubtype; 71 import android.widget.FrameLayout; 72 import android.widget.ImageButton; 73 import android.widget.LinearLayout; 74 import android.widget.TextView; 75 76 import java.io.FileDescriptor; 77 import java.io.PrintWriter; 78 import java.lang.annotation.Retention; 79 import java.lang.annotation.RetentionPolicy; 80 81 /** 82 * InputMethodService provides a standard implementation of an InputMethod, 83 * which final implementations can derive from and customize. See the 84 * base class {@link AbstractInputMethodService} and the {@link InputMethod} 85 * interface for more information on the basics of writing input methods. 86 * 87 * <p>In addition to the normal Service lifecycle methods, this class 88 * introduces some new specific callbacks that most subclasses will want 89 * to make use of:</p> 90 * <ul> 91 * <li> {@link #onInitializeInterface()} for user-interface initialization, 92 * in particular to deal with configuration changes while the service is 93 * running. 94 * <li> {@link #onBindInput} to find out about switching to a new client. 95 * <li> {@link #onStartInput} to deal with an input session starting with 96 * the client. 97 * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()}, 98 * and {@link #onCreateExtractTextView()} for non-demand generation of the UI. 99 * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input 100 * starting within the input area of the IME. 101 * </ul> 102 * 103 * <p>An input method has significant discretion in how it goes about its 104 * work: the {@link android.inputmethodservice.InputMethodService} provides 105 * a basic framework for standard UI elements (input view, candidates view, 106 * and running in fullscreen mode), but it is up to a particular implementor 107 * to decide how to use them. For example, one input method could implement 108 * an input area with a keyboard, another could allow the user to draw text, 109 * while a third could have no input area (and thus not be visible to the 110 * user) but instead listen to audio and perform text to speech conversion.</p> 111 * 112 * <p>In the implementation provided here, all of these elements are placed 113 * together in a single window managed by the InputMethodService. It will 114 * execute callbacks as it needs information about them, and provides APIs for 115 * programmatic control over them. They layout of these elements is explicitly 116 * defined:</p> 117 * 118 * <ul> 119 * <li>The soft input view, if available, is placed at the bottom of the 120 * screen. 121 * <li>The candidates view, if currently shown, is placed above the soft 122 * input view. 123 * <li>If not running fullscreen, the application is moved or resized to be 124 * above these views; if running fullscreen, the window will completely cover 125 * the application and its top part will contain the extract text of what is 126 * currently being edited by the application. 127 * </ul> 128 * 129 * 130 * <a name="SoftInputView"></a> 131 * <h3>Soft Input View</h3> 132 * 133 * <p>Central to most input methods is the soft input view. This is where most 134 * user interaction occurs: pressing on soft keys, drawing characters, or 135 * however else your input method wants to generate text. Most implementations 136 * will simply have their own view doing all of this work, and return a new 137 * instance of it when {@link #onCreateInputView()} is called. At that point, 138 * as long as the input view is visible, you will see user interaction in 139 * that view and can call back on the InputMethodService to interact with the 140 * application as appropriate.</p> 141 * 142 * <p>There are some situations where you want to decide whether or not your 143 * soft input view should be shown to the user. This is done by implementing 144 * the {@link #onEvaluateInputViewShown()} to return true or false based on 145 * whether it should be shown in the current environment. If any of your 146 * state has changed that may impact this, call 147 * {@link #updateInputViewShown()} to have it re-evaluated. The default 148 * implementation always shows the input view unless there is a hard 149 * keyboard available, which is the appropriate behavior for most input 150 * methods.</p> 151 * 152 * 153 * <a name="CandidatesView"></a> 154 * <h3>Candidates View</h3> 155 * 156 * <p>Often while the user is generating raw text, an input method wants to 157 * provide them with a list of possible interpretations of that text that can 158 * be selected for use. This is accomplished with the candidates view, and 159 * like the soft input view you implement {@link #onCreateCandidatesView()} 160 * to instantiate your own view implementing your candidates UI.</p> 161 * 162 * <p>Management of the candidates view is a little different than the input 163 * view, because the candidates view tends to be more transient, being shown 164 * only when there are possible candidates for the current text being entered 165 * by the user. To control whether the candidates view is shown, you use 166 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate 167 * view tends to be shown and hidden a lot, it does not impact the application 168 * UI in the same way as the soft input view: it will never cause application 169 * windows to resize, only cause them to be panned if needed for the user to 170 * see the current focus.</p> 171 * 172 * 173 * <a name="FullscreenMode"></a> 174 * <h3>Fullscreen Mode</h3> 175 * 176 * <p>Sometimes your input method UI is too large to integrate with the 177 * application UI, so you just want to take over the screen. This is 178 * accomplished by switching to full-screen mode, causing the input method 179 * window to fill the entire screen and add its own "extracted text" editor 180 * showing the user the text that is being typed. Unlike the other UI elements, 181 * there is a standard implementation for the extract editor that you should 182 * not need to change. The editor is placed at the top of the IME, above the 183 * input and candidates views.</p> 184 * 185 * <p>Similar to the input view, you control whether the IME is running in 186 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()} 187 * to return true or false based on 188 * whether it should be fullscreen in the current environment. If any of your 189 * state has changed that may impact this, call 190 * {@link #updateFullscreenMode()} to have it re-evaluated. The default 191 * implementation selects fullscreen mode when the screen is in a landscape 192 * orientation, which is appropriate behavior for most input methods that have 193 * a significant input area.</p> 194 * 195 * <p>When in fullscreen mode, you have some special requirements because the 196 * user can not see the application UI. In particular, you should implement 197 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions 198 * generated by your application, typically in your candidates view like you 199 * would normally show candidates. 200 * 201 * 202 * <a name="GeneratingText"></a> 203 * <h3>Generating Text</h3> 204 * 205 * <p>The key part of an IME is of course generating text for the application. 206 * This is done through calls to the 207 * {@link android.view.inputmethod.InputConnection} interface to the 208 * application, which can be retrieved from {@link #getCurrentInputConnection()}. 209 * This interface allows you to generate raw key events or, if the target 210 * supports it, directly edit in strings of candidates and committed text.</p> 211 * 212 * <p>Information about what the target is expected and supports can be found 213 * through the {@link android.view.inputmethod.EditorInfo} class, which is 214 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most 215 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType 216 * EditorInfo.inputType}; in particular, if this is 217 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL}, 218 * then the target does not support complex edits and you need to only deliver 219 * raw key events to it. An input method will also want to look at other 220 * values here, to for example detect password mode, auto complete text views, 221 * phone number entry, etc.</p> 222 * 223 * <p>When the user switches between input targets, you will receive calls to 224 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}. 225 * You can use these to reset and initialize your input state for the current 226 * target. For example, you will often want to clear any input state, and 227 * update a soft keyboard to be appropriate for the new inputType.</p> 228 * 229 * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground 230 * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation 231 * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation 232 */ 233 public class InputMethodService extends AbstractInputMethodService { 234 static final String TAG = "InputMethodService"; 235 static final boolean DEBUG = false; 236 237 /** 238 * The back button will close the input window. 239 */ 240 public static final int BACK_DISPOSITION_DEFAULT = 0; // based on window 241 242 /** 243 * This input method will not consume the back key. 244 */ 245 public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // back 246 247 /** 248 * This input method will consume the back key. 249 */ 250 public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // down 251 252 /** 253 * @hide 254 * The IME is active. It may or may not be visible. 255 */ 256 public static final int IME_ACTIVE = 0x1; 257 258 /** 259 * @hide 260 * The IME is visible. 261 */ 262 public static final int IME_VISIBLE = 0x2; 263 264 InputMethodManager mImm; 265 266 int mTheme = 0; 267 boolean mHardwareAccelerated = false; 268 269 LayoutInflater mInflater; 270 TypedArray mThemeAttrs; 271 View mRootView; 272 SoftInputWindow mWindow; 273 boolean mInitialized; 274 boolean mWindowCreated; 275 boolean mWindowAdded; 276 boolean mWindowVisible; 277 boolean mWindowWasVisible; 278 boolean mInShowWindow; 279 ViewGroup mFullscreenArea; 280 FrameLayout mExtractFrame; 281 FrameLayout mCandidatesFrame; 282 FrameLayout mInputFrame; 283 284 IBinder mToken; 285 286 InputBinding mInputBinding; 287 InputConnection mInputConnection; 288 boolean mInputStarted; 289 boolean mInputViewStarted; 290 boolean mCandidatesViewStarted; 291 InputConnection mStartedInputConnection; 292 EditorInfo mInputEditorInfo; 293 294 int mShowInputFlags; 295 boolean mShowInputRequested; 296 boolean mLastShowInputRequested; 297 int mCandidatesVisibility; 298 CompletionInfo[] mCurCompletions; 299 300 boolean mFullscreenApplied; 301 boolean mIsFullscreen; 302 View mExtractView; 303 boolean mExtractViewHidden; 304 ExtractEditText mExtractEditText; 305 ViewGroup mExtractAccessories; 306 View mExtractAction; 307 ExtractedText mExtractedText; 308 int mExtractedToken; 309 310 View mInputView; 311 boolean mIsInputViewShown; 312 313 int mStatusIcon; 314 int mBackDisposition; 315 316 /** 317 * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we 318 * have not shown our own window yet. In this situation, the previous inset continues to be 319 * shown as an empty region until it is explicitly updated. Basically we can trigger the update 320 * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}. 321 */ 322 boolean mShouldClearInsetOfPreviousIme; 323 324 final Insets mTmpInsets = new Insets(); 325 final int[] mTmpLocation = new int[2]; 326 327 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = 328 new ViewTreeObserver.OnComputeInternalInsetsListener() { 329 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 330 if (isExtractViewShown()) { 331 // In true fullscreen mode, we just say the window isn't covering 332 // any content so we don't impact whatever is behind. 333 View decor = getWindow().getWindow().getDecorView(); 334 info.contentInsets.top = info.visibleInsets.top 335 = decor.getHeight(); 336 info.touchableRegion.setEmpty(); 337 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 338 } else { 339 onComputeInsets(mTmpInsets); 340 info.contentInsets.top = mTmpInsets.contentTopInsets; 341 info.visibleInsets.top = mTmpInsets.visibleTopInsets; 342 info.touchableRegion.set(mTmpInsets.touchableRegion); 343 info.setTouchableInsets(mTmpInsets.touchableInsets); 344 } 345 } 346 }; 347 348 final View.OnClickListener mActionClickListener = new View.OnClickListener() { 349 public void onClick(View v) { 350 final EditorInfo ei = getCurrentInputEditorInfo(); 351 final InputConnection ic = getCurrentInputConnection(); 352 if (ei != null && ic != null) { 353 if (ei.actionId != 0) { 354 ic.performEditorAction(ei.actionId); 355 } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION) 356 != EditorInfo.IME_ACTION_NONE) { 357 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION); 358 } 359 } 360 } 361 }; 362 363 /** 364 * Concrete implementation of 365 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides 366 * all of the standard behavior for an input method. 367 */ 368 public class InputMethodImpl extends AbstractInputMethodImpl { 369 /** 370 * Take care of attaching the given window token provided by the system. 371 */ 372 public void attachToken(IBinder token) { 373 if (mToken == null) { 374 mToken = token; 375 mWindow.setToken(token); 376 } 377 } 378 379 /** 380 * Handle a new input binding, calling 381 * {@link InputMethodService#onBindInput InputMethodService.onBindInput()} 382 * when done. 383 */ 384 public void bindInput(InputBinding binding) { 385 mInputBinding = binding; 386 mInputConnection = binding.getConnection(); 387 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding 388 + " ic=" + mInputConnection); 389 InputConnection ic = getCurrentInputConnection(); 390 if (ic != null) ic.reportFullscreenMode(mIsFullscreen); 391 initialize(); 392 onBindInput(); 393 } 394 395 /** 396 * Clear the current input binding. 397 */ 398 public void unbindInput() { 399 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding 400 + " ic=" + mInputConnection); 401 onUnbindInput(); 402 mInputBinding = null; 403 mInputConnection = null; 404 } 405 406 public void startInput(InputConnection ic, EditorInfo attribute) { 407 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute); 408 doStartInput(ic, attribute, false); 409 } 410 411 public void restartInput(InputConnection ic, EditorInfo attribute) { 412 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute); 413 doStartInput(ic, attribute, true); 414 } 415 416 /** 417 * Handle a request by the system to hide the soft input area. 418 */ 419 public void hideSoftInput(int flags, ResultReceiver resultReceiver) { 420 if (DEBUG) Log.v(TAG, "hideSoftInput()"); 421 boolean wasVis = isInputViewShown(); 422 mShowInputFlags = 0; 423 mShowInputRequested = false; 424 doHideWindow(); 425 clearInsetOfPreviousIme(); 426 if (resultReceiver != null) { 427 resultReceiver.send(wasVis != isInputViewShown() 428 ? InputMethodManager.RESULT_HIDDEN 429 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN 430 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null); 431 } 432 } 433 434 /** 435 * Handle a request by the system to show the soft input area. 436 */ 437 public void showSoftInput(int flags, ResultReceiver resultReceiver) { 438 if (DEBUG) Log.v(TAG, "showSoftInput()"); 439 boolean wasVis = isInputViewShown(); 440 if (dispatchOnShowInputRequested(flags, false)) { 441 try { 442 showWindow(true); 443 } catch (BadTokenException e) { 444 // We have ignored BadTokenException here since Jelly Bean MR-2 (API Level 18). 445 // We could ignore BadTokenException in InputMethodService#showWindow() instead, 446 // but it may break assumptions for those who override #showWindow() that we can 447 // detect errors in #showWindow() by checking BadTokenException. 448 // TODO: Investigate its feasibility. Update JavaDoc of #showWindow() of 449 // whether it's OK to override #showWindow() or not. 450 } 451 } 452 clearInsetOfPreviousIme(); 453 // If user uses hard keyboard, IME button should always be shown. 454 boolean showing = isInputViewShown(); 455 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0), 456 mBackDisposition); 457 if (resultReceiver != null) { 458 resultReceiver.send(wasVis != isInputViewShown() 459 ? InputMethodManager.RESULT_SHOWN 460 : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN 461 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null); 462 } 463 } 464 465 public void changeInputMethodSubtype(InputMethodSubtype subtype) { 466 onCurrentInputMethodSubtypeChanged(subtype); 467 } 468 } 469 470 /** 471 * Concrete implementation of 472 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides 473 * all of the standard behavior for an input method session. 474 */ 475 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl { 476 public void finishInput() { 477 if (!isEnabled()) { 478 return; 479 } 480 if (DEBUG) Log.v(TAG, "finishInput() in " + this); 481 doFinishInput(); 482 } 483 484 /** 485 * Call {@link InputMethodService#onDisplayCompletions 486 * InputMethodService.onDisplayCompletions()}. 487 */ 488 public void displayCompletions(CompletionInfo[] completions) { 489 if (!isEnabled()) { 490 return; 491 } 492 mCurCompletions = completions; 493 onDisplayCompletions(completions); 494 } 495 496 /** 497 * Call {@link InputMethodService#onUpdateExtractedText 498 * InputMethodService.onUpdateExtractedText()}. 499 */ 500 public void updateExtractedText(int token, ExtractedText text) { 501 if (!isEnabled()) { 502 return; 503 } 504 onUpdateExtractedText(token, text); 505 } 506 507 /** 508 * Call {@link InputMethodService#onUpdateSelection 509 * InputMethodService.onUpdateSelection()}. 510 */ 511 public void updateSelection(int oldSelStart, int oldSelEnd, 512 int newSelStart, int newSelEnd, 513 int candidatesStart, int candidatesEnd) { 514 if (!isEnabled()) { 515 return; 516 } 517 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd, 518 newSelStart, newSelEnd, candidatesStart, candidatesEnd); 519 } 520 521 @Override 522 public void viewClicked(boolean focusChanged) { 523 if (!isEnabled()) { 524 return; 525 } 526 InputMethodService.this.onViewClicked(focusChanged); 527 } 528 529 /** 530 * Call {@link InputMethodService#onUpdateCursor 531 * InputMethodService.onUpdateCursor()}. 532 */ 533 public void updateCursor(Rect newCursor) { 534 if (!isEnabled()) { 535 return; 536 } 537 InputMethodService.this.onUpdateCursor(newCursor); 538 } 539 540 /** 541 * Call {@link InputMethodService#onAppPrivateCommand 542 * InputMethodService.onAppPrivateCommand()}. 543 */ 544 public void appPrivateCommand(String action, Bundle data) { 545 if (!isEnabled()) { 546 return; 547 } 548 InputMethodService.this.onAppPrivateCommand(action, data); 549 } 550 551 /** 552 * 553 */ 554 public void toggleSoftInput(int showFlags, int hideFlags) { 555 InputMethodService.this.onToggleSoftInput(showFlags, hideFlags); 556 } 557 558 /** 559 * Call {@link InputMethodService#onUpdateCursorAnchorInfo 560 * InputMethodService.onUpdateCursorAnchorInfo()}. 561 */ 562 public void updateCursorAnchorInfo(CursorAnchorInfo info) { 563 if (!isEnabled()) { 564 return; 565 } 566 InputMethodService.this.onUpdateCursorAnchorInfo(info); 567 } 568 } 569 570 /** 571 * Information about where interesting parts of the input method UI appear. 572 */ 573 public static final class Insets { 574 /** 575 * This is the top part of the UI that is the main content. It is 576 * used to determine the basic space needed, to resize/pan the 577 * application behind. It is assumed that this inset does not 578 * change very much, since any change will cause a full resize/pan 579 * of the application behind. This value is relative to the top edge 580 * of the input method window. 581 */ 582 public int contentTopInsets; 583 584 /** 585 * This is the top part of the UI that is visibly covering the 586 * application behind it. This provides finer-grained control over 587 * visibility, allowing you to change it relatively frequently (such 588 * as hiding or showing candidates) without disrupting the underlying 589 * UI too much. For example, this will never resize the application 590 * UI, will only pan if needed to make the current focus visible, and 591 * will not aggressively move the pan position when this changes unless 592 * needed to make the focus visible. This value is relative to the top edge 593 * of the input method window. 594 */ 595 public int visibleTopInsets; 596 597 /** 598 * This is the region of the UI that is touchable. It is used when 599 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}. 600 * The region should be specified relative to the origin of the window frame. 601 */ 602 public final Region touchableRegion = new Region(); 603 604 /** 605 * Option for {@link #touchableInsets}: the entire window frame 606 * can be touched. 607 */ 608 public static final int TOUCHABLE_INSETS_FRAME 609 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; 610 611 /** 612 * Option for {@link #touchableInsets}: the area inside of 613 * the content insets can be touched. 614 */ 615 public static final int TOUCHABLE_INSETS_CONTENT 616 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; 617 618 /** 619 * Option for {@link #touchableInsets}: the area inside of 620 * the visible insets can be touched. 621 */ 622 public static final int TOUCHABLE_INSETS_VISIBLE 623 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE; 624 625 /** 626 * Option for {@link #touchableInsets}: the region specified by 627 * {@link #touchableRegion} can be touched. 628 */ 629 public static final int TOUCHABLE_INSETS_REGION 630 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; 631 632 /** 633 * Determine which area of the window is touchable by the user. May 634 * be one of: {@link #TOUCHABLE_INSETS_FRAME}, 635 * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE}, 636 * or {@link #TOUCHABLE_INSETS_REGION}. 637 */ 638 public int touchableInsets; 639 } 640 641 /** 642 * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}. 643 * 644 * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API. 645 * Basically this functionality still needs to be considered as implementation details.</p> 646 */ 647 @MainThread 648 private static final class SettingsObserver extends ContentObserver { 649 @Retention(RetentionPolicy.SOURCE) 650 @IntDef({ 651 ShowImeWithHardKeyboardType.UNKNOWN, 652 ShowImeWithHardKeyboardType.FALSE, 653 ShowImeWithHardKeyboardType.TRUE, 654 }) 655 private @interface ShowImeWithHardKeyboardType { 656 int UNKNOWN = 0; 657 int FALSE = 1; 658 int TRUE = 2; 659 } 660 @ShowImeWithHardKeyboardType 661 private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN; 662 663 private final InputMethodService mService; 664 665 private SettingsObserver(InputMethodService service) { 666 super(new Handler(service.getMainLooper())); 667 mService = service; 668 } 669 670 /** 671 * A factory method that internally enforces two-phase initialization to make sure that the 672 * object reference will not be escaped until the object is properly constructed. 673 * 674 * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread. Hence 675 * this enforcement of two-phase initialization may be unnecessary at the moment.</p> 676 * 677 * @param service {@link InputMethodService} that needs to receive the callback. 678 * @return {@link SettingsObserver} that is already registered to 679 * {@link android.content.ContentResolver}. The caller must call 680 * {@link SettingsObserver#unregister()}. 681 */ 682 public static SettingsObserver createAndRegister(InputMethodService service) { 683 final SettingsObserver observer = new SettingsObserver(service); 684 // The observer is properly constructed. Let's start accepting the event. 685 service.getContentResolver().registerContentObserver( 686 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), 687 false, observer); 688 return observer; 689 } 690 691 void unregister() { 692 mService.getContentResolver().unregisterContentObserver(this); 693 } 694 695 private boolean shouldShowImeWithHardKeyboard() { 696 // Lazily initialize as needed. 697 if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) { 698 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(), 699 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ? 700 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE; 701 } 702 switch (mShowImeWithHardKeyboard) { 703 case ShowImeWithHardKeyboardType.TRUE: 704 return true; 705 case ShowImeWithHardKeyboardType.FALSE: 706 return false; 707 default: 708 Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard); 709 return false; 710 } 711 } 712 713 @Override 714 public void onChange(boolean selfChange, Uri uri) { 715 final Uri showImeWithHardKeyboardUri = 716 Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD); 717 if (showImeWithHardKeyboardUri.equals(uri)) { 718 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(), 719 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ? 720 ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE; 721 // In Android M and prior, state change of 722 // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered 723 // #onConfigurationChanged(). For compatibility reasons, we reset the internal 724 // state as if configuration was changed. 725 mService.resetStateForNewConfiguration(); 726 } 727 } 728 729 @Override 730 public String toString() { 731 return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}"; 732 } 733 } 734 private SettingsObserver mSettingsObserver; 735 736 /** 737 * You can call this to customize the theme used by your IME's window. 738 * This theme should typically be one that derives from 739 * {@link android.R.style#Theme_InputMethod}, which is the default theme 740 * you will get. This must be set before {@link #onCreate}, so you 741 * will typically call it in your constructor with the resource ID 742 * of your custom theme. 743 */ 744 @Override 745 public void setTheme(int theme) { 746 if (mWindow != null) { 747 throw new IllegalStateException("Must be called before onCreate()"); 748 } 749 mTheme = theme; 750 } 751 752 /** 753 * You can call this to try to enable hardware accelerated drawing for 754 * your IME. This must be set before {@link #onCreate}, so you 755 * will typically call it in your constructor. It is not always possible 756 * to use hardware accelerated drawing in an IME (for example on low-end 757 * devices that do not have the resources to support this), so the call 758 * returns true if it succeeds otherwise false if you will need to draw 759 * in software. You must be able to handle either case. 760 * 761 * @deprecated Starting in API 21, hardware acceleration is always enabled 762 * on capable devices. 763 */ 764 public boolean enableHardwareAcceleration() { 765 if (mWindow != null) { 766 throw new IllegalStateException("Must be called before onCreate()"); 767 } 768 if (ActivityManager.isHighEndGfx()) { 769 mHardwareAccelerated = true; 770 return true; 771 } 772 return false; 773 } 774 775 @Override public void onCreate() { 776 mTheme = Resources.selectSystemTheme(mTheme, 777 getApplicationInfo().targetSdkVersion, 778 android.R.style.Theme_InputMethod, 779 android.R.style.Theme_Holo_InputMethod, 780 android.R.style.Theme_DeviceDefault_InputMethod, 781 android.R.style.Theme_DeviceDefault_InputMethod); 782 super.setTheme(mTheme); 783 super.onCreate(); 784 mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); 785 mSettingsObserver = SettingsObserver.createAndRegister(this); 786 // If the previous IME has occupied non-empty inset in the screen, we need to decide whether 787 // we continue to use the same size of the inset or update it 788 mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0); 789 mInflater = (LayoutInflater)getSystemService( 790 Context.LAYOUT_INFLATER_SERVICE); 791 mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState, 792 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false); 793 if (mHardwareAccelerated) { 794 mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); 795 } 796 initViews(); 797 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT); 798 } 799 800 /** 801 * This is a hook that subclasses can use to perform initialization of 802 * their interface. It is called for you prior to any of your UI objects 803 * being created, both after the service is first created and after a 804 * configuration change happens. 805 */ 806 public void onInitializeInterface() { 807 // Intentionally empty 808 } 809 810 void initialize() { 811 if (!mInitialized) { 812 mInitialized = true; 813 onInitializeInterface(); 814 } 815 } 816 817 void initViews() { 818 mInitialized = false; 819 mWindowCreated = false; 820 mShowInputRequested = false; 821 mShowInputFlags = 0; 822 823 mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService); 824 mRootView = mInflater.inflate( 825 com.android.internal.R.layout.input_method, null); 826 mRootView.setSystemUiVisibility( 827 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 828 mWindow.setContentView(mRootView); 829 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer); 830 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 831 if (Settings.Global.getInt(getContentResolver(), 832 Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) { 833 mWindow.getWindow().setWindowAnimations( 834 com.android.internal.R.style.Animation_InputMethodFancy); 835 } 836 mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea); 837 mExtractViewHidden = false; 838 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea); 839 mExtractView = null; 840 mExtractEditText = null; 841 mExtractAccessories = null; 842 mExtractAction = null; 843 mFullscreenApplied = false; 844 845 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea); 846 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea); 847 mInputView = null; 848 mIsInputViewShown = false; 849 850 mExtractFrame.setVisibility(View.GONE); 851 mCandidatesVisibility = getCandidatesHiddenVisibility(); 852 mCandidatesFrame.setVisibility(mCandidatesVisibility); 853 mInputFrame.setVisibility(View.GONE); 854 } 855 856 @Override public void onDestroy() { 857 super.onDestroy(); 858 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 859 mInsetsComputer); 860 doFinishInput(); 861 if (mWindowAdded) { 862 // Disable exit animation for the current IME window 863 // to avoid the race condition between the exit and enter animations 864 // when the current IME is being switched to another one. 865 mWindow.getWindow().setWindowAnimations(0); 866 mWindow.dismiss(); 867 } 868 if (mSettingsObserver != null) { 869 mSettingsObserver.unregister(); 870 mSettingsObserver = null; 871 } 872 } 873 874 /** 875 * Take care of handling configuration changes. Subclasses of 876 * InputMethodService generally don't need to deal directly with 877 * this on their own; the standard implementation here takes care of 878 * regenerating the input method UI as a result of the configuration 879 * change, so you can rely on your {@link #onCreateInputView} and 880 * other methods being called as appropriate due to a configuration change. 881 * 882 * <p>When a configuration change does happen, 883 * {@link #onInitializeInterface()} is guaranteed to be called the next 884 * time prior to any of the other input or UI creation callbacks. The 885 * following will be called immediately depending if appropriate for current 886 * state: {@link #onStartInput} if input is active, and 887 * {@link #onCreateInputView} and {@link #onStartInputView} and related 888 * appropriate functions if the UI is displayed. 889 */ 890 @Override public void onConfigurationChanged(Configuration newConfig) { 891 super.onConfigurationChanged(newConfig); 892 resetStateForNewConfiguration(); 893 } 894 895 private void resetStateForNewConfiguration() { 896 boolean visible = mWindowVisible; 897 int showFlags = mShowInputFlags; 898 boolean showingInput = mShowInputRequested; 899 CompletionInfo[] completions = mCurCompletions; 900 initViews(); 901 mInputViewStarted = false; 902 mCandidatesViewStarted = false; 903 if (mInputStarted) { 904 doStartInput(getCurrentInputConnection(), 905 getCurrentInputEditorInfo(), true); 906 } 907 if (visible) { 908 if (showingInput) { 909 // If we were last showing the soft keyboard, try to do so again. 910 if (dispatchOnShowInputRequested(showFlags, true)) { 911 showWindow(true); 912 if (completions != null) { 913 mCurCompletions = completions; 914 onDisplayCompletions(completions); 915 } 916 } else { 917 doHideWindow(); 918 } 919 } else if (mCandidatesVisibility == View.VISIBLE) { 920 // If the candidates are currently visible, make sure the 921 // window is shown for them. 922 showWindow(false); 923 } else { 924 // Otherwise hide the window. 925 doHideWindow(); 926 } 927 // If user uses hard keyboard, IME button should always be shown. 928 boolean showing = onEvaluateInputViewShown(); 929 mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0), 930 mBackDisposition); 931 } 932 } 933 934 /** 935 * Implement to return our standard {@link InputMethodImpl}. Subclasses 936 * can override to provide their own customized version. 937 */ 938 @Override 939 public AbstractInputMethodImpl onCreateInputMethodInterface() { 940 return new InputMethodImpl(); 941 } 942 943 /** 944 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses 945 * can override to provide their own customized version. 946 */ 947 @Override 948 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() { 949 return new InputMethodSessionImpl(); 950 } 951 952 public LayoutInflater getLayoutInflater() { 953 return mInflater; 954 } 955 956 public Dialog getWindow() { 957 return mWindow; 958 } 959 960 public void setBackDisposition(int disposition) { 961 mBackDisposition = disposition; 962 } 963 964 public int getBackDisposition() { 965 return mBackDisposition; 966 } 967 968 /** 969 * Return the maximum width, in pixels, available the input method. 970 * Input methods are positioned at the bottom of the screen and, unless 971 * running in fullscreen, will generally want to be as short as possible 972 * so should compute their height based on their contents. However, they 973 * can stretch as much as needed horizontally. The function returns to 974 * you the maximum amount of space available horizontally, which you can 975 * use if needed for UI placement. 976 * 977 * <p>In many cases this is not needed, you can just rely on the normal 978 * view layout mechanisms to position your views within the full horizontal 979 * space given to the input method. 980 * 981 * <p>Note that this value can change dynamically, in particular when the 982 * screen orientation changes. 983 */ 984 public int getMaxWidth() { 985 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 986 return wm.getDefaultDisplay().getWidth(); 987 } 988 989 /** 990 * Return the currently active InputBinding for the input method, or 991 * null if there is none. 992 */ 993 public InputBinding getCurrentInputBinding() { 994 return mInputBinding; 995 } 996 997 /** 998 * Retrieve the currently active InputConnection that is bound to 999 * the input method, or null if there is none. 1000 */ 1001 public InputConnection getCurrentInputConnection() { 1002 InputConnection ic = mStartedInputConnection; 1003 if (ic != null) { 1004 return ic; 1005 } 1006 return mInputConnection; 1007 } 1008 1009 public boolean getCurrentInputStarted() { 1010 return mInputStarted; 1011 } 1012 1013 public EditorInfo getCurrentInputEditorInfo() { 1014 return mInputEditorInfo; 1015 } 1016 1017 /** 1018 * Re-evaluate whether the input method should be running in fullscreen 1019 * mode, and update its UI if this has changed since the last time it 1020 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to 1021 * determine whether it should currently run in fullscreen mode. You 1022 * can use {@link #isFullscreenMode()} to determine if the input method 1023 * is currently running in fullscreen mode. 1024 */ 1025 public void updateFullscreenMode() { 1026 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode(); 1027 boolean changed = mLastShowInputRequested != mShowInputRequested; 1028 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { 1029 changed = true; 1030 mIsFullscreen = isFullscreen; 1031 InputConnection ic = getCurrentInputConnection(); 1032 if (ic != null) ic.reportFullscreenMode(isFullscreen); 1033 mFullscreenApplied = true; 1034 initialize(); 1035 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 1036 mFullscreenArea.getLayoutParams(); 1037 if (isFullscreen) { 1038 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable( 1039 com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground)); 1040 lp.height = 0; 1041 lp.weight = 1; 1042 } else { 1043 mFullscreenArea.setBackgroundDrawable(null); 1044 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT; 1045 lp.weight = 0; 1046 } 1047 ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout( 1048 mFullscreenArea, lp); 1049 if (isFullscreen) { 1050 if (mExtractView == null) { 1051 View v = onCreateExtractTextView(); 1052 if (v != null) { 1053 setExtractView(v); 1054 } 1055 } 1056 startExtractingText(false); 1057 } 1058 updateExtractFrameVisibility(); 1059 } 1060 1061 if (changed) { 1062 onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested); 1063 mLastShowInputRequested = mShowInputRequested; 1064 } 1065 } 1066 1067 /** 1068 * Update the given window's parameters for the given mode. This is called 1069 * when the window is first displayed and each time the fullscreen or 1070 * candidates only mode changes. 1071 * 1072 * <p>The default implementation makes the layout for the window 1073 * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and 1074 * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode. 1075 * 1076 * @param win The input method's window. 1077 * @param isFullscreen If true, the window is running in fullscreen mode 1078 * and intended to cover the entire application display. 1079 * @param isCandidatesOnly If true, the window is only showing the 1080 * candidates view and none of the rest of its UI. This is mutually 1081 * exclusive with fullscreen mode. 1082 */ 1083 public void onConfigureWindow(Window win, boolean isFullscreen, 1084 boolean isCandidatesOnly) { 1085 final int currentHeight = mWindow.getWindow().getAttributes().height; 1086 final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT; 1087 if (mIsInputViewShown && currentHeight != newHeight) { 1088 Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: " 1089 + currentHeight + " -> " + newHeight); 1090 } 1091 mWindow.getWindow().setLayout(MATCH_PARENT, newHeight); 1092 } 1093 1094 /** 1095 * Return whether the input method is <em>currently</em> running in 1096 * fullscreen mode. This is the mode that was last determined and 1097 * applied by {@link #updateFullscreenMode()}. 1098 */ 1099 public boolean isFullscreenMode() { 1100 return mIsFullscreen; 1101 } 1102 1103 /** 1104 * Override this to control when the input method should run in 1105 * fullscreen mode. The default implementation runs in fullsceen only 1106 * when the screen is in landscape mode. If you change what 1107 * this returns, you will need to call {@link #updateFullscreenMode()} 1108 * yourself whenever the returned value may have changed to have it 1109 * re-evaluated and applied. 1110 */ 1111 public boolean onEvaluateFullscreenMode() { 1112 Configuration config = getResources().getConfiguration(); 1113 if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) { 1114 return false; 1115 } 1116 if (mInputEditorInfo != null 1117 && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) { 1118 return false; 1119 } 1120 return true; 1121 } 1122 1123 /** 1124 * Controls the visibility of the extracted text area. This only applies 1125 * when the input method is in fullscreen mode, and thus showing extracted 1126 * text. When false, the extracted text will not be shown, allowing some 1127 * of the application to be seen behind. This is normally set for you 1128 * by {@link #onUpdateExtractingVisibility}. This controls the visibility 1129 * of both the extracted text and candidate view; the latter since it is 1130 * not useful if there is no text to see. 1131 */ 1132 public void setExtractViewShown(boolean shown) { 1133 if (mExtractViewHidden == shown) { 1134 mExtractViewHidden = !shown; 1135 updateExtractFrameVisibility(); 1136 } 1137 } 1138 1139 /** 1140 * Return whether the fullscreen extract view is shown. This will only 1141 * return true if {@link #isFullscreenMode()} returns true, and in that 1142 * case its value depends on the last call to 1143 * {@link #setExtractViewShown(boolean)}. This effectively lets you 1144 * determine if the application window is entirely covered (when this 1145 * returns true) or if some part of it may be shown (if this returns 1146 * false, though if {@link #isFullscreenMode()} returns true in that case 1147 * then it is probably only a sliver of the application). 1148 */ 1149 public boolean isExtractViewShown() { 1150 return mIsFullscreen && !mExtractViewHidden; 1151 } 1152 1153 void updateExtractFrameVisibility() { 1154 final int vis; 1155 if (isFullscreenMode()) { 1156 vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE; 1157 // "vis" should be applied for the extract frame as well in the fullscreen mode. 1158 mExtractFrame.setVisibility(vis); 1159 } else { 1160 vis = View.VISIBLE; 1161 mExtractFrame.setVisibility(View.GONE); 1162 } 1163 updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE); 1164 if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) { 1165 int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE 1166 ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation 1167 : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation, 1168 0); 1169 if (animRes != 0) { 1170 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation( 1171 this, animRes)); 1172 } 1173 } 1174 mFullscreenArea.setVisibility(vis); 1175 } 1176 1177 /** 1178 * Compute the interesting insets into your UI. The default implementation 1179 * uses the top of the candidates frame for the visible insets, and the 1180 * top of the input frame for the content insets. The default touchable 1181 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}. 1182 * 1183 * <p>Note that this method is not called when 1184 * {@link #isExtractViewShown} returns true, since 1185 * in that case the application is left as-is behind the input method and 1186 * not impacted by anything in its UI. 1187 * 1188 * @param outInsets Fill in with the current UI insets. 1189 */ 1190 public void onComputeInsets(Insets outInsets) { 1191 int[] loc = mTmpLocation; 1192 if (mInputFrame.getVisibility() == View.VISIBLE) { 1193 mInputFrame.getLocationInWindow(loc); 1194 } else { 1195 View decor = getWindow().getWindow().getDecorView(); 1196 loc[1] = decor.getHeight(); 1197 } 1198 if (isFullscreenMode()) { 1199 // In fullscreen mode, we never resize the underlying window. 1200 View decor = getWindow().getWindow().getDecorView(); 1201 outInsets.contentTopInsets = decor.getHeight(); 1202 } else { 1203 outInsets.contentTopInsets = loc[1]; 1204 } 1205 if (mCandidatesFrame.getVisibility() == View.VISIBLE) { 1206 mCandidatesFrame.getLocationInWindow(loc); 1207 } 1208 outInsets.visibleTopInsets = loc[1]; 1209 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE; 1210 outInsets.touchableRegion.setEmpty(); 1211 } 1212 1213 /** 1214 * Re-evaluate whether the soft input area should currently be shown, and 1215 * update its UI if this has changed since the last time it 1216 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to 1217 * determine whether the input view should currently be shown. You 1218 * can use {@link #isInputViewShown()} to determine if the input view 1219 * is currently shown. 1220 */ 1221 public void updateInputViewShown() { 1222 boolean isShown = mShowInputRequested && onEvaluateInputViewShown(); 1223 if (mIsInputViewShown != isShown && mWindowVisible) { 1224 mIsInputViewShown = isShown; 1225 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE); 1226 if (mInputView == null) { 1227 initialize(); 1228 View v = onCreateInputView(); 1229 if (v != null) { 1230 setInputView(v); 1231 } 1232 } 1233 } 1234 } 1235 1236 /** 1237 * Returns true if we have been asked to show our input view. 1238 */ 1239 public boolean isShowInputRequested() { 1240 return mShowInputRequested; 1241 } 1242 1243 /** 1244 * Return whether the soft input view is <em>currently</em> shown to the 1245 * user. This is the state that was last determined and 1246 * applied by {@link #updateInputViewShown()}. 1247 */ 1248 public boolean isInputViewShown() { 1249 return mIsInputViewShown && mWindowVisible; 1250 } 1251 1252 /** 1253 * Override this to control when the soft input area should be shown to the user. The default 1254 * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden 1255 * unless the user shows an intention to use software keyboard. If you change what this 1256 * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned 1257 * value may have changed to have it re-evaluated and applied. 1258 * 1259 * <p>When you override this method, it is recommended to call 1260 * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is 1261 * returned.</p> 1262 */ 1263 @CallSuper 1264 public boolean onEvaluateInputViewShown() { 1265 if (mSettingsObserver == null) { 1266 Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here."); 1267 return false; 1268 } 1269 if (mSettingsObserver.shouldShowImeWithHardKeyboard()) { 1270 return true; 1271 } 1272 Configuration config = getResources().getConfiguration(); 1273 return config.keyboard == Configuration.KEYBOARD_NOKEYS 1274 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES; 1275 } 1276 1277 /** 1278 * Controls the visibility of the candidates display area. By default 1279 * it is hidden. 1280 */ 1281 public void setCandidatesViewShown(boolean shown) { 1282 updateCandidatesVisibility(shown); 1283 if (!mShowInputRequested && mWindowVisible != shown) { 1284 // If we are being asked to show the candidates view while the app 1285 // has not asked for the input view to be shown, then we need 1286 // to update whether the window is shown. 1287 if (shown) { 1288 showWindow(false); 1289 } else { 1290 doHideWindow(); 1291 } 1292 } 1293 } 1294 1295 void updateCandidatesVisibility(boolean shown) { 1296 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility(); 1297 if (mCandidatesVisibility != vis) { 1298 mCandidatesFrame.setVisibility(vis); 1299 mCandidatesVisibility = vis; 1300 } 1301 } 1302 1303 /** 1304 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE} 1305 * or {@link View#GONE View.GONE}) of the candidates view when it is not 1306 * shown. The default implementation returns GONE when 1307 * {@link #isExtractViewShown} returns true, 1308 * otherwise VISIBLE. Be careful if you change this to return GONE in 1309 * other situations -- if showing or hiding the candidates view causes 1310 * your window to resize, this can cause temporary drawing artifacts as 1311 * the resize takes place. 1312 */ 1313 public int getCandidatesHiddenVisibility() { 1314 return isExtractViewShown() ? View.GONE : View.INVISIBLE; 1315 } 1316 1317 public void showStatusIcon(@DrawableRes int iconResId) { 1318 mStatusIcon = iconResId; 1319 mImm.showStatusIcon(mToken, getPackageName(), iconResId); 1320 } 1321 1322 public void hideStatusIcon() { 1323 mStatusIcon = 0; 1324 mImm.hideStatusIcon(mToken); 1325 } 1326 1327 /** 1328 * Force switch to a new input method, as identified by <var>id</var>. This 1329 * input method will be destroyed, and the requested one started on the 1330 * current input field. 1331 * 1332 * @param id Unique identifier of the new input method ot start. 1333 */ 1334 public void switchInputMethod(String id) { 1335 mImm.setInputMethod(mToken, id); 1336 } 1337 1338 public void setExtractView(View view) { 1339 mExtractFrame.removeAllViews(); 1340 mExtractFrame.addView(view, new FrameLayout.LayoutParams( 1341 ViewGroup.LayoutParams.MATCH_PARENT, 1342 ViewGroup.LayoutParams.MATCH_PARENT)); 1343 mExtractView = view; 1344 if (view != null) { 1345 mExtractEditText = (ExtractEditText)view.findViewById( 1346 com.android.internal.R.id.inputExtractEditText); 1347 mExtractEditText.setIME(this); 1348 mExtractAction = view.findViewById( 1349 com.android.internal.R.id.inputExtractAction); 1350 if (mExtractAction != null) { 1351 mExtractAccessories = (ViewGroup)view.findViewById( 1352 com.android.internal.R.id.inputExtractAccessories); 1353 } 1354 startExtractingText(false); 1355 } else { 1356 mExtractEditText = null; 1357 mExtractAccessories = null; 1358 mExtractAction = null; 1359 } 1360 } 1361 1362 /** 1363 * Replaces the current candidates view with a new one. You only need to 1364 * call this when dynamically changing the view; normally, you should 1365 * implement {@link #onCreateCandidatesView()} and create your view when 1366 * first needed by the input method. 1367 */ 1368 public void setCandidatesView(View view) { 1369 mCandidatesFrame.removeAllViews(); 1370 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams( 1371 ViewGroup.LayoutParams.MATCH_PARENT, 1372 ViewGroup.LayoutParams.WRAP_CONTENT)); 1373 } 1374 1375 /** 1376 * Replaces the current input view with a new one. You only need to 1377 * call this when dynamically changing the view; normally, you should 1378 * implement {@link #onCreateInputView()} and create your view when 1379 * first needed by the input method. 1380 */ 1381 public void setInputView(View view) { 1382 mInputFrame.removeAllViews(); 1383 mInputFrame.addView(view, new FrameLayout.LayoutParams( 1384 ViewGroup.LayoutParams.MATCH_PARENT, 1385 ViewGroup.LayoutParams.WRAP_CONTENT)); 1386 mInputView = view; 1387 } 1388 1389 /** 1390 * Called by the framework to create the layout for showing extacted text. 1391 * Only called when in fullscreen mode. The returned view hierarchy must 1392 * have an {@link ExtractEditText} whose ID is 1393 * {@link android.R.id#inputExtractEditText}. 1394 */ 1395 public View onCreateExtractTextView() { 1396 return mInflater.inflate( 1397 com.android.internal.R.layout.input_method_extract_view, null); 1398 } 1399 1400 /** 1401 * Create and return the view hierarchy used to show candidates. This will 1402 * be called once, when the candidates are first displayed. You can return 1403 * null to have no candidates view; the default implementation returns null. 1404 * 1405 * <p>To control when the candidates view is displayed, use 1406 * {@link #setCandidatesViewShown(boolean)}. 1407 * To change the candidates view after the first one is created by this 1408 * function, use {@link #setCandidatesView(View)}. 1409 */ 1410 public View onCreateCandidatesView() { 1411 return null; 1412 } 1413 1414 /** 1415 * Create and return the view hierarchy used for the input area (such as 1416 * a soft keyboard). This will be called once, when the input area is 1417 * first displayed. You can return null to have no input area; the default 1418 * implementation returns null. 1419 * 1420 * <p>To control when the input view is displayed, implement 1421 * {@link #onEvaluateInputViewShown()}. 1422 * To change the input view after the first one is created by this 1423 * function, use {@link #setInputView(View)}. 1424 */ 1425 public View onCreateInputView() { 1426 return null; 1427 } 1428 1429 /** 1430 * Called when the input view is being shown and input has started on 1431 * a new editor. This will always be called after {@link #onStartInput}, 1432 * allowing you to do your general setup there and just view-specific 1433 * setup here. You are guaranteed that {@link #onCreateInputView()} will 1434 * have been called some time before this function is called. 1435 * 1436 * @param info Description of the type of text being edited. 1437 * @param restarting Set to true if we are restarting input on the 1438 * same text field as before. 1439 */ 1440 public void onStartInputView(EditorInfo info, boolean restarting) { 1441 // Intentionally empty 1442 } 1443 1444 /** 1445 * Called when the input view is being hidden from the user. This will 1446 * be called either prior to hiding the window, or prior to switching to 1447 * another target for editing. 1448 * 1449 * <p>The default 1450 * implementation uses the InputConnection to clear any active composing 1451 * text; you can override this (not calling the base class implementation) 1452 * to perform whatever behavior you would like. 1453 * 1454 * @param finishingInput If true, {@link #onFinishInput} will be 1455 * called immediately after. 1456 */ 1457 public void onFinishInputView(boolean finishingInput) { 1458 if (!finishingInput) { 1459 InputConnection ic = getCurrentInputConnection(); 1460 if (ic != null) { 1461 ic.finishComposingText(); 1462 } 1463 } 1464 } 1465 1466 /** 1467 * Called when only the candidates view has been shown for showing 1468 * processing as the user enters text through a hard keyboard. 1469 * This will always be called after {@link #onStartInput}, 1470 * allowing you to do your general setup there and just view-specific 1471 * setup here. You are guaranteed that {@link #onCreateCandidatesView()} 1472 * will have been called some time before this function is called. 1473 * 1474 * <p>Note that this will <em>not</em> be called when the input method 1475 * is running in full editing mode, and thus receiving 1476 * {@link #onStartInputView} to initiate that operation. This is only 1477 * for the case when candidates are being shown while the input method 1478 * editor is hidden but wants to show its candidates UI as text is 1479 * entered through some other mechanism. 1480 * 1481 * @param info Description of the type of text being edited. 1482 * @param restarting Set to true if we are restarting input on the 1483 * same text field as before. 1484 */ 1485 public void onStartCandidatesView(EditorInfo info, boolean restarting) { 1486 // Intentionally empty 1487 } 1488 1489 /** 1490 * Called when the candidates view is being hidden from the user. This will 1491 * be called either prior to hiding the window, or prior to switching to 1492 * another target for editing. 1493 * 1494 * <p>The default 1495 * implementation uses the InputConnection to clear any active composing 1496 * text; you can override this (not calling the base class implementation) 1497 * to perform whatever behavior you would like. 1498 * 1499 * @param finishingInput If true, {@link #onFinishInput} will be 1500 * called immediately after. 1501 */ 1502 public void onFinishCandidatesView(boolean finishingInput) { 1503 if (!finishingInput) { 1504 InputConnection ic = getCurrentInputConnection(); 1505 if (ic != null) { 1506 ic.finishComposingText(); 1507 } 1508 } 1509 } 1510 1511 /** 1512 * The system has decided that it may be time to show your input method. 1513 * This is called due to a corresponding call to your 1514 * {@link InputMethod#showSoftInput InputMethod.showSoftInput()} 1515 * method. The default implementation uses 1516 * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()}, 1517 * and the current configuration to decide whether the input view should 1518 * be shown at this point. 1519 * 1520 * @param flags Provides additional information about the show request, 1521 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}. 1522 * @param configChange This is true if we are re-showing due to a 1523 * configuration change. 1524 * @return Returns true to indicate that the window should be shown. 1525 */ 1526 public boolean onShowInputRequested(int flags, boolean configChange) { 1527 if (!onEvaluateInputViewShown()) { 1528 return false; 1529 } 1530 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) { 1531 if (!configChange && onEvaluateFullscreenMode()) { 1532 // Don't show if this is not explicitly requested by the user and 1533 // the input method is fullscreen. That would be too disruptive. 1534 // However, we skip this change for a config change, since if 1535 // the IME is already shown we do want to go into fullscreen 1536 // mode at this point. 1537 return false; 1538 } 1539 if (!mSettingsObserver.shouldShowImeWithHardKeyboard() && 1540 getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS) { 1541 // And if the device has a hard keyboard, even if it is 1542 // currently hidden, don't show the input method implicitly. 1543 // These kinds of devices don't need it that much. 1544 return false; 1545 } 1546 } 1547 return true; 1548 } 1549 1550 /** 1551 * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal 1552 * states depending on its result. Since {@link #onShowInputRequested(int, boolean)} is 1553 * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have 1554 * to have this method to ensure that those internal states are always updated no matter how 1555 * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author. 1556 * @param flags Provides additional information about the show request, 1557 * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}. 1558 * @param configChange This is true if we are re-showing due to a 1559 * configuration change. 1560 * @return Returns true to indicate that the window should be shown. 1561 * @see #onShowInputRequested(int, boolean) 1562 */ 1563 private boolean dispatchOnShowInputRequested(int flags, boolean configChange) { 1564 final boolean result = onShowInputRequested(flags, configChange); 1565 if (result) { 1566 mShowInputFlags = flags; 1567 } else { 1568 mShowInputFlags = 0; 1569 } 1570 return result; 1571 } 1572 1573 public void showWindow(boolean showInput) { 1574 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput 1575 + " mShowInputRequested=" + mShowInputRequested 1576 + " mWindowAdded=" + mWindowAdded 1577 + " mWindowCreated=" + mWindowCreated 1578 + " mWindowVisible=" + mWindowVisible 1579 + " mInputStarted=" + mInputStarted 1580 + " mShowInputFlags=" + mShowInputFlags); 1581 1582 if (mInShowWindow) { 1583 Log.w(TAG, "Re-entrance in to showWindow"); 1584 return; 1585 } 1586 1587 try { 1588 mWindowWasVisible = mWindowVisible; 1589 mInShowWindow = true; 1590 showWindowInner(showInput); 1591 } catch (BadTokenException e) { 1592 // BadTokenException is a normal consequence in certain situations, e.g., swapping IMEs 1593 // while there is a DO_SHOW_SOFT_INPUT message in the IIMethodWrapper queue. 1594 if (DEBUG) Log.v(TAG, "BadTokenException: IME is done."); 1595 mWindowVisible = false; 1596 mWindowAdded = false; 1597 // Rethrow the exception to preserve the existing behavior. Some IMEs may have directly 1598 // called this method and relied on this exception for some clean-up tasks. 1599 // TODO: Give developers a clear guideline of whether it's OK to call this method or 1600 // InputMethodManager#showSoftInputFromInputMethod() should always be used instead. 1601 throw e; 1602 } finally { 1603 // TODO: Is it OK to set true when we get BadTokenException? 1604 mWindowWasVisible = true; 1605 mInShowWindow = false; 1606 } 1607 } 1608 1609 void showWindowInner(boolean showInput) { 1610 boolean doShowInput = false; 1611 final int previousImeWindowStatus = 1612 (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0); 1613 mWindowVisible = true; 1614 if (!mShowInputRequested && mInputStarted && showInput) { 1615 doShowInput = true; 1616 mShowInputRequested = true; 1617 } 1618 1619 if (DEBUG) Log.v(TAG, "showWindow: updating UI"); 1620 initialize(); 1621 updateFullscreenMode(); 1622 updateInputViewShown(); 1623 1624 if (!mWindowAdded || !mWindowCreated) { 1625 mWindowAdded = true; 1626 mWindowCreated = true; 1627 initialize(); 1628 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView"); 1629 View v = onCreateCandidatesView(); 1630 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v); 1631 if (v != null) { 1632 setCandidatesView(v); 1633 } 1634 } 1635 if (mShowInputRequested) { 1636 if (!mInputViewStarted) { 1637 if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); 1638 mInputViewStarted = true; 1639 onStartInputView(mInputEditorInfo, false); 1640 } 1641 } else if (!mCandidatesViewStarted) { 1642 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); 1643 mCandidatesViewStarted = true; 1644 onStartCandidatesView(mInputEditorInfo, false); 1645 } 1646 1647 if (doShowInput) { 1648 startExtractingText(false); 1649 } 1650 1651 final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0); 1652 if (previousImeWindowStatus != nextImeWindowStatus) { 1653 mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition); 1654 } 1655 if ((previousImeWindowStatus & IME_ACTIVE) == 0) { 1656 if (DEBUG) Log.v(TAG, "showWindow: showing!"); 1657 onWindowShown(); 1658 mWindow.show(); 1659 // Put here rather than in onWindowShown() in case people forget to call 1660 // super.onWindowShown(). 1661 mShouldClearInsetOfPreviousIme = false; 1662 } 1663 } 1664 1665 private void finishViews() { 1666 if (mInputViewStarted) { 1667 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); 1668 onFinishInputView(false); 1669 } else if (mCandidatesViewStarted) { 1670 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView"); 1671 onFinishCandidatesView(false); 1672 } 1673 mInputViewStarted = false; 1674 mCandidatesViewStarted = false; 1675 } 1676 1677 private void doHideWindow() { 1678 mImm.setImeWindowStatus(mToken, 0, mBackDisposition); 1679 hideWindow(); 1680 } 1681 1682 public void hideWindow() { 1683 finishViews(); 1684 if (mWindowVisible) { 1685 mWindow.hide(); 1686 mWindowVisible = false; 1687 onWindowHidden(); 1688 mWindowWasVisible = false; 1689 } 1690 updateFullscreenMode(); 1691 } 1692 1693 /** 1694 * Called when the input method window has been shown to the user, after 1695 * previously not being visible. This is done after all of the UI setup 1696 * for the window has occurred (creating its views etc). 1697 */ 1698 public void onWindowShown() { 1699 // Intentionally empty 1700 } 1701 1702 /** 1703 * Called when the input method window has been hidden from the user, 1704 * after previously being visible. 1705 */ 1706 public void onWindowHidden() { 1707 // Intentionally empty 1708 } 1709 1710 /** 1711 * Reset the inset occupied the previous IME when and only when 1712 * {@link #mShouldClearInsetOfPreviousIme} is {@code true}. 1713 */ 1714 private void clearInsetOfPreviousIme() { 1715 if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() " 1716 + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme); 1717 if (!mShouldClearInsetOfPreviousIme) return; 1718 1719 mImm.clearLastInputMethodWindowForTransition(mToken); 1720 mShouldClearInsetOfPreviousIme = false; 1721 } 1722 1723 /** 1724 * Called when a new client has bound to the input method. This 1725 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)} 1726 * and {@link #onFinishInput()} calls as the user navigates through its 1727 * UI. Upon this call you know that {@link #getCurrentInputBinding} 1728 * and {@link #getCurrentInputConnection} return valid objects. 1729 */ 1730 public void onBindInput() { 1731 // Intentionally empty 1732 } 1733 1734 /** 1735 * Called when the previous bound client is no longer associated 1736 * with the input method. After returning {@link #getCurrentInputBinding} 1737 * and {@link #getCurrentInputConnection} will no longer return 1738 * valid objects. 1739 */ 1740 public void onUnbindInput() { 1741 // Intentionally empty 1742 } 1743 1744 /** 1745 * Called to inform the input method that text input has started in an 1746 * editor. You should use this callback to initialize the state of your 1747 * input to match the state of the editor given to it. 1748 * 1749 * @param attribute The attributes of the editor that input is starting 1750 * in. 1751 * @param restarting Set to true if input is restarting in the same 1752 * editor such as because the application has changed the text in 1753 * the editor. Otherwise will be false, indicating this is a new 1754 * session with the editor. 1755 */ 1756 public void onStartInput(EditorInfo attribute, boolean restarting) { 1757 // Intentionally empty 1758 } 1759 1760 void doFinishInput() { 1761 if (mInputViewStarted) { 1762 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); 1763 onFinishInputView(true); 1764 } else if (mCandidatesViewStarted) { 1765 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView"); 1766 onFinishCandidatesView(true); 1767 } 1768 mInputViewStarted = false; 1769 mCandidatesViewStarted = false; 1770 if (mInputStarted) { 1771 if (DEBUG) Log.v(TAG, "CALL: onFinishInput"); 1772 onFinishInput(); 1773 } 1774 mInputStarted = false; 1775 mStartedInputConnection = null; 1776 mCurCompletions = null; 1777 } 1778 1779 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { 1780 if (!restarting) { 1781 doFinishInput(); 1782 } 1783 mInputStarted = true; 1784 mStartedInputConnection = ic; 1785 mInputEditorInfo = attribute; 1786 initialize(); 1787 if (DEBUG) Log.v(TAG, "CALL: onStartInput"); 1788 onStartInput(attribute, restarting); 1789 if (mWindowVisible) { 1790 if (mShowInputRequested) { 1791 if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); 1792 mInputViewStarted = true; 1793 onStartInputView(mInputEditorInfo, restarting); 1794 startExtractingText(true); 1795 } else if (mCandidatesVisibility == View.VISIBLE) { 1796 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); 1797 mCandidatesViewStarted = true; 1798 onStartCandidatesView(mInputEditorInfo, restarting); 1799 } 1800 } 1801 } 1802 1803 /** 1804 * Called to inform the input method that text input has finished in 1805 * the last editor. At this point there may be a call to 1806 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a 1807 * new editor, or the input method may be left idle. This method is 1808 * <em>not</em> called when input restarts in the same editor. 1809 * 1810 * <p>The default 1811 * implementation uses the InputConnection to clear any active composing 1812 * text; you can override this (not calling the base class implementation) 1813 * to perform whatever behavior you would like. 1814 */ 1815 public void onFinishInput() { 1816 InputConnection ic = getCurrentInputConnection(); 1817 if (ic != null) { 1818 ic.finishComposingText(); 1819 } 1820 } 1821 1822 /** 1823 * Called when the application has reported auto-completion candidates that 1824 * it would like to have the input method displayed. Typically these are 1825 * only used when an input method is running in full-screen mode, since 1826 * otherwise the user can see and interact with the pop-up window of 1827 * completions shown by the application. 1828 * 1829 * <p>The default implementation here does nothing. 1830 */ 1831 public void onDisplayCompletions(CompletionInfo[] completions) { 1832 // Intentionally empty 1833 } 1834 1835 /** 1836 * Called when the application has reported new extracted text to be shown 1837 * due to changes in its current text state. The default implementation 1838 * here places the new text in the extract edit text, when the input 1839 * method is running in fullscreen mode. 1840 */ 1841 public void onUpdateExtractedText(int token, ExtractedText text) { 1842 if (mExtractedToken != token) { 1843 return; 1844 } 1845 if (text != null) { 1846 if (mExtractEditText != null) { 1847 mExtractedText = text; 1848 mExtractEditText.setExtractedText(text); 1849 } 1850 } 1851 } 1852 1853 /** 1854 * Called when the application has reported a new selection region of 1855 * the text. This is called whether or not the input method has requested 1856 * extracted text updates, although if so it will not receive this call 1857 * if the extracted text has changed as well. 1858 * 1859 * <p>Be careful about changing the text in reaction to this call with 1860 * methods such as setComposingText, commitText or 1861 * deleteSurroundingText. If the cursor moves as a result, this method 1862 * will be called again, which may result in an infinite loop. 1863 * 1864 * <p>The default implementation takes care of updating the cursor in 1865 * the extract text, if it is being shown. 1866 */ 1867 public void onUpdateSelection(int oldSelStart, int oldSelEnd, 1868 int newSelStart, int newSelEnd, 1869 int candidatesStart, int candidatesEnd) { 1870 final ExtractEditText eet = mExtractEditText; 1871 if (eet != null && isFullscreenMode() && mExtractedText != null) { 1872 final int off = mExtractedText.startOffset; 1873 eet.startInternalChanges(); 1874 newSelStart -= off; 1875 newSelEnd -= off; 1876 final int len = eet.getText().length(); 1877 if (newSelStart < 0) newSelStart = 0; 1878 else if (newSelStart > len) newSelStart = len; 1879 if (newSelEnd < 0) newSelEnd = 0; 1880 else if (newSelEnd > len) newSelEnd = len; 1881 eet.setSelection(newSelStart, newSelEnd); 1882 eet.finishInternalChanges(); 1883 } 1884 } 1885 1886 /** 1887 * Called when the user tapped or clicked a text view. 1888 * IMEs can't rely on this method being called because this was not part of the original IME 1889 * protocol, so applications with custom text editing written before this method appeared will 1890 * not call to inform the IME of this interaction. 1891 * @param focusChanged true if the user changed the focused view by this click. 1892 */ 1893 public void onViewClicked(boolean focusChanged) { 1894 // Intentionally empty 1895 } 1896 1897 /** 1898 * Called when the application has reported a new location of its text 1899 * cursor. This is only called if explicitly requested by the input method. 1900 * The default implementation does nothing. 1901 * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead. 1902 */ 1903 @Deprecated 1904 public void onUpdateCursor(Rect newCursor) { 1905 // Intentionally empty 1906 } 1907 1908 /** 1909 * Called when the application has reported a new location of its text insertion point and 1910 * characters in the composition string. This is only called if explicitly requested by the 1911 * input method. The default implementation does nothing. 1912 * @param cursorAnchorInfo The positional information of the text insertion point and the 1913 * composition string. 1914 */ 1915 public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) { 1916 // Intentionally empty 1917 } 1918 1919 /** 1920 * Close this input method's soft input area, removing it from the display. 1921 * The input method will continue running, but the user can no longer use 1922 * it to generate input by touching the screen. 1923 * @param flags Provides additional operating flags. Currently may be 1924 * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY 1925 * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set. 1926 */ 1927 public void requestHideSelf(int flags) { 1928 mImm.hideSoftInputFromInputMethod(mToken, flags); 1929 } 1930 1931 /** 1932 * Show the input method. This is a call back to the 1933 * IMF to handle showing the input method. 1934 * @param flags Provides additional operating flags. Currently may be 1935 * 0 or have the {@link InputMethodManager#SHOW_FORCED 1936 * InputMethodManager.} bit set. 1937 */ 1938 private void requestShowSelf(int flags) { 1939 mImm.showSoftInputFromInputMethod(mToken, flags); 1940 } 1941 1942 private boolean handleBack(boolean doIt) { 1943 if (mShowInputRequested) { 1944 // If the soft input area is shown, back closes it and we 1945 // consume the back key. 1946 if (doIt) requestHideSelf(0); 1947 return true; 1948 } else if (mWindowVisible) { 1949 if (mCandidatesVisibility == View.VISIBLE) { 1950 // If we are showing candidates even if no input area, then 1951 // hide them. 1952 if (doIt) setCandidatesViewShown(false); 1953 } else { 1954 // If we have the window visible for some other reason -- 1955 // most likely to show candidates -- then just get rid 1956 // of it. This really shouldn't happen, but just in case... 1957 if (doIt) doHideWindow(); 1958 } 1959 return true; 1960 } 1961 return false; 1962 } 1963 1964 /** 1965 * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise 1966 * {@code null} is returned. 1967 */ 1968 private ExtractEditText getExtractEditTextIfVisible() { 1969 if (!isExtractViewShown() || !isInputViewShown()) { 1970 return null; 1971 } 1972 return mExtractEditText; 1973 } 1974 1975 /** 1976 * Override this to intercept key down events before they are processed by the 1977 * application. If you return true, the application will not 1978 * process the event itself. If you return false, the normal application processing 1979 * will occur as if the IME had not seen the event at all. 1980 * 1981 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK 1982 * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to 1983 * possibly hide it when the key goes up (if not canceled or long pressed). In 1984 * addition, in fullscreen mode only, it will consume DPAD movement 1985 * events to move the cursor in the extracted text view, not allowing 1986 * them to perform navigation in the underlying application. 1987 */ 1988 public boolean onKeyDown(int keyCode, KeyEvent event) { 1989 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 1990 final ExtractEditText eet = getExtractEditTextIfVisible(); 1991 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) { 1992 return true; 1993 } 1994 if (handleBack(false)) { 1995 event.startTracking(); 1996 return true; 1997 } 1998 return false; 1999 } 2000 return doMovementKey(keyCode, event, MOVEMENT_DOWN); 2001 } 2002 2003 /** 2004 * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent) 2005 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle 2006 * the event). 2007 */ 2008 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 2009 return false; 2010 } 2011 2012 /** 2013 * Override this to intercept special key multiple events before they are 2014 * processed by the 2015 * application. If you return true, the application will not itself 2016 * process the event. If you return false, the normal application processing 2017 * will occur as if the IME had not seen the event at all. 2018 * 2019 * <p>The default implementation always returns false, except when 2020 * in fullscreen mode, where it will consume DPAD movement 2021 * events to move the cursor in the extracted text view, not allowing 2022 * them to perform navigation in the underlying application. 2023 */ 2024 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 2025 return doMovementKey(keyCode, event, count); 2026 } 2027 2028 /** 2029 * Override this to intercept key up events before they are processed by the 2030 * application. If you return true, the application will not itself 2031 * process the event. If you return false, the normal application processing 2032 * will occur as if the IME had not seen the event at all. 2033 * 2034 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK 2035 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In 2036 * addition, in fullscreen mode only, it will consume DPAD movement 2037 * events to move the cursor in the extracted text view, not allowing 2038 * them to perform navigation in the underlying application. 2039 */ 2040 public boolean onKeyUp(int keyCode, KeyEvent event) { 2041 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 2042 final ExtractEditText eet = getExtractEditTextIfVisible(); 2043 if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) { 2044 return true; 2045 } 2046 if (event.isTracking() && !event.isCanceled()) { 2047 return handleBack(true); 2048 } 2049 } 2050 return doMovementKey(keyCode, event, MOVEMENT_UP); 2051 } 2052 2053 /** 2054 * Override this to intercept trackball motion events before they are 2055 * processed by the application. 2056 * If you return true, the application will not itself process the event. 2057 * If you return false, the normal application processing will occur as if 2058 * the IME had not seen the event at all. 2059 */ 2060 @Override 2061 public boolean onTrackballEvent(MotionEvent event) { 2062 if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event); 2063 return false; 2064 } 2065 2066 /** 2067 * Override this to intercept generic motion events before they are 2068 * processed by the application. 2069 * If you return true, the application will not itself process the event. 2070 * If you return false, the normal application processing will occur as if 2071 * the IME had not seen the event at all. 2072 */ 2073 @Override 2074 public boolean onGenericMotionEvent(MotionEvent event) { 2075 if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event); 2076 return false; 2077 } 2078 2079 public void onAppPrivateCommand(String action, Bundle data) { 2080 } 2081 2082 /** 2083 * Handle a request by the system to toggle the soft input area. 2084 */ 2085 private void onToggleSoftInput(int showFlags, int hideFlags) { 2086 if (DEBUG) Log.v(TAG, "toggleSoftInput()"); 2087 if (isInputViewShown()) { 2088 requestHideSelf(hideFlags); 2089 } else { 2090 requestShowSelf(showFlags); 2091 } 2092 } 2093 2094 static final int MOVEMENT_DOWN = -1; 2095 static final int MOVEMENT_UP = -2; 2096 2097 void reportExtractedMovement(int keyCode, int count) { 2098 int dx = 0, dy = 0; 2099 switch (keyCode) { 2100 case KeyEvent.KEYCODE_DPAD_LEFT: 2101 dx = -count; 2102 break; 2103 case KeyEvent.KEYCODE_DPAD_RIGHT: 2104 dx = count; 2105 break; 2106 case KeyEvent.KEYCODE_DPAD_UP: 2107 dy = -count; 2108 break; 2109 case KeyEvent.KEYCODE_DPAD_DOWN: 2110 dy = count; 2111 break; 2112 } 2113 onExtractedCursorMovement(dx, dy); 2114 } 2115 2116 boolean doMovementKey(int keyCode, KeyEvent event, int count) { 2117 final ExtractEditText eet = getExtractEditTextIfVisible(); 2118 if (eet != null) { 2119 // If we are in fullscreen mode, the cursor will move around 2120 // the extract edit text, but should NOT cause focus to move 2121 // to other fields. 2122 MovementMethod movement = eet.getMovementMethod(); 2123 Layout layout = eet.getLayout(); 2124 if (movement != null && layout != null) { 2125 // We want our own movement method to handle the key, so the 2126 // cursor will properly move in our own word wrapping. 2127 if (count == MOVEMENT_DOWN) { 2128 if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) { 2129 reportExtractedMovement(keyCode, 1); 2130 return true; 2131 } 2132 } else if (count == MOVEMENT_UP) { 2133 if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) { 2134 return true; 2135 } 2136 } else { 2137 if (movement.onKeyOther(eet, eet.getText(), event)) { 2138 reportExtractedMovement(keyCode, count); 2139 } else { 2140 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN); 2141 if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) { 2142 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP); 2143 movement.onKeyUp(eet, eet.getText(), keyCode, up); 2144 while (--count > 0) { 2145 movement.onKeyDown(eet, eet.getText(), keyCode, down); 2146 movement.onKeyUp(eet, eet.getText(), keyCode, up); 2147 } 2148 reportExtractedMovement(keyCode, count); 2149 } 2150 } 2151 } 2152 } 2153 // Regardless of whether the movement method handled the key, 2154 // we never allow DPAD navigation to the application. 2155 switch (keyCode) { 2156 case KeyEvent.KEYCODE_DPAD_LEFT: 2157 case KeyEvent.KEYCODE_DPAD_RIGHT: 2158 case KeyEvent.KEYCODE_DPAD_UP: 2159 case KeyEvent.KEYCODE_DPAD_DOWN: 2160 return true; 2161 } 2162 } 2163 2164 return false; 2165 } 2166 2167 /** 2168 * Send the given key event code (as defined by {@link KeyEvent}) to the 2169 * current input connection is a key down + key up event pair. The sent 2170 * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD} 2171 * set, so that the recipient can identify them as coming from a software 2172 * input method, and 2173 * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so 2174 * that they don't impact the current touch mode of the UI. 2175 * 2176 * <p>Note that it's discouraged to send such key events in normal operation; 2177 * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type 2178 * text fields, or for non-rich input methods. A reasonably capable software 2179 * input method should use the 2180 * {@link android.view.inputmethod.InputConnection#commitText} family of methods 2181 * to send text to an application, rather than sending key events.</p> 2182 * 2183 * @param keyEventCode The raw key code to send, as defined by 2184 * {@link KeyEvent}. 2185 */ 2186 public void sendDownUpKeyEvents(int keyEventCode) { 2187 InputConnection ic = getCurrentInputConnection(); 2188 if (ic == null) return; 2189 long eventTime = SystemClock.uptimeMillis(); 2190 ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, 2191 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 2192 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); 2193 ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(), 2194 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 2195 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); 2196 } 2197 2198 /** 2199 * Ask the input target to execute its default action via 2200 * {@link InputConnection#performEditorAction 2201 * InputConnection.performEditorAction()}. 2202 * 2203 * @param fromEnterKey If true, this will be executed as if the user had 2204 * pressed an enter key on the keyboard, that is it will <em>not</em> 2205 * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION 2206 * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be 2207 * sent regardless of how the editor has set that flag. 2208 * 2209 * @return Returns a boolean indicating whether an action has been sent. 2210 * If false, either the editor did not specify a default action or it 2211 * does not want an action from the enter key. If true, the action was 2212 * sent (or there was no input connection at all). 2213 */ 2214 public boolean sendDefaultEditorAction(boolean fromEnterKey) { 2215 EditorInfo ei = getCurrentInputEditorInfo(); 2216 if (ei != null && 2217 (!fromEnterKey || (ei.imeOptions & 2218 EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) && 2219 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) != 2220 EditorInfo.IME_ACTION_NONE) { 2221 // If the enter key was pressed, and the editor has a default 2222 // action associated with pressing enter, then send it that 2223 // explicit action instead of the key event. 2224 InputConnection ic = getCurrentInputConnection(); 2225 if (ic != null) { 2226 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION); 2227 } 2228 return true; 2229 } 2230 2231 return false; 2232 } 2233 2234 /** 2235 * Send the given UTF-16 character to the current input connection. Most 2236 * characters will be delivered simply by calling 2237 * {@link InputConnection#commitText InputConnection.commitText()} with 2238 * the character; some, however, may be handled different. In particular, 2239 * the enter character ('\n') will either be delivered as an action code 2240 * or a raw key event, as appropriate. Consider this as a convenience 2241 * method for IMEs that do not have a full implementation of actions; a 2242 * fully complying IME will decide of the right action for each event and 2243 * will likely never call this method except maybe to handle events coming 2244 * from an actual hardware keyboard. 2245 * 2246 * @param charCode The UTF-16 character code to send. 2247 */ 2248 public void sendKeyChar(char charCode) { 2249 switch (charCode) { 2250 case '\n': // Apps may be listening to an enter key to perform an action 2251 if (!sendDefaultEditorAction(true)) { 2252 sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); 2253 } 2254 break; 2255 default: 2256 // Make sure that digits go through any text watcher on the client side. 2257 if (charCode >= '0' && charCode <= '9') { 2258 sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0); 2259 } else { 2260 InputConnection ic = getCurrentInputConnection(); 2261 if (ic != null) { 2262 ic.commitText(String.valueOf(charCode), 1); 2263 } 2264 } 2265 break; 2266 } 2267 } 2268 2269 /** 2270 * This is called when the user has moved the cursor in the extracted 2271 * text view, when running in fullsreen mode. The default implementation 2272 * performs the corresponding selection change on the underlying text 2273 * editor. 2274 */ 2275 public void onExtractedSelectionChanged(int start, int end) { 2276 InputConnection conn = getCurrentInputConnection(); 2277 if (conn != null) { 2278 conn.setSelection(start, end); 2279 } 2280 } 2281 2282 /** 2283 * @hide 2284 */ 2285 public void onExtractedDeleteText(int start, int end) { 2286 InputConnection conn = getCurrentInputConnection(); 2287 if (conn != null) { 2288 conn.finishComposingText(); 2289 conn.setSelection(start, start); 2290 conn.deleteSurroundingText(0, end - start); 2291 } 2292 } 2293 2294 /** 2295 * @hide 2296 */ 2297 public void onExtractedReplaceText(int start, int end, CharSequence text) { 2298 InputConnection conn = getCurrentInputConnection(); 2299 if (conn != null) { 2300 conn.setComposingRegion(start, end); 2301 conn.commitText(text, 1); 2302 } 2303 } 2304 2305 /** 2306 * @hide 2307 */ 2308 public void onExtractedSetSpan(Object span, int start, int end, int flags) { 2309 InputConnection conn = getCurrentInputConnection(); 2310 if (conn != null) { 2311 if (!conn.setSelection(start, end)) return; 2312 CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES); 2313 if (text instanceof Spannable) { 2314 ((Spannable) text).setSpan(span, 0, text.length(), flags); 2315 conn.setComposingRegion(start, end); 2316 conn.commitText(text, 1); 2317 } 2318 } 2319 } 2320 2321 /** 2322 * This is called when the user has clicked on the extracted text view, 2323 * when running in fullscreen mode. The default implementation hides 2324 * the candidates view when this happens, but only if the extracted text 2325 * editor has a vertical scroll bar because its text doesn't fit. 2326 * Re-implement this to provide whatever behavior you want. 2327 */ 2328 public void onExtractedTextClicked() { 2329 if (mExtractEditText == null) { 2330 return; 2331 } 2332 if (mExtractEditText.hasVerticalScrollBar()) { 2333 setCandidatesViewShown(false); 2334 } 2335 } 2336 2337 /** 2338 * This is called when the user has performed a cursor movement in the 2339 * extracted text view, when it is running in fullscreen mode. The default 2340 * implementation hides the candidates view when a vertical movement 2341 * happens, but only if the extracted text editor has a vertical scroll bar 2342 * because its text doesn't fit. 2343 * Re-implement this to provide whatever behavior you want. 2344 * @param dx The amount of cursor movement in the x dimension. 2345 * @param dy The amount of cursor movement in the y dimension. 2346 */ 2347 public void onExtractedCursorMovement(int dx, int dy) { 2348 if (mExtractEditText == null || dy == 0) { 2349 return; 2350 } 2351 if (mExtractEditText.hasVerticalScrollBar()) { 2352 setCandidatesViewShown(false); 2353 } 2354 } 2355 2356 /** 2357 * This is called when the user has selected a context menu item from the 2358 * extracted text view, when running in fullscreen mode. The default 2359 * implementation sends this action to the current InputConnection's 2360 * {@link InputConnection#performContextMenuAction(int)}, for it 2361 * to be processed in underlying "real" editor. Re-implement this to 2362 * provide whatever behavior you want. 2363 */ 2364 public boolean onExtractTextContextMenuItem(int id) { 2365 InputConnection ic = getCurrentInputConnection(); 2366 if (ic != null) { 2367 ic.performContextMenuAction(id); 2368 } 2369 return true; 2370 } 2371 2372 /** 2373 * Return text that can be used as a button label for the given 2374 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null 2375 * if there is no action requested. Note that there is no guarantee that 2376 * the returned text will be relatively short, so you probably do not 2377 * want to use it as text on a soft keyboard key label. 2378 * 2379 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}. 2380 * 2381 * @return Returns a label to use, or null if there is no action. 2382 */ 2383 public CharSequence getTextForImeAction(int imeOptions) { 2384 switch (imeOptions&EditorInfo.IME_MASK_ACTION) { 2385 case EditorInfo.IME_ACTION_NONE: 2386 return null; 2387 case EditorInfo.IME_ACTION_GO: 2388 return getText(com.android.internal.R.string.ime_action_go); 2389 case EditorInfo.IME_ACTION_SEARCH: 2390 return getText(com.android.internal.R.string.ime_action_search); 2391 case EditorInfo.IME_ACTION_SEND: 2392 return getText(com.android.internal.R.string.ime_action_send); 2393 case EditorInfo.IME_ACTION_NEXT: 2394 return getText(com.android.internal.R.string.ime_action_next); 2395 case EditorInfo.IME_ACTION_DONE: 2396 return getText(com.android.internal.R.string.ime_action_done); 2397 case EditorInfo.IME_ACTION_PREVIOUS: 2398 return getText(com.android.internal.R.string.ime_action_previous); 2399 default: 2400 return getText(com.android.internal.R.string.ime_action_default); 2401 } 2402 } 2403 2404 /** 2405 * Return a drawable resource id that can be used as a button icon for the given 2406 * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. 2407 * 2408 * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}. 2409 * 2410 * @return Returns a drawable resource id to use. 2411 */ 2412 @DrawableRes 2413 private int getIconForImeAction(int imeOptions) { 2414 switch (imeOptions&EditorInfo.IME_MASK_ACTION) { 2415 case EditorInfo.IME_ACTION_GO: 2416 return com.android.internal.R.drawable.ic_input_extract_action_go; 2417 case EditorInfo.IME_ACTION_SEARCH: 2418 return com.android.internal.R.drawable.ic_input_extract_action_search; 2419 case EditorInfo.IME_ACTION_SEND: 2420 return com.android.internal.R.drawable.ic_input_extract_action_send; 2421 case EditorInfo.IME_ACTION_NEXT: 2422 return com.android.internal.R.drawable.ic_input_extract_action_next; 2423 case EditorInfo.IME_ACTION_DONE: 2424 return com.android.internal.R.drawable.ic_input_extract_action_done; 2425 case EditorInfo.IME_ACTION_PREVIOUS: 2426 return com.android.internal.R.drawable.ic_input_extract_action_previous; 2427 default: 2428 return com.android.internal.R.drawable.ic_input_extract_action_return; 2429 } 2430 } 2431 2432 /** 2433 * Called when the fullscreen-mode extracting editor info has changed, 2434 * to determine whether the extracting (extract text and candidates) portion 2435 * of the UI should be shown. The standard implementation hides or shows 2436 * the extract area depending on whether it makes sense for the 2437 * current editor. In particular, a {@link InputType#TYPE_NULL} 2438 * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will 2439 * turn off the extract area since there is no text to be shown. 2440 */ 2441 public void onUpdateExtractingVisibility(EditorInfo ei) { 2442 if (ei.inputType == InputType.TYPE_NULL || 2443 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) { 2444 // No reason to show extract UI! 2445 setExtractViewShown(false); 2446 return; 2447 } 2448 2449 setExtractViewShown(true); 2450 } 2451 2452 /** 2453 * Called when the fullscreen-mode extracting editor info has changed, 2454 * to update the state of its UI such as the action buttons shown. 2455 * You do not need to deal with this if you are using the standard 2456 * full screen extract UI. If replacing it, you will need to re-implement 2457 * this to put the appropriate action button in your own UI and handle it, 2458 * and perform any other changes. 2459 * 2460 * <p>The standard implementation turns on or off its accessory area 2461 * depending on whether there is an action button, and hides or shows 2462 * the entire extract area depending on whether it makes sense for the 2463 * current editor. In particular, a {@link InputType#TYPE_NULL} or 2464 * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the 2465 * extract area since there is no text to be shown. 2466 */ 2467 public void onUpdateExtractingViews(EditorInfo ei) { 2468 if (!isExtractViewShown()) { 2469 return; 2470 } 2471 2472 if (mExtractAccessories == null) { 2473 return; 2474 } 2475 final boolean hasAction = ei.actionLabel != null || ( 2476 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE && 2477 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 && 2478 ei.inputType != InputType.TYPE_NULL); 2479 if (hasAction) { 2480 mExtractAccessories.setVisibility(View.VISIBLE); 2481 if (mExtractAction != null) { 2482 if (mExtractAction instanceof ImageButton) { 2483 ((ImageButton) mExtractAction) 2484 .setImageResource(getIconForImeAction(ei.imeOptions)); 2485 if (ei.actionLabel != null) { 2486 mExtractAction.setContentDescription(ei.actionLabel); 2487 } else { 2488 mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions)); 2489 } 2490 } else { 2491 if (ei.actionLabel != null) { 2492 ((TextView) mExtractAction).setText(ei.actionLabel); 2493 } else { 2494 ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions)); 2495 } 2496 } 2497 mExtractAction.setOnClickListener(mActionClickListener); 2498 } 2499 } else { 2500 mExtractAccessories.setVisibility(View.GONE); 2501 if (mExtractAction != null) { 2502 mExtractAction.setOnClickListener(null); 2503 } 2504 } 2505 } 2506 2507 /** 2508 * This is called when, while currently displayed in extract mode, the 2509 * current input target changes. The default implementation will 2510 * auto-hide the IME if the new target is not a full editor, since this 2511 * can be a confusing experience for the user. 2512 */ 2513 public void onExtractingInputChanged(EditorInfo ei) { 2514 if (ei.inputType == InputType.TYPE_NULL) { 2515 requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS); 2516 } 2517 } 2518 2519 void startExtractingText(boolean inputChanged) { 2520 final ExtractEditText eet = mExtractEditText; 2521 if (eet != null && getCurrentInputStarted() 2522 && isFullscreenMode()) { 2523 mExtractedToken++; 2524 ExtractedTextRequest req = new ExtractedTextRequest(); 2525 req.token = mExtractedToken; 2526 req.flags = InputConnection.GET_TEXT_WITH_STYLES; 2527 req.hintMaxLines = 10; 2528 req.hintMaxChars = 10000; 2529 InputConnection ic = getCurrentInputConnection(); 2530 mExtractedText = ic == null? null 2531 : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR); 2532 if (mExtractedText == null || ic == null) { 2533 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = " 2534 + mExtractedText + ", input connection = " + ic); 2535 } 2536 final EditorInfo ei = getCurrentInputEditorInfo(); 2537 2538 try { 2539 eet.startInternalChanges(); 2540 onUpdateExtractingVisibility(ei); 2541 onUpdateExtractingViews(ei); 2542 int inputType = ei.inputType; 2543 if ((inputType&EditorInfo.TYPE_MASK_CLASS) 2544 == EditorInfo.TYPE_CLASS_TEXT) { 2545 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) { 2546 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; 2547 } 2548 } 2549 eet.setInputType(inputType); 2550 eet.setHint(ei.hintText); 2551 if (mExtractedText != null) { 2552 eet.setEnabled(true); 2553 eet.setExtractedText(mExtractedText); 2554 } else { 2555 eet.setEnabled(false); 2556 eet.setText(""); 2557 } 2558 } finally { 2559 eet.finishInternalChanges(); 2560 } 2561 2562 if (inputChanged) { 2563 onExtractingInputChanged(ei); 2564 } 2565 } 2566 } 2567 2568 // TODO: Handle the subtype change event 2569 /** 2570 * Called when the subtype was changed. 2571 * @param newSubtype the subtype which is being changed to. 2572 */ 2573 protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) { 2574 if (DEBUG) { 2575 int nameResId = newSubtype.getNameResId(); 2576 String mode = newSubtype.getMode(); 2577 String output = "changeInputMethodSubtype:" 2578 + (nameResId == 0 ? "<none>" : getString(nameResId)) + "," 2579 + mode + "," 2580 + newSubtype.getLocale() + "," + newSubtype.getExtraValue(); 2581 Log.v(TAG, "--- " + output); 2582 } 2583 } 2584 2585 /** 2586 * @return The recommended height of the input method window. 2587 * An IME author can get the last input method's height as the recommended height 2588 * by calling this in 2589 * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}. 2590 * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME 2591 * switching by using this value as a visible inset height. It's efficient for the smooth 2592 * transition between different IMEs. However, note that this may return 0 (or possibly 2593 * unexpectedly low height). You should thus avoid relying on the return value of this method 2594 * all the time. Please make sure to use a reasonable height for the IME. 2595 */ 2596 public int getInputMethodWindowRecommendedHeight() { 2597 return mImm.getInputMethodWindowVisibleHeight(); 2598 } 2599 2600 /** 2601 * Performs a dump of the InputMethodService's internal state. Override 2602 * to add your own information to the dump. 2603 */ 2604 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 2605 final Printer p = new PrintWriterPrinter(fout); 2606 p.println("Input method service state for " + this + ":"); 2607 p.println(" mWindowCreated=" + mWindowCreated 2608 + " mWindowAdded=" + mWindowAdded); 2609 p.println(" mWindowVisible=" + mWindowVisible 2610 + " mWindowWasVisible=" + mWindowWasVisible 2611 + " mInShowWindow=" + mInShowWindow); 2612 p.println(" Configuration=" + getResources().getConfiguration()); 2613 p.println(" mToken=" + mToken); 2614 p.println(" mInputBinding=" + mInputBinding); 2615 p.println(" mInputConnection=" + mInputConnection); 2616 p.println(" mStartedInputConnection=" + mStartedInputConnection); 2617 p.println(" mInputStarted=" + mInputStarted 2618 + " mInputViewStarted=" + mInputViewStarted 2619 + " mCandidatesViewStarted=" + mCandidatesViewStarted); 2620 2621 if (mInputEditorInfo != null) { 2622 p.println(" mInputEditorInfo:"); 2623 mInputEditorInfo.dump(p, " "); 2624 } else { 2625 p.println(" mInputEditorInfo: null"); 2626 } 2627 2628 p.println(" mShowInputRequested=" + mShowInputRequested 2629 + " mLastShowInputRequested=" + mLastShowInputRequested 2630 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags)); 2631 p.println(" mCandidatesVisibility=" + mCandidatesVisibility 2632 + " mFullscreenApplied=" + mFullscreenApplied 2633 + " mIsFullscreen=" + mIsFullscreen 2634 + " mExtractViewHidden=" + mExtractViewHidden); 2635 2636 if (mExtractedText != null) { 2637 p.println(" mExtractedText:"); 2638 p.println(" text=" + mExtractedText.text.length() + " chars" 2639 + " startOffset=" + mExtractedText.startOffset); 2640 p.println(" selectionStart=" + mExtractedText.selectionStart 2641 + " selectionEnd=" + mExtractedText.selectionEnd 2642 + " flags=0x" + Integer.toHexString(mExtractedText.flags)); 2643 } else { 2644 p.println(" mExtractedText: null"); 2645 } 2646 p.println(" mExtractedToken=" + mExtractedToken); 2647 p.println(" mIsInputViewShown=" + mIsInputViewShown 2648 + " mStatusIcon=" + mStatusIcon); 2649 p.println("Last computed insets:"); 2650 p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets 2651 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets 2652 + " touchableInsets=" + mTmpInsets.touchableInsets 2653 + " touchableRegion=" + mTmpInsets.touchableRegion); 2654 p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme); 2655 p.println(" mSettingsObserver=" + mSettingsObserver); 2656 } 2657 } 2658