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