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