InputMethodService.java revision d24b8183b93e781080b2c16c487e60d51c12da31
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.graphics.Rect; 26import android.graphics.drawable.Drawable; 27import android.os.Bundle; 28import android.os.IBinder; 29import android.provider.Settings; 30import android.text.Layout; 31import android.text.Spannable; 32import android.text.method.MovementMethod; 33import android.util.Log; 34import android.util.PrintWriterPrinter; 35import android.util.Printer; 36import android.view.KeyEvent; 37import android.view.LayoutInflater; 38import android.view.MotionEvent; 39import android.view.View; 40import android.view.ViewGroup; 41import android.view.ViewTreeObserver; 42import android.view.Window; 43import android.view.WindowManager; 44import android.view.inputmethod.CompletionInfo; 45import android.view.inputmethod.ExtractedText; 46import android.view.inputmethod.ExtractedTextRequest; 47import android.view.inputmethod.InputBinding; 48import android.view.inputmethod.InputConnection; 49import android.view.inputmethod.InputMethod; 50import android.view.inputmethod.InputMethodManager; 51import android.view.inputmethod.EditorInfo; 52import android.widget.FrameLayout; 53 54import java.io.FileDescriptor; 55import java.io.PrintWriter; 56 57/** 58 * InputMethodService provides a standard implementation of an InputMethod, 59 * which final implementations can derive from and customize. See the 60 * base class {@link AbstractInputMethodService} and the {@link InputMethod} 61 * interface for more information on the basics of writing input methods. 62 * 63 * <p>In addition to the normal Service lifecycle methods, this class 64 * introduces some new specific callbacks that most subclasses will want 65 * to make use of:</p> 66 * <ul> 67 * <li> {@link #onInitializeInterface()} for user-interface initialization, 68 * in particular to deal with configuration changes while the service is 69 * running. 70 * <li> {@link #onBindInput} to find out about switching to a new client. 71 * <li> {@link #onStartInput} to deal with an input session starting with 72 * the client. 73 * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()}, 74 * and {@link #onCreateExtractTextView()} for non-demand generation of the UI. 75 * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input 76 * starting within the input area of the IME. 77 * </ul> 78 * 79 * <p>An input method has significant discretion in how it goes about its 80 * work: the {@link android.inputmethodservice.InputMethodService} provides 81 * a basic framework for standard UI elements (input view, candidates view, 82 * and running in fullscreen mode), but it is up to a particular implementor 83 * to decide how to use them. For example, one input method could implement 84 * an input area with a keyboard, another could allow the user to draw text, 85 * while a third could have no input area (and thus not be visible to the 86 * user) but instead listen to audio and perform text to speech conversion.</p> 87 * 88 * <p>In the implementation provided here, all of these elements are placed 89 * together in a single window managed by the InputMethodService. It will 90 * execute callbacks as it needs information about them, and provides APIs for 91 * programmatic control over them. They layout of these elements is explicitly 92 * defined:</p> 93 * 94 * <ul> 95 * <li>The soft input view, if available, is placed at the bottom of the 96 * screen. 97 * <li>The candidates view, if currently shown, is placed above the soft 98 * input view. 99 * <li>If not running fullscreen, the application is moved or resized to be 100 * above these views; if running fullscreen, the window will completely cover 101 * the application and its top part will contain the extract text of what is 102 * currently being edited by the application. 103 * </ul> 104 * 105 * 106 * <a name="SoftInputView"></a> 107 * <h3>Soft Input View</h3> 108 * 109 * <p>Central to most input methods is the soft input view. This is where most 110 * user interaction occurs: pressing on soft keys, drawing characters, or 111 * however else your input method wants to generate text. Most implementations 112 * will simply have their own view doing all of this work, and return a new 113 * instance of it when {@link #onCreateInputView()} is called. At that point, 114 * as long as the input view is visible, you will see user interaction in 115 * that view and can call back on the InputMethodService to interact with the 116 * application as appropriate.</p> 117 * 118 * <p>There are some situations where you want to decide whether or not your 119 * soft input view should be shown to the user. This is done by implementing 120 * the {@link #onEvaluateInputViewShown()} to return true or false based on 121 * whether it should be shown in the current environment. If any of your 122 * state has changed that may impact this, call 123 * {@link #updateInputViewShown()} to have it re-evaluated. The default 124 * implementation always shows the input view unless there is a hard 125 * keyboard available, which is the appropriate behavior for most input 126 * methods.</p> 127 * 128 * 129 * <a name="CandidatesView"></a> 130 * <h3>Candidates View</h3> 131 * 132 * <p>Often while the user is generating raw text, an input method wants to 133 * provide them with a list of possible interpretations of that text that can 134 * be selected for use. This is accomplished with the candidates view, and 135 * like the soft input view you implement {@link #onCreateCandidatesView()} 136 * to instantiate your own view implementing your candidates UI.</p> 137 * 138 * <p>Management of the candidates view is a little different than the input 139 * view, because the candidates view tends to be more transient, being shown 140 * only when there are possible candidates for the current text being entered 141 * by the user. To control whether the candidates view is shown, you use 142 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate 143 * view tends to be shown and hidden a lot, it does not impact the application 144 * UI in the same way as the soft input view: it will never cause application 145 * windows to resize, only cause them to be panned if needed for the user to 146 * see the current focus.</p> 147 * 148 * 149 * <a name="FullscreenMode"></a> 150 * <h3>Fullscreen Mode</h3> 151 * 152 * <p>Sometimes your input method UI is too large to integrate with the 153 * application UI, so you just want to take over the screen. This is 154 * accomplished by switching to full-screen mode, causing the input method 155 * window to fill the entire screen and add its own "extracted text" editor 156 * showing the user the text that is being typed. Unlike the other UI elements, 157 * there is a standard implementation for the extract editor that you should 158 * not need to change. The editor is placed at the top of the IME, above the 159 * input and candidates views.</p> 160 * 161 * <p>Similar to the input view, you control whether the IME is running in 162 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()} 163 * to return true or false based on 164 * whether it should be fullscreen in the current environment. If any of your 165 * state has changed that may impact this, call 166 * {@link #updateFullscreenMode()} to have it re-evaluated. The default 167 * implementation selects fullscreen mode when the screen is in a landscape 168 * orientation, which is appropriate behavior for most input methods that have 169 * a significant input area.</p> 170 * 171 * <p>When in fullscreen mode, you have some special requirements because the 172 * user can not see the application UI. In particular, you should implement 173 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions 174 * generated by your application, typically in your candidates view like you 175 * would normally show candidates. 176 * 177 * 178 * <a name="GeneratingText"></a> 179 * <h3>Generating Text</h3> 180 * 181 * <p>The key part of an IME is of course generating text for the application. 182 * This is done through calls to the 183 * {@link android.view.inputmethod.InputConnection} interface to the 184 * application, which can be retrieved from {@link #getCurrentInputConnection()}. 185 * This interface allows you to generate raw key events or, if the target 186 * supports it, directly edit in strings of candidates and committed text.</p> 187 * 188 * <p>Information about what the target is expected and supports can be found 189 * through the {@link android.view.inputmethod.EditorInfo} class, which is 190 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most 191 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType 192 * EditorInfo.inputType}; in particular, if this is 193 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL}, 194 * then the target does not support complex edits and you need to only deliver 195 * raw key events to it. An input method will also want to look at other 196 * values here, to for example detect password mode, auto complete text views, 197 * phone number entry, etc.</p> 198 * 199 * <p>When the user switches between input targets, you will receive calls to 200 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}. 201 * You can use these to reset and initialize your input state for the current 202 * target. For example, you will often want to clear any input state, and 203 * update a soft keyboard to be appropriate for the new inputType.</p> 204 */ 205public class InputMethodService extends AbstractInputMethodService { 206 static final String TAG = "InputMethodService"; 207 static final boolean DEBUG = false; 208 209 LayoutInflater mInflater; 210 View mRootView; 211 SoftInputWindow mWindow; 212 boolean mInitialized; 213 boolean mWindowCreated; 214 boolean mWindowAdded; 215 boolean mWindowVisible; 216 FrameLayout mExtractFrame; 217 FrameLayout mCandidatesFrame; 218 FrameLayout mInputFrame; 219 220 IBinder mToken; 221 222 InputBinding mInputBinding; 223 InputConnection mInputConnection; 224 boolean mInputStarted; 225 boolean mInputViewStarted; 226 boolean mCandidatesViewStarted; 227 InputConnection mStartedInputConnection; 228 EditorInfo mInputEditorInfo; 229 230 boolean mShowInputRequested; 231 boolean mLastShowInputRequested; 232 int mCandidatesVisibility; 233 234 boolean mShowInputForced; 235 236 boolean mFullscreenApplied; 237 boolean mIsFullscreen; 238 View mExtractView; 239 ExtractEditText mExtractEditText; 240 ExtractedText mExtractedText; 241 int mExtractedToken; 242 243 View mInputView; 244 boolean mIsInputViewShown; 245 246 int mStatusIcon; 247 248 final Insets mTmpInsets = new Insets(); 249 final int[] mTmpLocation = new int[2]; 250 251 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = 252 new ViewTreeObserver.OnComputeInternalInsetsListener() { 253 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 254 if (isFullscreenMode()) { 255 // In fullscreen mode, we just say the window isn't covering 256 // any content so we don't impact whatever is behind. 257 View decor = getWindow().getWindow().getDecorView(); 258 info.contentInsets.top = info.visibleInsets.top 259 = decor.getHeight(); 260 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 261 } else { 262 onComputeInsets(mTmpInsets); 263 info.contentInsets.top = mTmpInsets.contentTopInsets; 264 info.visibleInsets.top = mTmpInsets.visibleTopInsets; 265 info.setTouchableInsets(mTmpInsets.touchableInsets); 266 } 267 } 268 }; 269 270 /** 271 * Concrete implementation of 272 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides 273 * all of the standard behavior for an input method. 274 */ 275 public class InputMethodImpl extends AbstractInputMethodImpl { 276 /** 277 * Take care of attaching the given window token provided by the system. 278 */ 279 public void attachToken(IBinder token) { 280 if (mToken == null) { 281 mToken = token; 282 mWindow.setToken(token); 283 } 284 } 285 286 /** 287 * Handle a new input binding, calling 288 * {@link InputMethodService#onBindInput InputMethodService.onBindInput()} 289 * when done. 290 */ 291 public void bindInput(InputBinding binding) { 292 mInputBinding = binding; 293 mInputConnection = binding.getConnection(); 294 if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding 295 + " ic=" + mInputConnection); 296 initialize(); 297 onBindInput(); 298 } 299 300 /** 301 * Clear the current input binding. 302 */ 303 public void unbindInput() { 304 if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding 305 + " ic=" + mInputConnection); 306 onUnbindInput(); 307 mInputStarted = false; 308 mInputBinding = null; 309 mInputConnection = null; 310 } 311 312 public void startInput(InputConnection ic, EditorInfo attribute) { 313 if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute); 314 doStartInput(ic, attribute, false); 315 } 316 317 public void restartInput(InputConnection ic, EditorInfo attribute) { 318 if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute); 319 doStartInput(ic, attribute, true); 320 } 321 322 /** 323 * Handle a request by the system to hide the soft input area. 324 */ 325 public void hideSoftInput() { 326 if (DEBUG) Log.v(TAG, "hideSoftInput()"); 327 mShowInputRequested = false; 328 mShowInputForced = false; 329 hideWindow(); 330 } 331 332 /** 333 * Handle a request by the system to show the soft input area. 334 */ 335 public void showSoftInput(int flags) { 336 if (DEBUG) Log.v(TAG, "showSoftInput()"); 337 onShowRequested(flags); 338 } 339 } 340 341 /** 342 * Concrete implementation of 343 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides 344 * all of the standard behavior for an input method session. 345 */ 346 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl { 347 public void finishInput() { 348 if (!isEnabled()) { 349 return; 350 } 351 if (DEBUG) Log.v(TAG, "finishInput() in " + this); 352 doFinishInput(); 353 } 354 355 /** 356 * Call {@link InputMethodService#onDisplayCompletions 357 * InputMethodService.onDisplayCompletions()}. 358 */ 359 public void displayCompletions(CompletionInfo[] completions) { 360 if (!isEnabled()) { 361 return; 362 } 363 onDisplayCompletions(completions); 364 } 365 366 /** 367 * Call {@link InputMethodService#onUpdateExtractedText 368 * InputMethodService.onUpdateExtractedText()}. 369 */ 370 public void updateExtractedText(int token, ExtractedText text) { 371 if (!isEnabled()) { 372 return; 373 } 374 onUpdateExtractedText(token, text); 375 } 376 377 /** 378 * Call {@link InputMethodService#onUpdateSelection 379 * InputMethodService.onUpdateSelection()}. 380 */ 381 public void updateSelection(int oldSelStart, int oldSelEnd, 382 int newSelStart, int newSelEnd, 383 int candidatesStart, int candidatesEnd) { 384 if (!isEnabled()) { 385 return; 386 } 387 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd, 388 newSelStart, newSelEnd, candidatesStart, candidatesEnd); 389 } 390 391 /** 392 * Call {@link InputMethodService#onUpdateCursor 393 * InputMethodService.onUpdateCursor()}. 394 */ 395 public void updateCursor(Rect newCursor) { 396 if (!isEnabled()) { 397 return; 398 } 399 InputMethodService.this.onUpdateCursor(newCursor); 400 } 401 402 /** 403 * Call {@link InputMethodService#onAppPrivateCommand 404 * InputMethodService.onAppPrivateCommand()}. 405 */ 406 public void appPrivateCommand(String action, Bundle data) { 407 if (!isEnabled()) { 408 return; 409 } 410 InputMethodService.this.onAppPrivateCommand(action, data); 411 } 412 } 413 414 /** 415 * Information about where interesting parts of the input method UI appear. 416 */ 417 public static final class Insets { 418 /** 419 * This is the top part of the UI that is the main content. It is 420 * used to determine the basic space needed, to resize/pan the 421 * application behind. It is assumed that this inset does not 422 * change very much, since any change will cause a full resize/pan 423 * of the application behind. This value is relative to the top edge 424 * of the input method window. 425 */ 426 int contentTopInsets; 427 428 /** 429 * This is the top part of the UI that is visibly covering the 430 * application behind it. This provides finer-grained control over 431 * visibility, allowing you to change it relatively frequently (such 432 * as hiding or showing candidates) without disrupting the underlying 433 * UI too much. For example, this will never resize the application 434 * UI, will only pan if needed to make the current focus visible, and 435 * will not aggressively move the pan position when this changes unless 436 * needed to make the focus visible. This value is relative to the top edge 437 * of the input method window. 438 */ 439 int visibleTopInsets; 440 441 /** 442 * Option for {@link #touchableInsets}: the entire window frame 443 * can be touched. 444 */ 445 public static final int TOUCHABLE_INSETS_FRAME 446 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; 447 448 /** 449 * Option for {@link #touchableInsets}: the area inside of 450 * the content insets can be touched. 451 */ 452 public static final int TOUCHABLE_INSETS_CONTENT 453 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; 454 455 /** 456 * Option for {@link #touchableInsets}: the area inside of 457 * the visible insets can be touched. 458 */ 459 public static final int TOUCHABLE_INSETS_VISIBLE 460 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE; 461 462 /** 463 * Determine which area of the window is touchable by the user. May 464 * be one of: {@link #TOUCHABLE_INSETS_FRAME}, 465 * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_VISIBLE}. 466 */ 467 public int touchableInsets; 468 } 469 470 @Override public void onCreate() { 471 super.onCreate(); 472 mInflater = (LayoutInflater)getSystemService( 473 Context.LAYOUT_INFLATER_SERVICE); 474 mWindow = new SoftInputWindow(this); 475 initViews(); 476 mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); 477 } 478 479 /** 480 * This is a hook that subclasses can use to perform initialization of 481 * their interface. It is called for you prior to any of your UI objects 482 * being created, both after the service is first created and after a 483 * configuration change happens. 484 */ 485 public void onInitializeInterface() { 486 } 487 488 void initialize() { 489 if (!mInitialized) { 490 mInitialized = true; 491 onInitializeInterface(); 492 } 493 } 494 495 void initViews() { 496 mInitialized = false; 497 mWindowCreated = false; 498 mShowInputRequested = false; 499 mShowInputForced = false; 500 501 mRootView = mInflater.inflate( 502 com.android.internal.R.layout.input_method, null); 503 mWindow.setContentView(mRootView); 504 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 505 if (Settings.System.getInt(getContentResolver(), 506 Settings.System.FANCY_IME_ANIMATIONS, 0) != 0) { 507 mWindow.getWindow().setWindowAnimations( 508 com.android.internal.R.style.Animation_InputMethodFancy); 509 } 510 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea); 511 mExtractView = null; 512 mExtractEditText = null; 513 mFullscreenApplied = false; 514 515 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea); 516 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea); 517 mInputView = null; 518 mIsInputViewShown = false; 519 520 mExtractFrame.setVisibility(View.GONE); 521 mCandidatesVisibility = getCandidatesHiddenVisibility(); 522 mCandidatesFrame.setVisibility(mCandidatesVisibility); 523 mInputFrame.setVisibility(View.GONE); 524 } 525 526 @Override public void onDestroy() { 527 super.onDestroy(); 528 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 529 mInsetsComputer); 530 if (mWindowAdded) { 531 mWindow.dismiss(); 532 } 533 } 534 535 /** 536 * Take care of handling configuration changes. Subclasses of 537 * InputMethodService generally don't need to deal directly with 538 * this on their own; the standard implementation here takes care of 539 * regenerating the input method UI as a result of the configuration 540 * change, so you can rely on your {@link #onCreateInputView} and 541 * other methods being called as appropriate due to a configuration change. 542 * 543 * <p>When a configuration change does happen, 544 * {@link #onInitializeInterface()} is guaranteed to be called the next 545 * time prior to any of the other input or UI creation callbacks. The 546 * following will be called immediately depending if appropriate for current 547 * state: {@link #onStartInput} if input is active, and 548 * {@link #onCreateInputView} and {@link #onStartInputView} and related 549 * appropriate functions if the UI is displayed. 550 */ 551 @Override public void onConfigurationChanged(Configuration newConfig) { 552 super.onConfigurationChanged(newConfig); 553 554 boolean visible = mWindowVisible; 555 boolean showingInput = mShowInputRequested; 556 boolean showingForced = mShowInputForced; 557 boolean showingCandidates = mCandidatesVisibility == View.VISIBLE; 558 initViews(); 559 mInputViewStarted = false; 560 mCandidatesViewStarted = false; 561 if (mInputStarted) { 562 doStartInput(getCurrentInputConnection(), 563 getCurrentInputEditorInfo(), true); 564 } 565 if (visible) { 566 if (showingForced) { 567 // If we are showing the full soft keyboard, then go through 568 // this path to take care of current decisions about fullscreen 569 // etc. 570 onShowRequested(InputMethod.SHOW_FORCED|InputMethod.SHOW_EXPLICIT); 571 } else if (showingInput) { 572 // If we are showing the full soft keyboard, then go through 573 // this path to take care of current decisions about fullscreen 574 // etc. 575 onShowRequested(InputMethod.SHOW_EXPLICIT); 576 } else { 577 // Otherwise just put it back for its candidates. 578 showWindow(false); 579 } 580 if (showingCandidates) { 581 setCandidatesViewShown(true); 582 } 583 } 584 } 585 586 /** 587 * Implement to return our standard {@link InputMethodImpl}. Subclasses 588 * can override to provide their own customized version. 589 */ 590 public AbstractInputMethodImpl onCreateInputMethodInterface() { 591 return new InputMethodImpl(); 592 } 593 594 /** 595 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses 596 * can override to provide their own customized version. 597 */ 598 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() { 599 return new InputMethodSessionImpl(); 600 } 601 602 public LayoutInflater getLayoutInflater() { 603 return mInflater; 604 } 605 606 public Dialog getWindow() { 607 return mWindow; 608 } 609 610 /** 611 * Return the maximum width, in pixels, available the input method. 612 * Input methods are positioned at the bottom of the screen and, unless 613 * running in fullscreen, will generally want to be as short as possible 614 * so should compute their height based on their contents. However, they 615 * can stretch as much as needed horizontally. The function returns to 616 * you the maximum amount of space available horizontally, which you can 617 * use if needed for UI placement. 618 * 619 * <p>In many cases this is not needed, you can just rely on the normal 620 * view layout mechanisms to position your views within the full horizontal 621 * space given to the input method. 622 * 623 * <p>Note that this value can change dynamically, in particular when the 624 * screen orientation changes. 625 */ 626 public int getMaxWidth() { 627 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 628 return wm.getDefaultDisplay().getWidth(); 629 } 630 631 /** 632 * Return the currently active InputBinding for the input method, or 633 * null if there is none. 634 */ 635 public InputBinding getCurrentInputBinding() { 636 return mInputBinding; 637 } 638 639 /** 640 * Retrieve the currently active InputConnection that is bound to 641 * the input method, or null if there is none. 642 */ 643 public InputConnection getCurrentInputConnection() { 644 InputConnection ic = mStartedInputConnection; 645 if (ic != null) { 646 return ic; 647 } 648 return mInputConnection; 649 } 650 651 public boolean getCurrentInputStarted() { 652 return mInputStarted; 653 } 654 655 public EditorInfo getCurrentInputEditorInfo() { 656 return mInputEditorInfo; 657 } 658 659 /** 660 * Re-evaluate whether the input method should be running in fullscreen 661 * mode, and update its UI if this has changed since the last time it 662 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to 663 * determine whether it should currently run in fullscreen mode. You 664 * can use {@link #isFullscreenMode()} to determine if the input method 665 * is currently running in fullscreen mode. 666 */ 667 public void updateFullscreenMode() { 668 boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode(); 669 boolean changed = mLastShowInputRequested != mShowInputRequested; 670 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { 671 changed = true; 672 mIsFullscreen = isFullscreen; 673 mFullscreenApplied = true; 674 initialize(); 675 Drawable bg = onCreateBackgroundDrawable(); 676 if (bg == null) { 677 // We need to give the window a real drawable, so that it 678 // correctly sets its mode. 679 bg = getResources().getDrawable(android.R.color.transparent); 680 } 681 mWindow.getWindow().setBackgroundDrawable(bg); 682 mExtractFrame.setVisibility(isFullscreen ? View.VISIBLE : View.GONE); 683 if (isFullscreen) { 684 if (mExtractView == null) { 685 View v = onCreateExtractTextView(); 686 if (v != null) { 687 setExtractView(v); 688 } 689 } 690 startExtractingText(); 691 } 692 } 693 694 if (changed) { 695 onConfigureWindow(mWindow.getWindow(), isFullscreen, 696 !mShowInputRequested); 697 mLastShowInputRequested = mShowInputRequested; 698 } 699 } 700 701 /** 702 * Update the given window's parameters for the given mode. This is called 703 * when the window is first displayed and each time the fullscreen or 704 * candidates only mode changes. 705 * 706 * <p>The default implementation makes the layout for the window 707 * FILL_PARENT x FILL_PARENT when in fullscreen mode, and 708 * FILL_PARENT x WRAP_CONTENT when in non-fullscreen mode. 709 * 710 * @param win The input method's window. 711 * @param isFullscreen If true, the window is running in fullscreen mode 712 * and intended to cover the entire application display. 713 * @param isCandidatesOnly If true, the window is only showing the 714 * candidates view and none of the rest of its UI. This is mutually 715 * exclusive with fullscreen mode. 716 */ 717 public void onConfigureWindow(Window win, boolean isFullscreen, 718 boolean isCandidatesOnly) { 719 if (isFullscreen) { 720 mWindow.getWindow().setLayout(FILL_PARENT, FILL_PARENT); 721 } else { 722 mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); 723 } 724 } 725 726 /** 727 * Return whether the input method is <em>currently</em> running in 728 * fullscreen mode. This is the mode that was last determined and 729 * applied by {@link #updateFullscreenMode()}. 730 */ 731 public boolean isFullscreenMode() { 732 return mIsFullscreen; 733 } 734 735 /** 736 * Override this to control when the input method should run in 737 * fullscreen mode. The default implementation runs in fullsceen only 738 * when the screen is in landscape mode. If you change what 739 * this returns, you will need to call {@link #updateFullscreenMode()} 740 * yourself whenever the returned value may have changed to have it 741 * re-evaluated and applied. 742 */ 743 public boolean onEvaluateFullscreenMode() { 744 Configuration config = getResources().getConfiguration(); 745 return config.orientation == Configuration.ORIENTATION_LANDSCAPE; 746 } 747 748 /** 749 * Compute the interesting insets into your UI. The default implementation 750 * uses the top of the candidates frame for the visible insets, and the 751 * top of the input frame for the content insets. The default touchable 752 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}. 753 * 754 * <p>Note that this method is not called when in fullscreen mode, since 755 * in that case the application is left as-is behind the input method and 756 * not impacted by anything in its UI. 757 * 758 * @param outInsets Fill in with the current UI insets. 759 */ 760 public void onComputeInsets(Insets outInsets) { 761 int[] loc = mTmpLocation; 762 if (mInputFrame.getVisibility() == View.VISIBLE) { 763 mInputFrame.getLocationInWindow(loc); 764 } else { 765 loc[1] = 0; 766 } 767 outInsets.contentTopInsets = loc[1]; 768 if (mCandidatesFrame.getVisibility() == View.VISIBLE) { 769 mCandidatesFrame.getLocationInWindow(loc); 770 } 771 outInsets.visibleTopInsets = loc[1]; 772 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE; 773 } 774 775 /** 776 * Re-evaluate whether the soft input area should currently be shown, and 777 * update its UI if this has changed since the last time it 778 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to 779 * determine whether the input view should currently be shown. You 780 * can use {@link #isInputViewShown()} to determine if the input view 781 * is currently shown. 782 */ 783 public void updateInputViewShown() { 784 boolean isShown = mShowInputRequested && onEvaluateInputViewShown(); 785 if (mIsInputViewShown != isShown && mWindowVisible) { 786 mIsInputViewShown = isShown; 787 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE); 788 if (mInputView == null) { 789 initialize(); 790 View v = onCreateInputView(); 791 if (v != null) { 792 setInputView(v); 793 } 794 } 795 } 796 } 797 798 /** 799 * Returns true if we have been asked to show our input view. 800 */ 801 public boolean isShowInputRequested() { 802 return mShowInputRequested; 803 } 804 805 /** 806 * Return whether the soft input view is <em>currently</em> shown to the 807 * user. This is the state that was last determined and 808 * applied by {@link #updateInputViewShown()}. 809 */ 810 public boolean isInputViewShown() { 811 return mIsInputViewShown && mWindowVisible; 812 } 813 814 /** 815 * Override this to control when the soft input area should be shown to 816 * the user. The default implementation only shows the input view when 817 * there is no hard keyboard or the keyboard is hidden. If you change what 818 * this returns, you will need to call {@link #updateInputViewShown()} 819 * yourself whenever the returned value may have changed to have it 820 * re-evalauted and applied. 821 */ 822 public boolean onEvaluateInputViewShown() { 823 Configuration config = getResources().getConfiguration(); 824 return config.keyboard == Configuration.KEYBOARD_NOKEYS 825 || config.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES; 826 } 827 828 /** 829 * Controls the visibility of the candidates display area. By default 830 * it is hidden. 831 */ 832 public void setCandidatesViewShown(boolean shown) { 833 int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility(); 834 if (mCandidatesVisibility != vis) { 835 mCandidatesFrame.setVisibility(vis); 836 mCandidatesVisibility = vis; 837 } 838 if (!mShowInputRequested && mWindowVisible != shown) { 839 // If we are being asked to show the candidates view while the app 840 // has not asked for the input view to be shown, then we need 841 // to update whether the window is shown. 842 if (shown) { 843 showWindow(false); 844 } else { 845 hideWindow(); 846 } 847 } 848 } 849 850 /** 851 * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE} 852 * or {@link View#GONE View.GONE}) of the candidates view when it is not 853 * shown. The default implementation returns GONE when in fullscreen mode, 854 * otherwise VISIBLE. Be careful if you change this to return GONE in 855 * other situations -- if showing or hiding the candidates view causes 856 * your window to resize, this can cause temporary drawing artifacts as 857 * the resize takes place. 858 */ 859 public int getCandidatesHiddenVisibility() { 860 return isFullscreenMode() ? View.GONE : View.INVISIBLE; 861 } 862 863 public void setStatusIcon(int iconResId) { 864 mStatusIcon = iconResId; 865 InputConnection ic = getCurrentInputConnection(); 866 if (ic != null && mWindowVisible) { 867 ic.showStatusIcon(getPackageName(), iconResId); 868 } 869 } 870 871 /** 872 * Force switch to a new input method, as identified by <var>id</var>. This 873 * input method will be destroyed, and the requested one started on the 874 * current input field. 875 * 876 * @param id Unique identifier of the new input method ot start. 877 */ 878 public void switchInputMethod(String id) { 879 ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) 880 .setInputMethod(mToken, id); 881 } 882 883 public void setExtractView(View view) { 884 mExtractFrame.removeAllViews(); 885 mExtractFrame.addView(view, new FrameLayout.LayoutParams( 886 ViewGroup.LayoutParams.FILL_PARENT, 887 ViewGroup.LayoutParams.FILL_PARENT)); 888 mExtractView = view; 889 if (view != null) { 890 mExtractEditText = (ExtractEditText)view.findViewById( 891 com.android.internal.R.id.inputExtractEditText); 892 mExtractEditText.setIME(this); 893 startExtractingText(); 894 } else { 895 mExtractEditText = null; 896 } 897 } 898 899 /** 900 * Replaces the current candidates view with a new one. You only need to 901 * call this when dynamically changing the view; normally, you should 902 * implement {@link #onCreateCandidatesView()} and create your view when 903 * first needed by the input method. 904 */ 905 public void setCandidatesView(View view) { 906 mCandidatesFrame.removeAllViews(); 907 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams( 908 ViewGroup.LayoutParams.FILL_PARENT, 909 ViewGroup.LayoutParams.WRAP_CONTENT)); 910 } 911 912 /** 913 * Replaces the current input view with a new one. You only need to 914 * call this when dynamically changing the view; normally, you should 915 * implement {@link #onCreateInputView()} and create your view when 916 * first needed by the input method. 917 */ 918 public void setInputView(View view) { 919 mInputFrame.removeAllViews(); 920 mInputFrame.addView(view, new FrameLayout.LayoutParams( 921 ViewGroup.LayoutParams.FILL_PARENT, 922 ViewGroup.LayoutParams.WRAP_CONTENT)); 923 mInputView = view; 924 } 925 926 /** 927 * Called by the framework to create a Drawable for the background of 928 * the input method window. May return null for no background. The default 929 * implementation returns a non-null standard background only when in 930 * fullscreen mode. This is called each time the fullscreen mode changes. 931 */ 932 public Drawable onCreateBackgroundDrawable() { 933 if (isFullscreenMode()) { 934 return getResources().getDrawable( 935 com.android.internal.R.drawable.input_method_fullscreen_background); 936 } 937 return null; 938 } 939 940 /** 941 * Called by the framework to create the layout for showing extacted text. 942 * Only called when in fullscreen mode. The returned view hierarchy must 943 * have an {@link ExtractEditText} whose ID is 944 * {@link android.R.id#inputExtractEditText}. 945 */ 946 public View onCreateExtractTextView() { 947 return mInflater.inflate( 948 com.android.internal.R.layout.input_method_extract_view, null); 949 } 950 951 /** 952 * Create and return the view hierarchy used to show candidates. This will 953 * be called once, when the candidates are first displayed. You can return 954 * null to have no candidates view; the default implementation returns null. 955 * 956 * <p>To control when the candidates view is displayed, use 957 * {@link #setCandidatesViewShown(boolean)}. 958 * To change the candidates view after the first one is created by this 959 * function, use {@link #setCandidatesView(View)}. 960 */ 961 public View onCreateCandidatesView() { 962 return null; 963 } 964 965 /** 966 * Create and return the view hierarchy used for the input area (such as 967 * a soft keyboard). This will be called once, when the input area is 968 * first displayed. You can return null to have no input area; the default 969 * implementation returns null. 970 * 971 * <p>To control when the input view is displayed, implement 972 * {@link #onEvaluateInputViewShown()}. 973 * To change the input view after the first one is created by this 974 * function, use {@link #setInputView(View)}. 975 */ 976 public View onCreateInputView() { 977 return null; 978 } 979 980 /** 981 * Called when the input view is being shown and input has started on 982 * a new editor. This will always be called after {@link #onStartInput}, 983 * allowing you to do your general setup there and just view-specific 984 * setup here. You are guaranteed that {@link #onCreateInputView()} will 985 * have been called some time before this function is called. 986 * 987 * @param info Description of the type of text being edited. 988 * @param restarting Set to true if we are restarting input on the 989 * same text field as before. 990 */ 991 public void onStartInputView(EditorInfo info, boolean restarting) { 992 } 993 994 /** 995 * Called when the input view is being hidden from the user. This will 996 * be called either prior to hiding the window, or prior to switching to 997 * another target for editing. 998 * 999 * <p>The default 1000 * implementation uses the InputConnection to clear any active composing 1001 * text; you can override this (not calling the base class implementation) 1002 * to perform whatever behavior you would like. 1003 * 1004 * @boolean finishingInput If true, {@link #onFinishInput} will be 1005 * called immediately after. 1006 */ 1007 public void onFinishInputView(boolean finishingInput) { 1008 if (!finishingInput) { 1009 InputConnection ic = getCurrentInputConnection(); 1010 if (ic != null) { 1011 ic.finishComposingText(); 1012 } 1013 } 1014 } 1015 1016 /** 1017 * Called when only the candidates view has been shown for showing 1018 * processing as the user enters text through a hard keyboard. 1019 * This will always be called after {@link #onStartInput}, 1020 * allowing you to do your general setup there and just view-specific 1021 * setup here. You are guaranteed that {@link #onCreateCandidatesView()} 1022 * will have been called some time before this function is called. 1023 * 1024 * <p>Note that this will <em>not</em> be called when the input method 1025 * is running in full editing mode, and thus receiving 1026 * {@link #onStartInputView} to initiate that operation. This is only 1027 * for the case when candidates are being shown while the input method 1028 * editor is hidden but wants to show its candidates UI as text is 1029 * entered through some other mechanism. 1030 * 1031 * @param info Description of the type of text being edited. 1032 * @param restarting Set to true if we are restarting input on the 1033 * same text field as before. 1034 */ 1035 public void onStartCandidatesView(EditorInfo info, boolean restarting) { 1036 } 1037 1038 /** 1039 * Called when the candidates view is being hidden from the user. This will 1040 * be called either prior to hiding the window, or prior to switching to 1041 * another target for editing. 1042 * 1043 * <p>The default 1044 * implementation uses the InputConnection to clear any active composing 1045 * text; you can override this (not calling the base class implementation) 1046 * to perform whatever behavior you would like. 1047 * 1048 * @boolean finishingInput If true, {@link #onFinishInput} will be 1049 * called immediately after. 1050 */ 1051 public void onFinishCandidatesView(boolean finishingInput) { 1052 if (!finishingInput) { 1053 InputConnection ic = getCurrentInputConnection(); 1054 if (ic != null) { 1055 ic.finishComposingText(); 1056 } 1057 } 1058 } 1059 1060 /** 1061 * The system has decided that it may be time to show your input method. 1062 * This is called due to a corresponding call to your 1063 * {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)} 1064 * method. The default implementation simply calls 1065 * {@link #showWindow(boolean)}, except if the 1066 * {@link InputMethod#SHOW_EXPLICIT InputMethod.SHOW_EXPLICIT} flag is 1067 * not set and the input method is running in fullscreen mode. 1068 * 1069 * @param flags Provides additional information about the show request, 1070 * as per {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)}. 1071 */ 1072 public void onShowRequested(int flags) { 1073 if (!onEvaluateInputViewShown()) { 1074 return; 1075 } 1076 if ((flags&InputMethod.SHOW_EXPLICIT) == 0) { 1077 if (onEvaluateFullscreenMode()) { 1078 // Don't show if this is not explicitly requested by the user and 1079 // the input method is fullscreen. That would be too disruptive. 1080 return; 1081 } 1082 Configuration config = getResources().getConfiguration(); 1083 if (config.keyboard != Configuration.KEYBOARD_NOKEYS) { 1084 // And if the device has a hard keyboard, even if it is 1085 // currently hidden, don't show the input method implicitly. 1086 // These kinds of devices don't need it that much. 1087 return; 1088 } 1089 } 1090 if ((flags&InputMethod.SHOW_FORCED) != 0) { 1091 mShowInputForced = true; 1092 } 1093 showWindow(true); 1094 } 1095 1096 public void showWindow(boolean showInput) { 1097 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput 1098 + " mShowInputRequested=" + mShowInputRequested 1099 + " mWindowAdded=" + mWindowAdded 1100 + " mWindowCreated=" + mWindowCreated 1101 + " mWindowVisible=" + mWindowVisible 1102 + " mInputStarted=" + mInputStarted); 1103 boolean doShowInput = false; 1104 boolean wasVisible = mWindowVisible; 1105 boolean wasCreated = mWindowCreated; 1106 mWindowVisible = true; 1107 if (!mShowInputRequested) { 1108 if (mInputStarted) { 1109 if (showInput) { 1110 doShowInput = true; 1111 mShowInputRequested = true; 1112 } 1113 } 1114 } else { 1115 showInput = true; 1116 } 1117 1118 if (DEBUG) Log.v(TAG, "showWindow: updating UI"); 1119 initialize(); 1120 updateFullscreenMode(); 1121 updateInputViewShown(); 1122 1123 if (!mWindowAdded || !mWindowCreated) { 1124 mWindowAdded = true; 1125 mWindowCreated = true; 1126 initialize(); 1127 if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView"); 1128 View v = onCreateCandidatesView(); 1129 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v); 1130 if (v != null) { 1131 setCandidatesView(v); 1132 } 1133 } 1134 if (mShowInputRequested) { 1135 if (!mInputViewStarted) { 1136 if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); 1137 mInputViewStarted = true; 1138 onStartInputView(mInputEditorInfo, false); 1139 } 1140 } else if (!mCandidatesViewStarted) { 1141 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); 1142 mCandidatesViewStarted = true; 1143 onStartCandidatesView(mInputEditorInfo, false); 1144 } 1145 1146 if (doShowInput) { 1147 startExtractingText(); 1148 } 1149 1150 if (!wasVisible) { 1151 if (DEBUG) Log.v(TAG, "showWindow: showing!"); 1152 mWindow.show(); 1153 } 1154 1155 if (!wasVisible || !wasCreated) { 1156 InputConnection ic = getCurrentInputConnection(); 1157 if (ic != null) { 1158 ic.showStatusIcon(getPackageName(), mStatusIcon); 1159 } 1160 } 1161 } 1162 1163 public void hideWindow() { 1164 if (mInputViewStarted) { 1165 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); 1166 onFinishInputView(false); 1167 } else if (mCandidatesViewStarted) { 1168 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView"); 1169 onFinishCandidatesView(false); 1170 } 1171 mInputViewStarted = false; 1172 mCandidatesViewStarted = false; 1173 if (mWindowVisible) { 1174 mWindow.hide(); 1175 mWindowVisible = false; 1176 InputConnection ic = getCurrentInputConnection(); 1177 if (ic != null) { 1178 ic.hideStatusIcon(); 1179 } 1180 } 1181 } 1182 1183 /** 1184 * Called when a new client has bound to the input method. This 1185 * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)} 1186 * and {@link #onFinishInput()} calls as the user navigates through its 1187 * UI. Upon this call you know that {@link #getCurrentInputBinding} 1188 * and {@link #getCurrentInputConnection} return valid objects. 1189 */ 1190 public void onBindInput() { 1191 } 1192 1193 /** 1194 * Called when the previous bound client is no longer associated 1195 * with the input method. After returning {@link #getCurrentInputBinding} 1196 * and {@link #getCurrentInputConnection} will no longer return 1197 * valid objects. 1198 */ 1199 public void onUnbindInput() { 1200 } 1201 1202 /** 1203 * Called to inform the input method that text input has started in an 1204 * editor. You should use this callback to initialize the state of your 1205 * input to match the state of the editor given to it. 1206 * 1207 * @param attribute The attributes of the editor that input is starting 1208 * in. 1209 * @param restarting Set to true if input is restarting in the same 1210 * editor such as because the application has changed the text in 1211 * the editor. Otherwise will be false, indicating this is a new 1212 * session with the editor. 1213 */ 1214 public void onStartInput(EditorInfo attribute, boolean restarting) { 1215 } 1216 1217 void doFinishInput() { 1218 if (mInputViewStarted) { 1219 if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); 1220 onFinishInputView(true); 1221 } else if (mCandidatesViewStarted) { 1222 if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView"); 1223 onFinishCandidatesView(true); 1224 } 1225 mInputViewStarted = false; 1226 mCandidatesViewStarted = false; 1227 if (mInputStarted) { 1228 if (DEBUG) Log.v(TAG, "CALL: onFinishInput"); 1229 onFinishInput(); 1230 } 1231 mInputStarted = false; 1232 mStartedInputConnection = null; 1233 } 1234 1235 void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { 1236 if (!restarting) { 1237 doFinishInput(); 1238 } 1239 mInputStarted = true; 1240 mStartedInputConnection = ic; 1241 mInputEditorInfo = attribute; 1242 initialize(); 1243 if (DEBUG) Log.v(TAG, "CALL: onStartInput"); 1244 onStartInput(attribute, restarting); 1245 if (mWindowVisible) { 1246 if (mShowInputRequested) { 1247 if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); 1248 mInputViewStarted = true; 1249 onStartInputView(mInputEditorInfo, restarting); 1250 startExtractingText(); 1251 } else if (mCandidatesVisibility == View.VISIBLE) { 1252 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); 1253 mCandidatesViewStarted = true; 1254 onStartCandidatesView(mInputEditorInfo, restarting); 1255 } 1256 } 1257 } 1258 1259 /** 1260 * Called to inform the input method that text input has finished in 1261 * the last editor. At this point there may be a call to 1262 * {@link #onStartInput(EditorInfo, boolean)} to perform input in a 1263 * new editor, or the input method may be left idle. This method is 1264 * <em>not</em> called when input restarts in the same editor. 1265 * 1266 * <p>The default 1267 * implementation uses the InputConnection to clear any active composing 1268 * text; you can override this (not calling the base class implementation) 1269 * to perform whatever behavior you would like. 1270 */ 1271 public void onFinishInput() { 1272 InputConnection ic = getCurrentInputConnection(); 1273 if (ic != null) { 1274 ic.finishComposingText(); 1275 } 1276 } 1277 1278 /** 1279 * Called when the application has reported auto-completion candidates that 1280 * it would like to have the input method displayed. Typically these are 1281 * only used when an input method is running in full-screen mode, since 1282 * otherwise the user can see and interact with the pop-up window of 1283 * completions shown by the application. 1284 * 1285 * <p>The default implementation here does nothing. 1286 */ 1287 public void onDisplayCompletions(CompletionInfo[] completions) { 1288 } 1289 1290 /** 1291 * Called when the application has reported new extracted text to be shown 1292 * due to changes in its current text state. The default implementation 1293 * here places the new text in the extract edit text, when the input 1294 * method is running in fullscreen mode. 1295 */ 1296 public void onUpdateExtractedText(int token, ExtractedText text) { 1297 if (mExtractedToken != token) { 1298 return; 1299 } 1300 if (mExtractEditText != null && text != null) { 1301 mExtractedText = text; 1302 mExtractEditText.setExtractedText(text); 1303 } 1304 } 1305 1306 /** 1307 * Called when the application has reported a new selection region of 1308 * the text. This is called whether or not the input method has requested 1309 * extracted text updates, although if so it will not receive this call 1310 * if the extracted text has changed as well. 1311 * 1312 * <p>The default implementation takes care of updating the cursor in 1313 * the extract text, if it is being shown. 1314 */ 1315 public void onUpdateSelection(int oldSelStart, int oldSelEnd, 1316 int newSelStart, int newSelEnd, 1317 int candidatesStart, int candidatesEnd) { 1318 final ExtractEditText eet = mExtractEditText; 1319 if (eet != null && mExtractedText != null) { 1320 final int off = mExtractedText.startOffset; 1321 eet.startInternalChanges(); 1322 eet.setSelection(newSelStart-off, newSelEnd-off); 1323 eet.finishInternalChanges(); 1324 } 1325 } 1326 1327 /** 1328 * Called when the application has reported a new location of its text 1329 * cursor. This is only called if explicitly requested by the input method. 1330 * The default implementation does nothing. 1331 */ 1332 public void onUpdateCursor(Rect newCursor) { 1333 } 1334 1335 /** 1336 * Close this input method's soft input area, removing it from the display. 1337 * The input method will continue running, but the user can no longer use 1338 * it to generate input by touching the screen. 1339 * @param flags Provides additional operating flags. Currently may be 1340 * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY 1341 * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set. 1342 */ 1343 public void dismissSoftInput(int flags) { 1344 ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) 1345 .hideSoftInputFromInputMethod(mToken, flags); 1346 } 1347 1348 /** 1349 * Override this to intercept key down events before they are processed by the 1350 * application. If you return true, the application will not itself 1351 * process the event. If you return true, the normal application processing 1352 * will occur as if the IME had not seen the event at all. 1353 * 1354 * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK 1355 * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In 1356 * additional, in fullscreen mode only, it will consume DPAD movement 1357 * events to move the cursor in the extracted text view, not allowing 1358 * them to perform navigation in the underlying application. 1359 */ 1360 public boolean onKeyDown(int keyCode, KeyEvent event) { 1361 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK 1362 && event.getRepeatCount() == 0) { 1363 if (mShowInputRequested) { 1364 // If the soft input area is shown, back closes it and we 1365 // consume the back key. 1366 dismissSoftInput(0); 1367 return true; 1368 } else if (mWindowVisible) { 1369 if (mCandidatesVisibility == View.VISIBLE) { 1370 // If we are showing candidates even if no input area, then 1371 // hide them. 1372 setCandidatesViewShown(false); 1373 return true; 1374 } else { 1375 // If we have the window visible for some other reason -- 1376 // most likely to show candidates -- then just get rid 1377 // of it. This really shouldn't happen, but just in case... 1378 hideWindow(); 1379 return true; 1380 } 1381 } 1382 } 1383 1384 return doMovementKey(keyCode, event, MOVEMENT_DOWN); 1385 } 1386 1387 /** 1388 * Override this to intercept special key multiple events before they are 1389 * processed by the 1390 * application. If you return true, the application will not itself 1391 * process the event. If you return true, the normal application processing 1392 * will occur as if the IME had not seen the event at all. 1393 * 1394 * <p>The default implementation always returns false, except when 1395 * in fullscreen mode, where it will consume DPAD movement 1396 * events to move the cursor in the extracted text view, not allowing 1397 * them to perform navigation in the underlying application. 1398 */ 1399 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 1400 return doMovementKey(keyCode, event, count); 1401 } 1402 1403 /** 1404 * Override this to intercept key up events before they are processed by the 1405 * application. If you return true, the application will not itself 1406 * process the event. If you return true, the normal application processing 1407 * will occur as if the IME had not seen the event at all. 1408 * 1409 * <p>The default implementation always returns false, except when 1410 * in fullscreen mode, where it will consume DPAD movement 1411 * events to move the cursor in the extracted text view, not allowing 1412 * them to perform navigation in the underlying application. 1413 */ 1414 public boolean onKeyUp(int keyCode, KeyEvent event) { 1415 return doMovementKey(keyCode, event, MOVEMENT_UP); 1416 } 1417 1418 public boolean onTrackballEvent(MotionEvent event) { 1419 return false; 1420 } 1421 1422 public void onAppPrivateCommand(String action, Bundle data) { 1423 } 1424 1425 static final int MOVEMENT_DOWN = -1; 1426 static final int MOVEMENT_UP = -2; 1427 1428 boolean doMovementKey(int keyCode, KeyEvent event, int count) { 1429 final ExtractEditText eet = mExtractEditText; 1430 if (isFullscreenMode() && isInputViewShown() && eet != null) { 1431 // If we are in fullscreen mode, the cursor will move around 1432 // the extract edit text, but should NOT cause focus to move 1433 // to other fields. 1434 MovementMethod movement = eet.getMovementMethod(); 1435 Layout layout = eet.getLayout(); 1436 if (movement != null && layout != null) { 1437 // We want our own movement method to handle the key, so the 1438 // cursor will properly move in our own word wrapping. 1439 if (count == MOVEMENT_DOWN) { 1440 if (movement.onKeyDown(eet, 1441 (Spannable)eet.getText(), keyCode, event)) { 1442 return true; 1443 } 1444 } else if (count == MOVEMENT_UP) { 1445 if (movement.onKeyUp(eet, 1446 (Spannable)eet.getText(), keyCode, event)) { 1447 return true; 1448 } 1449 } else { 1450 KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); 1451 if (movement.onKeyDown(eet, 1452 (Spannable)eet.getText(), keyCode, down)) { 1453 KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP); 1454 movement.onKeyUp(eet, 1455 (Spannable)eet.getText(), keyCode, up); 1456 while (--count > 0) { 1457 movement.onKeyDown(eet, 1458 (Spannable)eet.getText(), keyCode, down); 1459 movement.onKeyUp(eet, 1460 (Spannable)eet.getText(), keyCode, up); 1461 } 1462 } 1463 } 1464 } 1465 // Regardless of whether the movement method handled the key, 1466 // we never allow DPAD navigation to the application. 1467 switch (keyCode) { 1468 case KeyEvent.KEYCODE_DPAD_LEFT: 1469 case KeyEvent.KEYCODE_DPAD_RIGHT: 1470 case KeyEvent.KEYCODE_DPAD_UP: 1471 case KeyEvent.KEYCODE_DPAD_DOWN: 1472 return true; 1473 } 1474 } 1475 1476 return false; 1477 } 1478 1479 /** 1480 * This is called when the user has moved the cursor in the extracted 1481 * text view, when running in fullsreen mode. The default implementation 1482 * performs the corresponding selection change on the underlying text 1483 * editor. 1484 */ 1485 public void onExtractedSelectionChanged(int start, int end) { 1486 InputConnection conn = getCurrentInputConnection(); 1487 if (conn != null) { 1488 conn.setSelection(start, end); 1489 } 1490 } 1491 1492 /** 1493 * This is called when the user has clicked on the extracted text view, 1494 * when running in fullscreen mode. The default implementation hides 1495 * the candidates view when this happens. Re-implement this to provide 1496 * whatever behavior you want. 1497 */ 1498 public void onExtractedTextClicked() { 1499 setCandidatesViewShown(false); 1500 } 1501 1502 /** 1503 * This is called when the user has selected a context menu item from the 1504 * extracted text view, when running in fullscreen mode. The default 1505 * implementation sends this action to the current InputConnection's 1506 * {@link InputConnection#performContextMenuAction(int)}, for it 1507 * to be processed in underlying "real" editor. Re-implement this to 1508 * provide whatever behavior you want. 1509 */ 1510 public boolean onExtractTextContextMenuItem(int id) { 1511 InputConnection ic = getCurrentInputConnection(); 1512 if (ic != null) { 1513 ic.performContextMenuAction(id); 1514 } 1515 return true; 1516 } 1517 1518 void startExtractingText() { 1519 final ExtractEditText eet = mExtractEditText; 1520 if (eet != null && getCurrentInputStarted() 1521 && isFullscreenMode()) { 1522 mExtractedToken++; 1523 ExtractedTextRequest req = new ExtractedTextRequest(); 1524 req.token = mExtractedToken; 1525 req.flags = InputConnection.GET_TEXT_WITH_STYLES; 1526 req.hintMaxLines = 10; 1527 req.hintMaxChars = 10000; 1528 mExtractedText = getCurrentInputConnection().getExtractedText(req, 1529 InputConnection.GET_EXTRACTED_TEXT_MONITOR); 1530 try { 1531 eet.startInternalChanges(); 1532 int inputType = getCurrentInputEditorInfo().inputType; 1533 if ((inputType&EditorInfo.TYPE_MASK_CLASS) 1534 == EditorInfo.TYPE_CLASS_TEXT) { 1535 if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) { 1536 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; 1537 } 1538 } 1539 eet.setInputType(inputType); 1540 eet.setHint(mInputEditorInfo.hintText); 1541 if (mExtractedText != null) { 1542 eet.setExtractedText(mExtractedText); 1543 } 1544 } finally { 1545 eet.finishInternalChanges(); 1546 } 1547 } 1548 } 1549 1550 /** 1551 * Performs a dump of the InputMethodService's internal state. Override 1552 * to add your own information to the dump. 1553 */ 1554 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 1555 final Printer p = new PrintWriterPrinter(fout); 1556 p.println("Input method service state for " + this + ":"); 1557 p.println(" mWindowCreated=" + mWindowCreated 1558 + " mWindowAdded=" + mWindowAdded 1559 + " mWindowVisible=" + mWindowVisible); 1560 p.println(" Configuration=" + getResources().getConfiguration()); 1561 p.println(" mToken=" + mToken); 1562 p.println(" mInputBinding=" + mInputBinding); 1563 p.println(" mInputConnection=" + mInputConnection); 1564 p.println(" mStartedInputConnection=" + mStartedInputConnection); 1565 p.println(" mInputStarted=" + mInputStarted 1566 + " mInputViewStarted=" + mInputViewStarted 1567 + " mCandidatesViewStarted=" + mCandidatesViewStarted); 1568 1569 if (mInputEditorInfo != null) { 1570 p.println(" mInputEditorInfo:"); 1571 mInputEditorInfo.dump(p, " "); 1572 } else { 1573 p.println(" mInputEditorInfo: null"); 1574 } 1575 1576 p.println(" mShowInputRequested=" + mShowInputRequested 1577 + " mLastShowInputRequested=" + mLastShowInputRequested 1578 + " mShowInputForced=" + mShowInputForced); 1579 p.println(" mCandidatesVisibility=" + mCandidatesVisibility 1580 + " mFullscreenApplied=" + mFullscreenApplied 1581 + " mIsFullscreen=" + mIsFullscreen); 1582 1583 if (mExtractedText != null) { 1584 p.println(" mExtractedText:"); 1585 p.println(" text=" + mExtractedText.text.length() + " chars" 1586 + " startOffset=" + mExtractedText.startOffset); 1587 p.println(" selectionStart=" + mExtractedText.selectionStart 1588 + " selectionEnd=" + mExtractedText.selectionEnd 1589 + " flags=0x" + Integer.toHexString(mExtractedText.flags)); 1590 } else { 1591 p.println(" mExtractedText: null"); 1592 } 1593 p.println(" mExtractedToken=" + mExtractedToken); 1594 p.println(" mIsInputViewShown=" + mIsInputViewShown 1595 + " mStatusIcon=" + mStatusIcon); 1596 } 1597} 1598