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