InputMethodService.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
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.util.Log; 30import android.view.KeyEvent; 31import android.view.LayoutInflater; 32import android.view.MotionEvent; 33import android.view.View; 34import android.view.ViewGroup; 35import android.view.ViewTreeObserver; 36import android.view.inputmethod.CompletionInfo; 37import android.view.inputmethod.ExtractedText; 38import android.view.inputmethod.ExtractedTextRequest; 39import android.view.inputmethod.InputBinding; 40import android.view.inputmethod.InputConnection; 41import android.view.inputmethod.InputMethod; 42import android.view.inputmethod.InputMethodManager; 43import android.view.inputmethod.EditorInfo; 44import android.widget.FrameLayout; 45 46/** 47 * InputMethodService provides a standard implementation of an InputMethod, 48 * which final implementations can derive from and customize. See the 49 * base class {@link AbstractInputMethodService} and the {@link InputMethod} 50 * interface for more information on the basics of writing input methods. 51 */ 52public class InputMethodService extends AbstractInputMethodService { 53 static final String TAG = "InputMethodService"; 54 static final boolean DEBUG = false; 55 56 LayoutInflater mInflater; 57 View mRootView; 58 SoftInputWindow mWindow; 59 boolean mWindowCreated; 60 boolean mWindowAdded; 61 boolean mWindowVisible; 62 FrameLayout mExtractFrame; 63 FrameLayout mCandidatesFrame; 64 FrameLayout mInputFrame; 65 66 IBinder mToken; 67 68 InputBinding mInputBinding; 69 InputConnection mInputConnection; 70 boolean mInputStarted; 71 EditorInfo mInputInfo; 72 73 boolean mShowInputRequested; 74 boolean mShowCandidatesRequested; 75 76 boolean mFullscreenApplied; 77 boolean mIsFullscreen; 78 View mExtractView; 79 ExtractEditText mExtractEditText; 80 ExtractedText mExtractedText; 81 int mExtractedToken; 82 83 View mInputView; 84 boolean mIsInputViewShown; 85 86 int mStatusIcon; 87 88 final Insets mTmpInsets = new Insets(); 89 final int[] mTmpLocation = new int[2]; 90 91 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = 92 new ViewTreeObserver.OnComputeInternalInsetsListener() { 93 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 94 if (isFullscreenMode()) { 95 // In fullscreen mode, we just say the window isn't covering 96 // any content so we don't impact whatever is behind. 97 View decor = getWindow().getWindow().getDecorView(); 98 info.contentInsets.top = info.visibleInsets.top 99 = decor.getHeight(); 100 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 101 } else { 102 onComputeInsets(mTmpInsets); 103 info.contentInsets.top = mTmpInsets.contentTopInsets; 104 info.visibleInsets.top = mTmpInsets.visibleTopInsets; 105 info.setTouchableInsets(mTmpInsets.touchableInsets); 106 } 107 } 108 }; 109 110 /** 111 * Concrete implementation of 112 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides 113 * all of the standard behavior for an input method. 114 */ 115 public class InputMethodImpl extends AbstractInputMethodImpl { 116 /** 117 * Take care of attaching the given window token provided by the system. 118 */ 119 public void attachToken(IBinder token) { 120 if (mToken == null) { 121 mToken = token; 122 mWindow.setToken(token); 123 } 124 } 125 126 /** 127 * Handle a new input binding, calling 128 * {@link InputMethodService#onBindInput InputMethodService.onBindInput()} 129 * when done. 130 */ 131 public void bindInput(InputBinding binding) { 132 mInputBinding = binding; 133 mInputConnection = binding.getConnection(); 134 onBindInput(); 135 } 136 137 /** 138 * Clear the current input binding. 139 */ 140 public void unbindInput() { 141 mInputStarted = false; 142 mInputBinding = null; 143 mInputConnection = null; 144 } 145 146 public void startInput(EditorInfo attribute) { 147 doStartInput(attribute, false); 148 } 149 150 public void restartInput(EditorInfo attribute) { 151 doStartInput(attribute, false); 152 } 153 154 /** 155 * Handle a request by the system to hide the soft input area. 156 */ 157 public void hideSoftInput() { 158 if (DEBUG) Log.v(TAG, "hideSoftInput()"); 159 mShowInputRequested = false; 160 hideWindow(); 161 } 162 163 /** 164 * Handle a request by the system to show the soft input area. 165 */ 166 public void showSoftInput() { 167 if (DEBUG) Log.v(TAG, "showSoftInput()"); 168 showWindow(true); 169 } 170 } 171 172 /** 173 * Concrete implementation of 174 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides 175 * all of the standard behavior for an input method session. 176 */ 177 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl { 178 public void finishInput() { 179 if (!isEnabled()) { 180 return; 181 } 182 onFinishInput(); 183 mInputStarted = false; 184 } 185 186 /** 187 * Call {@link InputMethodService#onDisplayCompletions 188 * InputMethodService.onDisplayCompletions()}. 189 */ 190 public void displayCompletions(CompletionInfo[] completions) { 191 if (!isEnabled()) { 192 return; 193 } 194 onDisplayCompletions(completions); 195 } 196 197 /** 198 * Call {@link InputMethodService#onUpdateExtractedText 199 * InputMethodService.onUpdateExtractedText()}. 200 */ 201 public void updateExtractedText(int token, ExtractedText text) { 202 if (!isEnabled()) { 203 return; 204 } 205 onUpdateExtractedText(token, text); 206 } 207 208 /** 209 * Call {@link InputMethodService#onUpdateSelection 210 * InputMethodService.onUpdateSelection()}. 211 */ 212 public void updateSelection(int oldSelStart, int oldSelEnd, 213 int newSelStart, int newSelEnd) { 214 if (!isEnabled()) { 215 return; 216 } 217 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd, 218 newSelStart, newSelEnd); 219 } 220 221 /** 222 * Call {@link InputMethodService#onUpdateCursor 223 * InputMethodService.onUpdateCursor()}. 224 */ 225 public void updateCursor(Rect newCursor) { 226 if (!isEnabled()) { 227 return; 228 } 229 InputMethodService.this.onUpdateCursor(newCursor); 230 } 231 232 /** 233 * Call {@link InputMethodService#onAppPrivateCommand 234 * InputMethodService.onAppPrivateCommand()}. 235 */ 236 public void appPrivateCommand(String action, Bundle data) { 237 if (!isEnabled()) { 238 return; 239 } 240 InputMethodService.this.onAppPrivateCommand(action, data); 241 } 242 } 243 244 /** 245 * Information about where interesting parts of the input method UI appear. 246 */ 247 public static final class Insets { 248 /** 249 * This is the top part of the UI that is the main content. It is 250 * used to determine the basic space needed, to resize/pan the 251 * application behind. It is assumed that this inset does not 252 * change very much, since any change will cause a full resize/pan 253 * of the application behind. This value is relative to the top edge 254 * of the input method window. 255 */ 256 int contentTopInsets; 257 258 /** 259 * This is the top part of the UI that is visibly covering the 260 * application behind it. This provides finer-grained control over 261 * visibility, allowing you to change it relatively frequently (such 262 * as hiding or showing candidates) without disrupting the underlying 263 * UI too much. For example, this will never resize the application 264 * UI, will only pan if needed to make the current focus visible, and 265 * will not aggressively move the pan position when this changes unless 266 * needed to make the focus visible. This value is relative to the top edge 267 * of the input method window. 268 */ 269 int visibleTopInsets; 270 271 /** 272 * Option for {@link #touchableInsets}: the entire window frame 273 * can be touched. 274 */ 275 public static final int TOUCHABLE_INSETS_FRAME 276 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; 277 278 /** 279 * Option for {@link #touchableInsets}: the area inside of 280 * the content insets can be touched. 281 */ 282 public static final int TOUCHABLE_INSETS_CONTENT 283 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; 284 285 /** 286 * Option for {@link #touchableInsets}: the area inside of 287 * the visible insets can be touched. 288 */ 289 public static final int TOUCHABLE_INSETS_VISIBLE 290 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE; 291 292 /** 293 * Determine which area of the window is touchable by the user. May 294 * be one of: {@link #TOUCHABLE_INSETS_FRAME}, 295 * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_VISIBLE}. 296 */ 297 public int touchableInsets; 298 } 299 300 @Override public void onCreate() { 301 super.onCreate(); 302 mInflater = (LayoutInflater)getSystemService( 303 Context.LAYOUT_INFLATER_SERVICE); 304 mWindow = new SoftInputWindow(this); 305 initViews(); 306 } 307 308 void initViews() { 309 mWindowVisible = false; 310 mWindowCreated = false; 311 mShowInputRequested = false; 312 mShowCandidatesRequested = false; 313 314 mRootView = mInflater.inflate( 315 com.android.internal.R.layout.input_method, null); 316 mWindow.setContentView(mRootView); 317 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 318 319 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea); 320 mExtractView = null; 321 mExtractEditText = null; 322 mFullscreenApplied = false; 323 324 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea); 325 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea); 326 mInputView = null; 327 mIsInputViewShown = false; 328 329 mExtractFrame.setVisibility(View.GONE); 330 mCandidatesFrame.setVisibility(View.GONE); 331 mInputFrame.setVisibility(View.GONE); 332 } 333 334 @Override public void onDestroy() { 335 super.onDestroy(); 336 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 337 mInsetsComputer); 338 if (mWindowAdded) { 339 mWindow.dismiss(); 340 } 341 } 342 343 /** 344 * Implement to return our standard {@link InputMethodImpl}. Subclasses 345 * can override to provide their own customized version. 346 */ 347 public AbstractInputMethodImpl onCreateInputMethodInterface() { 348 return new InputMethodImpl(); 349 } 350 351 /** 352 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses 353 * can override to provide their own customized version. 354 */ 355 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() { 356 return new InputMethodSessionImpl(); 357 } 358 359 public LayoutInflater getLayoutInflater() { 360 return mInflater; 361 } 362 363 public Dialog getWindow() { 364 return mWindow; 365 } 366 367 /** 368 * Return the currently active InputBinding for the input method, or 369 * null if there is none. 370 */ 371 public InputBinding getCurrentInputBinding() { 372 return mInputBinding; 373 } 374 375 /** 376 * Retrieve the currently active InputConnection that is bound to 377 * the input method, or null if there is none. 378 */ 379 public InputConnection getCurrentInputConnection() { 380 return mInputConnection; 381 } 382 383 public boolean getCurrentInputStarted() { 384 return mInputStarted; 385 } 386 387 public EditorInfo getCurrentInputInfo() { 388 return mInputInfo; 389 } 390 391 /** 392 * Re-evaluate whether the input method should be running in fullscreen 393 * mode, and update its UI if this has changed since the last time it 394 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to 395 * determine whether it should currently run in fullscreen mode. You 396 * can use {@link #isFullscreenMode()} to determine if the input method 397 * is currently running in fullscreen mode. 398 */ 399 public void updateFullscreenMode() { 400 boolean isFullscreen = onEvaluateFullscreenMode(); 401 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { 402 mIsFullscreen = isFullscreen; 403 mFullscreenApplied = true; 404 mWindow.getWindow().setBackgroundDrawable( 405 onCreateBackgroundDrawable()); 406 mExtractFrame.setVisibility(isFullscreen ? View.VISIBLE : View.GONE); 407 if (isFullscreen) { 408 if (mExtractView == null) { 409 View v = onCreateExtractTextView(); 410 if (v != null) { 411 setExtractView(v); 412 } 413 } 414 startExtractingText(); 415 mWindow.getWindow().setLayout(FILL_PARENT, FILL_PARENT); 416 } else { 417 mWindow.getWindow().setLayout(WRAP_CONTENT, WRAP_CONTENT); 418 } 419 } 420 } 421 422 /** 423 * Return whether the input method is <em>currently</em> running in 424 * fullscreen mode. This is the mode that was last determined and 425 * applied by {@link #updateFullscreenMode()}. 426 */ 427 public boolean isFullscreenMode() { 428 return mIsFullscreen; 429 } 430 431 /** 432 * Override this to control when the input method should run in 433 * fullscreen mode. The default implementation runs in fullsceen only 434 * when the screen is in landscape mode and the input view is being 435 * shown ({@link #onEvaluateInputViewShown} returns true). If you change what 436 * this returns, you will need to call {@link #updateFullscreenMode()} 437 * yourself whenever the returned value may have changed to have it 438 * re-evaluated and applied. 439 */ 440 public boolean onEvaluateFullscreenMode() { 441 Configuration config = getResources().getConfiguration(); 442 return config.orientation == Configuration.ORIENTATION_LANDSCAPE 443 && onEvaluateInputViewShown(); 444 } 445 446 /** 447 * Compute the interesting insets into your UI. The default implementation 448 * uses the top of the candidates frame for the visible insets, and the 449 * top of the input frame for the content insets. The default touchable 450 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}. 451 * 452 * <p>Note that this method is not called when in fullscreen mode, since 453 * in that case the application is left as-is behind the input method and 454 * not impacted by anything in its UI. 455 * 456 * @param outInsets Fill in with the current UI insets. 457 */ 458 public void onComputeInsets(Insets outInsets) { 459 int[] loc = mTmpLocation; 460 if (mInputFrame.getVisibility() == View.VISIBLE) { 461 mInputFrame.getLocationInWindow(loc); 462 outInsets.contentTopInsets = loc[1]; 463 } 464 if (mCandidatesFrame.getVisibility() == View.VISIBLE) { 465 mCandidatesFrame.getLocationInWindow(loc); 466 outInsets.visibleTopInsets = loc[1]; 467 } else { 468 outInsets.visibleTopInsets = loc[1]; 469 } 470 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE; 471 } 472 473 /** 474 * Re-evaluate whether the soft input area should currently be shown, and 475 * update its UI if this has changed since the last time it 476 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to 477 * determine whether the input view should currently be shown. You 478 * can use {@link #isInputViewShown()} to determine if the input view 479 * is currently shown. 480 */ 481 public void updateInputViewShown() { 482 boolean isShown = onEvaluateInputViewShown(); 483 if (mIsInputViewShown != isShown && mWindowVisible) { 484 mIsInputViewShown = isShown; 485 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE); 486 if (mInputView == null) { 487 View v = onCreateInputView(); 488 if (v != null) { 489 setInputView(v); 490 } 491 } 492 } 493 } 494 495 /** 496 * Return whether the soft input view is <em>currently</em> shown to the 497 * user. This is the state that was last determined and 498 * applied by {@link #updateInputViewShown()}. 499 */ 500 public boolean isInputViewShown() { 501 return mIsInputViewShown; 502 } 503 504 /** 505 * Override this to control when the soft input area should be shown to 506 * the user. The default implementation only shows the input view when 507 * there is no hard keyboard or the keyboard is hidden. If you change what 508 * this returns, you will need to call {@link #updateInputViewShown()} 509 * yourself whenever the returned value may have changed to have it 510 * re-evalauted and applied. 511 */ 512 public boolean onEvaluateInputViewShown() { 513 Configuration config = getResources().getConfiguration(); 514 return config.keyboard == Configuration.KEYBOARD_NOKEYS 515 || config.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES; 516 } 517 518 /** 519 * Controls the visibility of the candidates display area. By default 520 * it is hidden. 521 */ 522 public void setCandidatesViewShown(boolean shown) { 523 if (mShowCandidatesRequested != shown) { 524 mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE); 525 if (!mShowInputRequested) { 526 // If we are being asked to show the candidates view while the app 527 // has not asked for the input view to be shown, then we need 528 // to update whether the window is shown. 529 if (shown) { 530 showWindow(false); 531 } else { 532 hideWindow(); 533 } 534 } 535 mShowCandidatesRequested = shown; 536 } 537 } 538 539 public void setStatusIcon(int iconResId) { 540 mStatusIcon = iconResId; 541 if (mInputConnection != null && mWindowVisible) { 542 mInputConnection.showStatusIcon(getPackageName(), iconResId); 543 } 544 } 545 546 /** 547 * Force switch to a new input method, as identified by <var>id</var>. This 548 * input method will be destroyed, and the requested one started on the 549 * current input field. 550 * 551 * @param id Unique identifier of the new input method ot start. 552 */ 553 public void switchInputMethod(String id) { 554 ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) 555 .setInputMethod(mToken, id); 556 } 557 558 public void setExtractView(View view) { 559 mExtractFrame.removeAllViews(); 560 mExtractFrame.addView(view, new FrameLayout.LayoutParams( 561 ViewGroup.LayoutParams.FILL_PARENT, 562 ViewGroup.LayoutParams.WRAP_CONTENT)); 563 mExtractView = view; 564 if (view != null) { 565 mExtractEditText = (ExtractEditText)view.findViewById( 566 com.android.internal.R.id.inputExtractEditText); 567 startExtractingText(); 568 } else { 569 mExtractEditText = null; 570 } 571 } 572 573 /** 574 * Replaces the current candidates view with a new one. You only need to 575 * call this when dynamically changing the view; normally, you should 576 * implement {@link #onCreateCandidatesView()} and create your view when 577 * first needed by the input method. 578 */ 579 public void setCandidatesView(View view) { 580 mCandidatesFrame.removeAllViews(); 581 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams( 582 ViewGroup.LayoutParams.FILL_PARENT, 583 ViewGroup.LayoutParams.WRAP_CONTENT)); 584 } 585 586 /** 587 * Replaces the current input view with a new one. You only need to 588 * call this when dynamically changing the view; normally, you should 589 * implement {@link #onCreateInputView()} and create your view when 590 * first needed by the input method. 591 */ 592 public void setInputView(View view) { 593 mInputFrame.removeAllViews(); 594 mInputFrame.addView(view, new FrameLayout.LayoutParams( 595 ViewGroup.LayoutParams.FILL_PARENT, 596 ViewGroup.LayoutParams.WRAP_CONTENT)); 597 mInputView = view; 598 } 599 600 /** 601 * Called by the framework to create a Drawable for the background of 602 * the input method window. May return null for no background. The default 603 * implementation returns a non-null standard background only when in 604 * fullscreen mode. 605 */ 606 public Drawable onCreateBackgroundDrawable() { 607 if (isFullscreenMode()) { 608 return getResources().getDrawable( 609 com.android.internal.R.drawable.input_method_fullscreen_background); 610 } 611 return null; 612 } 613 614 /** 615 * Called by the framework to create the layout for showing extacted text. 616 * Only called when in fullscreen mode. The returned view hierarchy must 617 * have an {@link ExtractEditText} whose ID is 618 * {@link android.R.id#inputExtractEditText}. 619 */ 620 public View onCreateExtractTextView() { 621 return mInflater.inflate( 622 com.android.internal.R.layout.input_method_extract_view, null); 623 } 624 625 /** 626 * Create and return the view hierarchy used to show candidates. This will 627 * be called once, when the candidates are first displayed. You can return 628 * null to have no candidates view; the default implementation returns null. 629 * 630 * <p>To control when the candidates view is displayed, use 631 * {@link #setCandidatesViewShown(boolean)}. 632 * To change the candidates view after the first one is created by this 633 * function, use {@link #setCandidatesView(View)}. 634 */ 635 public View onCreateCandidatesView() { 636 return null; 637 } 638 639 /** 640 * Create and return the view hierarchy used for the input area (such as 641 * a soft keyboard). This will be called once, when the input area is 642 * first displayed. You can return null to have no input area; the default 643 * implementation returns null. 644 * 645 * <p>To control when the input view is displayed, implement 646 * {@link #onEvaluateInputViewShown()}. 647 * To change the input view after the first one is created by this 648 * function, use {@link #setInputView(View)}. 649 */ 650 public View onCreateInputView() { 651 return null; 652 } 653 654 /** 655 * Called when an input session is starting or restarting. 656 * 657 * @param info Description of the type of text being edited. 658 * @param restarting Set to true if we are restarting input on the 659 * same text field as before. 660 */ 661 public void onStartInputView(EditorInfo info, boolean restarting) { 662 } 663 664 @Override 665 public void onConfigurationChanged(Configuration newConfig) { 666 super.onConfigurationChanged(newConfig); 667 668 boolean visible = mWindowVisible; 669 boolean showingInput = mShowInputRequested; 670 boolean showingCandidates = mShowCandidatesRequested; 671 initViews(); 672 if (visible) { 673 if (showingCandidates) { 674 setCandidatesViewShown(true); 675 } 676 showWindow(showingInput); 677 } 678 } 679 680 public void showWindow(boolean showInput) { 681 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput 682 + " mShowInputRequested=" + mShowInputRequested 683 + " mWindowAdded=" + mWindowAdded 684 + " mWindowCreated=" + mWindowCreated 685 + " mWindowVisible=" + mWindowVisible 686 + " mInputStarted=" + mInputStarted); 687 boolean doShowInput = false; 688 boolean wasVisible = mWindowVisible; 689 mWindowVisible = true; 690 if (!mShowInputRequested) { 691 doShowInput = true; 692 mShowInputRequested = true; 693 } else { 694 showInput = true; 695 } 696 697 if (doShowInput) { 698 if (DEBUG) Log.v(TAG, "showWindow: updating UI"); 699 updateFullscreenMode(); 700 updateInputViewShown(); 701 } 702 703 if (!mWindowAdded || !mWindowCreated) { 704 mWindowAdded = true; 705 mWindowCreated = true; 706 View v = onCreateCandidatesView(); 707 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v); 708 if (v != null) { 709 setCandidatesView(v); 710 } 711 } 712 if (doShowInput) { 713 if (mInputStarted) { 714 if (DEBUG) Log.v(TAG, "showWindow: starting input view"); 715 onStartInputView(mInputInfo, false); 716 } 717 startExtractingText(); 718 } 719 720 if (!wasVisible) { 721 if (DEBUG) Log.v(TAG, "showWindow: showing!"); 722 mWindow.show(); 723 if (mInputConnection != null) { 724 mInputConnection.showStatusIcon(getPackageName(), mStatusIcon); 725 } 726 } 727 } 728 729 public void hideWindow() { 730 if (mWindowVisible) { 731 mWindow.hide(); 732 mWindowVisible = false; 733 if (mInputConnection != null) { 734 mInputConnection.hideStatusIcon(); 735 } 736 } 737 } 738 739 public void onBindInput() { 740 } 741 742 public void onStartInput(EditorInfo attribute, boolean restarting) { 743 } 744 745 void doStartInput(EditorInfo attribute, boolean restarting) { 746 mInputStarted = true; 747 mInputInfo = attribute; 748 onStartInput(attribute, restarting); 749 if (mWindowVisible) { 750 if (mWindowCreated) { 751 onStartInputView(mInputInfo, restarting); 752 } 753 startExtractingText(); 754 } 755 } 756 757 public void onFinishInput() { 758 } 759 760 /** 761 * Called when the application has reported auto-completion candidates that 762 * it would like to have the input method displayed. Typically these are 763 * only used when an input method is running in full-screen mode, since 764 * otherwise the user can see and interact with the pop-up window of 765 * completions shown by the application. 766 * 767 * <p>The default implementation here does nothing. 768 */ 769 public void onDisplayCompletions(CompletionInfo[] completions) { 770 } 771 772 /** 773 * Called when the application has reported new extracted text to be shown 774 * due to changes in its current text state. The default implementation 775 * here places the new text in the extract edit text, when the input 776 * method is running in fullscreen mode. 777 */ 778 public void onUpdateExtractedText(int token, ExtractedText text) { 779 if (mExtractedToken != token) { 780 return; 781 } 782 if (mExtractEditText != null && text != null) { 783 mExtractedText = text; 784 mExtractEditText.setExtractedText(text); 785 } 786 } 787 788 /** 789 * Called when the application has reported a new selection region of 790 * the text. This is called whether or not the input method has requested 791 * extracted text updates, although if so it will not receive this call 792 * if the extracted text has changed as well. 793 * 794 * <p>The default implementation takes care of updating the cursor in 795 * the extract text, if it is being shown. 796 */ 797 public void onUpdateSelection(int oldSelStart, int oldSelEnd, 798 int newSelStart, int newSelEnd) { 799 if (mExtractEditText != null && mExtractedText != null) { 800 final int off = mExtractedText.startOffset; 801 mExtractEditText.setSelection(newSelStart-off, newSelEnd-off); 802 } 803 } 804 805 /** 806 * Called when the application has reported a new location of its text 807 * cursor. This is only called if explicitly requested by the input method. 808 * The default implementation does nothing. 809 */ 810 public void onUpdateCursor(Rect newCursor) { 811 } 812 813 /** 814 * Close this input method's soft input area, removing it from the display. 815 * The input method will continue running, but the user can no longer use 816 * it to generate input by touching the screen. 817 */ 818 public void dismissSoftInput() { 819 ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) 820 .hideSoftInputFromInputMethod(mToken); 821 } 822 823 public boolean onKeyDown(int keyCode, KeyEvent event) { 824 if (mWindowVisible && event.getKeyCode() == KeyEvent.KEYCODE_BACK 825 && event.getRepeatCount() == 0) { 826 dismissSoftInput(); 827 return true; 828 } 829 return false; 830 } 831 832 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 833 return false; 834 } 835 836 public boolean onKeyUp(int keyCode, KeyEvent event) { 837 return false; 838 } 839 840 public boolean onTrackballEvent(MotionEvent event) { 841 return false; 842 } 843 844 public void onAppPrivateCommand(String action, Bundle data) { 845 } 846 847 void startExtractingText() { 848 if (mExtractEditText != null && getCurrentInputStarted() 849 && isFullscreenMode()) { 850 mExtractedToken++; 851 ExtractedTextRequest req = new ExtractedTextRequest(); 852 req.token = mExtractedToken; 853 req.hintMaxLines = 10; 854 req.hintMaxChars = 10000; 855 mExtractedText = mInputConnection.getExtractedText(req, 856 InputConnection.EXTRACTED_TEXT_MONITOR); 857 if (mExtractedText != null) { 858 mExtractEditText.setExtractedText(mExtractedText); 859 } 860 mExtractEditText.setInputType(getCurrentInputInfo().inputType); 861 mExtractEditText.setHint(mInputInfo.hintText); 862 } 863 } 864} 865