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