InputMethodService.java revision 9266c558bf1d21ff647525ff99f7dadbca417309
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 * 52 * <p>An input method has significant discretion in how it goes about its 53 * work: the {@link android.inputmethodservice.InputMethodService} provides 54 * a basic framework for standard UI elements (input view, candidates view, 55 * and running in fullscreen mode), but it is up to a particular implementor 56 * to decide how to use them. For example, one input method could implement 57 * an input area with a keyboard, another could allow the user to draw text, 58 * while a third could have no input area (and thus not be visible to the 59 * user) but instead listen to audio and perform text to speech conversion.</p> 60 * 61 * <p>In the implementation provided here, all of these elements are placed 62 * together in a single window managed by the InputMethodService. It will 63 * execute callbacks as it needs information about them, and provides APIs for 64 * programmatic control over them. They layout of these elements is explicitly 65 * defined:</p> 66 * 67 * <ul> 68 * <li>The soft input view, if available, is placed at the bottom of the 69 * screen. 70 * <li>The candidates view, if currently shown, is placed above the soft 71 * input view. 72 * <li>If not running fullscreen, the application is moved or resized to be 73 * above these views; if running fullscreen, the window will completely cover 74 * the application and its top part will contain the extract text of what is 75 * currently being edited by the application. 76 * </ul> 77 * 78 * 79 * <a name="SoftInputView"></a> 80 * <h3>Soft Input View</h3> 81 * 82 * <p>Central to most input methods is the soft input view. This is where most 83 * user interaction occurs: pressing on soft keys, drawing characters, or 84 * however else your input method wants to generate text. Most implementations 85 * will simply have their own view doing all of this work, and return a new 86 * instance of it when {@link #onCreateInputView()} is called. At that point, 87 * as long as the input view is visible, you will see user interaction in 88 * that view and can call back on the InputMethodService to interact with the 89 * application as appropriate.</p> 90 * 91 * <p>There are some situations where you want to decide whether or not your 92 * soft input view should be shown to the user. This is done by implementing 93 * the {@link #onEvaluateInputViewShown()} to return true or false based on 94 * whether it should be shown in the current environment. If any of your 95 * state has changed that may impact this, call 96 * {@link #updateInputViewShown()} to have it re-evaluated. The default 97 * implementation always shows the input view unless there is a hard 98 * keyboard available, which is the appropriate behavior for most input 99 * methods.</p> 100 * 101 * 102 * <a name="CandidatesView"></a> 103 * <h3>Candidates View</h3> 104 * 105 * <p>Often while the user is generating raw text, an input method wants to 106 * provide them with a list of possible interpretations of that text that can 107 * be selected for use. This is accomplished with the candidates view, and 108 * like the soft input view you implement {@link #onCreateCandidatesView()} 109 * to instantiate your own view implementing your candidates UI.</p> 110 * 111 * <p>Management of the candidates view is a little different than the input 112 * view, because the candidates view tends to be more transient, being shown 113 * only when there are possible candidates for the current text being entered 114 * by the user. To control whether the candidates view is shown, you use 115 * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate 116 * view tends to be shown and hidden a lot, it does not impact the application 117 * UI in the same way as the soft input view: it will never cause application 118 * windows to resize, only cause them to be panned if needed for the user to 119 * see the current focus.</p> 120 * 121 * 122 * <a name="FullscreenMode"></a> 123 * <h3>Fullscreen Mode</h3> 124 * 125 * <p>Sometimes your input method UI is too large to integrate with the 126 * application UI, so you just want to take over the screen. This is 127 * accomplished by switching to full-screen mode, causing the input method 128 * window to fill the entire screen and add its own "extracted text" editor 129 * showing the user the text that is being typed. Unlike the other UI elements, 130 * there is a standard implementation for the extract editor that you should 131 * not need to change. The editor is placed at the top of the IME, above the 132 * input and candidates views.</p> 133 * 134 * <p>Similar to the input view, you control whether the IME is running in 135 * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()} 136 * to return true or false based on 137 * whether it should be fullscreen in the current environment. If any of your 138 * state has changed that may impact this, call 139 * {@link #updateFullscreenMode()} to have it re-evaluated. The default 140 * implementation selects fullscreen mode when the screen is in a landscape 141 * orientation, which is appropriate behavior for most input methods that have 142 * a significant input area.</p> 143 * 144 * <p>When in fullscreen mode, you have some special requirements because the 145 * user can not see the application UI. In particular, you should implement 146 * {@link #onDisplayCompletions(CompletionInfo[])} to show completions 147 * generated by your application, typically in your candidates view like you 148 * would normally show candidates. 149 * 150 * 151 * <a name="GeneratingText"></a> 152 * <h3>Generating Text</h3> 153 * 154 * <p>The key part of an IME is of course generating text for the application. 155 * This is done through calls to the 156 * {@link android.view.inputmethod.InputConnection} interface to the 157 * application, which can be retrieved from {@link #getCurrentInputConnection()}. 158 * This interface allows you to generate raw key events or, if the target 159 * supports it, directly edit in strings of candidates and committed text.</p> 160 * 161 * <p>Information about what the target is expected and supports can be found 162 * through the {@link android.view.inputmethod.EditorInfo} class, which is 163 * retrieved with {@link #getCurrentInputEditorInfo()} method. The most 164 * important part of this is {@link android.view.inputmethod.EditorInfo#inputType 165 * EditorInfo.inputType}; in particular, if this is 166 * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL}, 167 * then the target does not support complex edits and you need to only deliver 168 * raw key events to it. An input method will also want to look at other 169 * values here, to for example detect password mode, auto complete text views, 170 * phone number entry, etc.</p> 171 * 172 * <p>When the user switches between input targets, you will receive calls to 173 * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}. 174 * You can use these to reset and initialize your input state for the current 175 * target. For example, you will often want to clear any input state, and 176 * update a soft keyboard to be appropriate for the new inputType.</p> 177 */ 178public class InputMethodService extends AbstractInputMethodService { 179 static final String TAG = "InputMethodService"; 180 static final boolean DEBUG = false; 181 182 LayoutInflater mInflater; 183 View mRootView; 184 SoftInputWindow mWindow; 185 boolean mWindowCreated; 186 boolean mWindowAdded; 187 boolean mWindowVisible; 188 FrameLayout mExtractFrame; 189 FrameLayout mCandidatesFrame; 190 FrameLayout mInputFrame; 191 192 IBinder mToken; 193 194 InputBinding mInputBinding; 195 InputConnection mInputConnection; 196 boolean mInputStarted; 197 EditorInfo mInputEditorInfo; 198 199 boolean mShowInputRequested; 200 boolean mShowCandidatesRequested; 201 202 boolean mFullscreenApplied; 203 boolean mIsFullscreen; 204 View mExtractView; 205 ExtractEditText mExtractEditText; 206 ExtractedText mExtractedText; 207 int mExtractedToken; 208 209 View mInputView; 210 boolean mIsInputViewShown; 211 212 int mStatusIcon; 213 214 final Insets mTmpInsets = new Insets(); 215 final int[] mTmpLocation = new int[2]; 216 217 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = 218 new ViewTreeObserver.OnComputeInternalInsetsListener() { 219 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 220 if (isFullscreenMode()) { 221 // In fullscreen mode, we just say the window isn't covering 222 // any content so we don't impact whatever is behind. 223 View decor = getWindow().getWindow().getDecorView(); 224 info.contentInsets.top = info.visibleInsets.top 225 = decor.getHeight(); 226 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 227 } else { 228 onComputeInsets(mTmpInsets); 229 info.contentInsets.top = mTmpInsets.contentTopInsets; 230 info.visibleInsets.top = mTmpInsets.visibleTopInsets; 231 info.setTouchableInsets(mTmpInsets.touchableInsets); 232 } 233 } 234 }; 235 236 /** 237 * Concrete implementation of 238 * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides 239 * all of the standard behavior for an input method. 240 */ 241 public class InputMethodImpl extends AbstractInputMethodImpl { 242 /** 243 * Take care of attaching the given window token provided by the system. 244 */ 245 public void attachToken(IBinder token) { 246 if (mToken == null) { 247 mToken = token; 248 mWindow.setToken(token); 249 } 250 } 251 252 /** 253 * Handle a new input binding, calling 254 * {@link InputMethodService#onBindInput InputMethodService.onBindInput()} 255 * when done. 256 */ 257 public void bindInput(InputBinding binding) { 258 mInputBinding = binding; 259 mInputConnection = binding.getConnection(); 260 onBindInput(); 261 } 262 263 /** 264 * Clear the current input binding. 265 */ 266 public void unbindInput() { 267 mInputStarted = false; 268 mInputBinding = null; 269 mInputConnection = null; 270 } 271 272 public void startInput(EditorInfo attribute) { 273 doStartInput(attribute, false); 274 } 275 276 public void restartInput(EditorInfo attribute) { 277 doStartInput(attribute, false); 278 } 279 280 /** 281 * Handle a request by the system to hide the soft input area. 282 */ 283 public void hideSoftInput() { 284 if (DEBUG) Log.v(TAG, "hideSoftInput()"); 285 mShowInputRequested = false; 286 hideWindow(); 287 } 288 289 /** 290 * Handle a request by the system to show the soft input area. 291 */ 292 public void showSoftInput(int flags) { 293 if (DEBUG) Log.v(TAG, "showSoftInput()"); 294 onShowRequested(flags); 295 } 296 } 297 298 /** 299 * Concrete implementation of 300 * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides 301 * all of the standard behavior for an input method session. 302 */ 303 public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl { 304 public void finishInput() { 305 if (!isEnabled()) { 306 return; 307 } 308 onFinishInput(); 309 mInputStarted = false; 310 } 311 312 /** 313 * Call {@link InputMethodService#onDisplayCompletions 314 * InputMethodService.onDisplayCompletions()}. 315 */ 316 public void displayCompletions(CompletionInfo[] completions) { 317 if (!isEnabled()) { 318 return; 319 } 320 onDisplayCompletions(completions); 321 } 322 323 /** 324 * Call {@link InputMethodService#onUpdateExtractedText 325 * InputMethodService.onUpdateExtractedText()}. 326 */ 327 public void updateExtractedText(int token, ExtractedText text) { 328 if (!isEnabled()) { 329 return; 330 } 331 onUpdateExtractedText(token, text); 332 } 333 334 /** 335 * Call {@link InputMethodService#onUpdateSelection 336 * InputMethodService.onUpdateSelection()}. 337 */ 338 public void updateSelection(int oldSelStart, int oldSelEnd, 339 int newSelStart, int newSelEnd, 340 int candidatesStart, int candidatesEnd) { 341 if (!isEnabled()) { 342 return; 343 } 344 InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd, 345 newSelStart, newSelEnd, candidatesStart, candidatesEnd); 346 } 347 348 /** 349 * Call {@link InputMethodService#onUpdateCursor 350 * InputMethodService.onUpdateCursor()}. 351 */ 352 public void updateCursor(Rect newCursor) { 353 if (!isEnabled()) { 354 return; 355 } 356 InputMethodService.this.onUpdateCursor(newCursor); 357 } 358 359 /** 360 * Call {@link InputMethodService#onAppPrivateCommand 361 * InputMethodService.onAppPrivateCommand()}. 362 */ 363 public void appPrivateCommand(String action, Bundle data) { 364 if (!isEnabled()) { 365 return; 366 } 367 InputMethodService.this.onAppPrivateCommand(action, data); 368 } 369 } 370 371 /** 372 * Information about where interesting parts of the input method UI appear. 373 */ 374 public static final class Insets { 375 /** 376 * This is the top part of the UI that is the main content. It is 377 * used to determine the basic space needed, to resize/pan the 378 * application behind. It is assumed that this inset does not 379 * change very much, since any change will cause a full resize/pan 380 * of the application behind. This value is relative to the top edge 381 * of the input method window. 382 */ 383 int contentTopInsets; 384 385 /** 386 * This is the top part of the UI that is visibly covering the 387 * application behind it. This provides finer-grained control over 388 * visibility, allowing you to change it relatively frequently (such 389 * as hiding or showing candidates) without disrupting the underlying 390 * UI too much. For example, this will never resize the application 391 * UI, will only pan if needed to make the current focus visible, and 392 * will not aggressively move the pan position when this changes unless 393 * needed to make the focus visible. This value is relative to the top edge 394 * of the input method window. 395 */ 396 int visibleTopInsets; 397 398 /** 399 * Option for {@link #touchableInsets}: the entire window frame 400 * can be touched. 401 */ 402 public static final int TOUCHABLE_INSETS_FRAME 403 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; 404 405 /** 406 * Option for {@link #touchableInsets}: the area inside of 407 * the content insets can be touched. 408 */ 409 public static final int TOUCHABLE_INSETS_CONTENT 410 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; 411 412 /** 413 * Option for {@link #touchableInsets}: the area inside of 414 * the visible insets can be touched. 415 */ 416 public static final int TOUCHABLE_INSETS_VISIBLE 417 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE; 418 419 /** 420 * Determine which area of the window is touchable by the user. May 421 * be one of: {@link #TOUCHABLE_INSETS_FRAME}, 422 * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_VISIBLE}. 423 */ 424 public int touchableInsets; 425 } 426 427 @Override public void onCreate() { 428 super.onCreate(); 429 mInflater = (LayoutInflater)getSystemService( 430 Context.LAYOUT_INFLATER_SERVICE); 431 mWindow = new SoftInputWindow(this); 432 initViews(); 433 mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); 434 } 435 436 void initViews() { 437 mWindowVisible = false; 438 mWindowCreated = false; 439 mShowInputRequested = false; 440 mShowCandidatesRequested = false; 441 442 mRootView = mInflater.inflate( 443 com.android.internal.R.layout.input_method, null); 444 mWindow.setContentView(mRootView); 445 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 446 447 mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea); 448 mExtractView = null; 449 mExtractEditText = null; 450 mFullscreenApplied = false; 451 452 mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea); 453 mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea); 454 mInputView = null; 455 mIsInputViewShown = false; 456 457 mExtractFrame.setVisibility(View.GONE); 458 mCandidatesFrame.setVisibility(View.GONE); 459 mInputFrame.setVisibility(View.GONE); 460 } 461 462 @Override public void onDestroy() { 463 super.onDestroy(); 464 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 465 mInsetsComputer); 466 if (mWindowAdded) { 467 mWindow.dismiss(); 468 } 469 } 470 471 /** 472 * Implement to return our standard {@link InputMethodImpl}. Subclasses 473 * can override to provide their own customized version. 474 */ 475 public AbstractInputMethodImpl onCreateInputMethodInterface() { 476 return new InputMethodImpl(); 477 } 478 479 /** 480 * Implement to return our standard {@link InputMethodSessionImpl}. Subclasses 481 * can override to provide their own customized version. 482 */ 483 public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() { 484 return new InputMethodSessionImpl(); 485 } 486 487 public LayoutInflater getLayoutInflater() { 488 return mInflater; 489 } 490 491 public Dialog getWindow() { 492 return mWindow; 493 } 494 495 /** 496 * Return the currently active InputBinding for the input method, or 497 * null if there is none. 498 */ 499 public InputBinding getCurrentInputBinding() { 500 return mInputBinding; 501 } 502 503 /** 504 * Retrieve the currently active InputConnection that is bound to 505 * the input method, or null if there is none. 506 */ 507 public InputConnection getCurrentInputConnection() { 508 return mInputConnection; 509 } 510 511 public boolean getCurrentInputStarted() { 512 return mInputStarted; 513 } 514 515 public EditorInfo getCurrentInputEditorInfo() { 516 return mInputEditorInfo; 517 } 518 519 /** 520 * Re-evaluate whether the input method should be running in fullscreen 521 * mode, and update its UI if this has changed since the last time it 522 * was evaluated. This will call {@link #onEvaluateFullscreenMode()} to 523 * determine whether it should currently run in fullscreen mode. You 524 * can use {@link #isFullscreenMode()} to determine if the input method 525 * is currently running in fullscreen mode. 526 */ 527 public void updateFullscreenMode() { 528 boolean isFullscreen = onEvaluateFullscreenMode(); 529 if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { 530 mIsFullscreen = isFullscreen; 531 mFullscreenApplied = true; 532 mWindow.getWindow().setBackgroundDrawable( 533 onCreateBackgroundDrawable()); 534 mExtractFrame.setVisibility(isFullscreen ? View.VISIBLE : View.GONE); 535 if (isFullscreen) { 536 if (mExtractView == null) { 537 View v = onCreateExtractTextView(); 538 if (v != null) { 539 setExtractView(v); 540 } 541 } 542 startExtractingText(); 543 mWindow.getWindow().setLayout(FILL_PARENT, FILL_PARENT); 544 } else { 545 mWindow.getWindow().setLayout(WRAP_CONTENT, WRAP_CONTENT); 546 } 547 } 548 } 549 550 /** 551 * Return whether the input method is <em>currently</em> running in 552 * fullscreen mode. This is the mode that was last determined and 553 * applied by {@link #updateFullscreenMode()}. 554 */ 555 public boolean isFullscreenMode() { 556 return mIsFullscreen; 557 } 558 559 /** 560 * Override this to control when the input method should run in 561 * fullscreen mode. The default implementation runs in fullsceen only 562 * when the screen is in landscape mode and the input view is being 563 * shown ({@link #onEvaluateInputViewShown} returns true). If you change what 564 * this returns, you will need to call {@link #updateFullscreenMode()} 565 * yourself whenever the returned value may have changed to have it 566 * re-evaluated and applied. 567 */ 568 public boolean onEvaluateFullscreenMode() { 569 Configuration config = getResources().getConfiguration(); 570 return config.orientation == Configuration.ORIENTATION_LANDSCAPE 571 && onEvaluateInputViewShown(); 572 } 573 574 /** 575 * Compute the interesting insets into your UI. The default implementation 576 * uses the top of the candidates frame for the visible insets, and the 577 * top of the input frame for the content insets. The default touchable 578 * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}. 579 * 580 * <p>Note that this method is not called when in fullscreen mode, since 581 * in that case the application is left as-is behind the input method and 582 * not impacted by anything in its UI. 583 * 584 * @param outInsets Fill in with the current UI insets. 585 */ 586 public void onComputeInsets(Insets outInsets) { 587 int[] loc = mTmpLocation; 588 if (mInputFrame.getVisibility() == View.VISIBLE) { 589 mInputFrame.getLocationInWindow(loc); 590 } else { 591 loc[1] = 0; 592 } 593 outInsets.contentTopInsets = loc[1]; 594 if (mCandidatesFrame.getVisibility() == View.VISIBLE) { 595 mCandidatesFrame.getLocationInWindow(loc); 596 } 597 outInsets.visibleTopInsets = loc[1]; 598 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE; 599 } 600 601 /** 602 * Re-evaluate whether the soft input area should currently be shown, and 603 * update its UI if this has changed since the last time it 604 * was evaluated. This will call {@link #onEvaluateInputViewShown()} to 605 * determine whether the input view should currently be shown. You 606 * can use {@link #isInputViewShown()} to determine if the input view 607 * is currently shown. 608 */ 609 public void updateInputViewShown() { 610 boolean isShown = onEvaluateInputViewShown(); 611 if (mIsInputViewShown != isShown && mWindowVisible) { 612 mIsInputViewShown = isShown; 613 mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE); 614 if (mInputView == null) { 615 View v = onCreateInputView(); 616 if (v != null) { 617 setInputView(v); 618 } 619 } 620 } 621 } 622 623 /** 624 * Return whether the soft input view is <em>currently</em> shown to the 625 * user. This is the state that was last determined and 626 * applied by {@link #updateInputViewShown()}. 627 */ 628 public boolean isInputViewShown() { 629 return mIsInputViewShown; 630 } 631 632 /** 633 * Override this to control when the soft input area should be shown to 634 * the user. The default implementation only shows the input view when 635 * there is no hard keyboard or the keyboard is hidden. If you change what 636 * this returns, you will need to call {@link #updateInputViewShown()} 637 * yourself whenever the returned value may have changed to have it 638 * re-evalauted and applied. 639 */ 640 public boolean onEvaluateInputViewShown() { 641 Configuration config = getResources().getConfiguration(); 642 return config.keyboard == Configuration.KEYBOARD_NOKEYS 643 || config.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES; 644 } 645 646 /** 647 * Controls the visibility of the candidates display area. By default 648 * it is hidden. 649 */ 650 public void setCandidatesViewShown(boolean shown) { 651 if (mShowCandidatesRequested != shown) { 652 mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE); 653 if (!mShowInputRequested) { 654 // If we are being asked to show the candidates view while the app 655 // has not asked for the input view to be shown, then we need 656 // to update whether the window is shown. 657 if (shown) { 658 showWindow(false); 659 } else { 660 hideWindow(); 661 } 662 } 663 mShowCandidatesRequested = shown; 664 } 665 } 666 667 public void setStatusIcon(int iconResId) { 668 mStatusIcon = iconResId; 669 if (mInputConnection != null && mWindowVisible) { 670 mInputConnection.showStatusIcon(getPackageName(), iconResId); 671 } 672 } 673 674 /** 675 * Force switch to a new input method, as identified by <var>id</var>. This 676 * input method will be destroyed, and the requested one started on the 677 * current input field. 678 * 679 * @param id Unique identifier of the new input method ot start. 680 */ 681 public void switchInputMethod(String id) { 682 ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) 683 .setInputMethod(mToken, id); 684 } 685 686 public void setExtractView(View view) { 687 mExtractFrame.removeAllViews(); 688 mExtractFrame.addView(view, new FrameLayout.LayoutParams( 689 ViewGroup.LayoutParams.FILL_PARENT, 690 ViewGroup.LayoutParams.WRAP_CONTENT)); 691 mExtractView = view; 692 if (view != null) { 693 mExtractEditText = (ExtractEditText)view.findViewById( 694 com.android.internal.R.id.inputExtractEditText); 695 startExtractingText(); 696 } else { 697 mExtractEditText = null; 698 } 699 } 700 701 /** 702 * Replaces the current candidates view with a new one. You only need to 703 * call this when dynamically changing the view; normally, you should 704 * implement {@link #onCreateCandidatesView()} and create your view when 705 * first needed by the input method. 706 */ 707 public void setCandidatesView(View view) { 708 mCandidatesFrame.removeAllViews(); 709 mCandidatesFrame.addView(view, new FrameLayout.LayoutParams( 710 ViewGroup.LayoutParams.FILL_PARENT, 711 ViewGroup.LayoutParams.WRAP_CONTENT)); 712 } 713 714 /** 715 * Replaces the current input view with a new one. You only need to 716 * call this when dynamically changing the view; normally, you should 717 * implement {@link #onCreateInputView()} and create your view when 718 * first needed by the input method. 719 */ 720 public void setInputView(View view) { 721 mInputFrame.removeAllViews(); 722 mInputFrame.addView(view, new FrameLayout.LayoutParams( 723 ViewGroup.LayoutParams.FILL_PARENT, 724 ViewGroup.LayoutParams.WRAP_CONTENT)); 725 mInputView = view; 726 } 727 728 /** 729 * Called by the framework to create a Drawable for the background of 730 * the input method window. May return null for no background. The default 731 * implementation returns a non-null standard background only when in 732 * fullscreen mode. 733 */ 734 public Drawable onCreateBackgroundDrawable() { 735 if (isFullscreenMode()) { 736 return getResources().getDrawable( 737 com.android.internal.R.drawable.input_method_fullscreen_background); 738 } 739 return null; 740 } 741 742 /** 743 * Called by the framework to create the layout for showing extacted text. 744 * Only called when in fullscreen mode. The returned view hierarchy must 745 * have an {@link ExtractEditText} whose ID is 746 * {@link android.R.id#inputExtractEditText}. 747 */ 748 public View onCreateExtractTextView() { 749 return mInflater.inflate( 750 com.android.internal.R.layout.input_method_extract_view, null); 751 } 752 753 /** 754 * Create and return the view hierarchy used to show candidates. This will 755 * be called once, when the candidates are first displayed. You can return 756 * null to have no candidates view; the default implementation returns null. 757 * 758 * <p>To control when the candidates view is displayed, use 759 * {@link #setCandidatesViewShown(boolean)}. 760 * To change the candidates view after the first one is created by this 761 * function, use {@link #setCandidatesView(View)}. 762 */ 763 public View onCreateCandidatesView() { 764 return null; 765 } 766 767 /** 768 * Create and return the view hierarchy used for the input area (such as 769 * a soft keyboard). This will be called once, when the input area is 770 * first displayed. You can return null to have no input area; the default 771 * implementation returns null. 772 * 773 * <p>To control when the input view is displayed, implement 774 * {@link #onEvaluateInputViewShown()}. 775 * To change the input view after the first one is created by this 776 * function, use {@link #setInputView(View)}. 777 */ 778 public View onCreateInputView() { 779 return null; 780 } 781 782 /** 783 * Called when an input session is starting or restarting. 784 * 785 * @param info Description of the type of text being edited. 786 * @param restarting Set to true if we are restarting input on the 787 * same text field as before. 788 */ 789 public void onStartInputView(EditorInfo info, boolean restarting) { 790 } 791 792 @Override 793 public void onConfigurationChanged(Configuration newConfig) { 794 super.onConfigurationChanged(newConfig); 795 796 boolean visible = mWindowVisible; 797 boolean showingInput = mShowInputRequested; 798 boolean showingCandidates = mShowCandidatesRequested; 799 initViews(); 800 if (visible) { 801 if (showingCandidates) { 802 setCandidatesViewShown(true); 803 } 804 showWindow(showingInput); 805 } 806 } 807 808 /** 809 * The system has decided that it may be time to show your input method. 810 * This is called due to a corresponding call to your 811 * {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)} 812 * method. The default implementation simply calls 813 * {@link #showWindow(boolean)}, except if the 814 * {@link InputMethod#SHOW_EXPLICIT InputMethod.SHOW_EXPLICIT} flag is 815 * not set and the input method is running in fullscreen mode. 816 * 817 * @param flags Provides additional information about the show request, 818 * as per {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)}. 819 */ 820 public void onShowRequested(int flags) { 821 if ((flags&InputMethod.SHOW_EXPLICIT) == 0 && onEvaluateFullscreenMode()) { 822 // Don't show if this is not explicit requested by the user and 823 // the input method is fullscreen. That would be too disruptive. 824 return; 825 } 826 showWindow(true); 827 } 828 829 public void showWindow(boolean showInput) { 830 if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput 831 + " mShowInputRequested=" + mShowInputRequested 832 + " mWindowAdded=" + mWindowAdded 833 + " mWindowCreated=" + mWindowCreated 834 + " mWindowVisible=" + mWindowVisible 835 + " mInputStarted=" + mInputStarted); 836 boolean doShowInput = false; 837 boolean wasVisible = mWindowVisible; 838 mWindowVisible = true; 839 if (!mShowInputRequested) { 840 doShowInput = true; 841 mShowInputRequested = true; 842 } else { 843 showInput = true; 844 } 845 846 if (doShowInput) { 847 if (DEBUG) Log.v(TAG, "showWindow: updating UI"); 848 updateFullscreenMode(); 849 updateInputViewShown(); 850 } 851 852 if (!mWindowAdded || !mWindowCreated) { 853 mWindowAdded = true; 854 mWindowCreated = true; 855 View v = onCreateCandidatesView(); 856 if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v); 857 if (v != null) { 858 setCandidatesView(v); 859 } 860 } 861 if (doShowInput) { 862 if (mInputStarted) { 863 if (DEBUG) Log.v(TAG, "showWindow: starting input view"); 864 onStartInputView(mInputEditorInfo, false); 865 } 866 startExtractingText(); 867 } 868 869 if (!wasVisible) { 870 if (DEBUG) Log.v(TAG, "showWindow: showing!"); 871 mWindow.show(); 872 if (mInputConnection != null) { 873 mInputConnection.showStatusIcon(getPackageName(), mStatusIcon); 874 } 875 } 876 } 877 878 public void hideWindow() { 879 if (mWindowVisible) { 880 mWindow.hide(); 881 mWindowVisible = false; 882 if (mInputConnection != null) { 883 mInputConnection.hideStatusIcon(); 884 } 885 } 886 } 887 888 public void onBindInput() { 889 } 890 891 public void onStartInput(EditorInfo attribute, boolean restarting) { 892 } 893 894 void doStartInput(EditorInfo attribute, boolean restarting) { 895 mInputStarted = true; 896 mInputEditorInfo = attribute; 897 onStartInput(attribute, restarting); 898 if (mWindowVisible) { 899 if (mWindowCreated) { 900 onStartInputView(mInputEditorInfo, restarting); 901 } 902 startExtractingText(); 903 } 904 } 905 906 public void onFinishInput() { 907 } 908 909 /** 910 * Called when the application has reported auto-completion candidates that 911 * it would like to have the input method displayed. Typically these are 912 * only used when an input method is running in full-screen mode, since 913 * otherwise the user can see and interact with the pop-up window of 914 * completions shown by the application. 915 * 916 * <p>The default implementation here does nothing. 917 */ 918 public void onDisplayCompletions(CompletionInfo[] completions) { 919 } 920 921 /** 922 * Called when the application has reported new extracted text to be shown 923 * due to changes in its current text state. The default implementation 924 * here places the new text in the extract edit text, when the input 925 * method is running in fullscreen mode. 926 */ 927 public void onUpdateExtractedText(int token, ExtractedText text) { 928 if (mExtractedToken != token) { 929 return; 930 } 931 if (mExtractEditText != null && text != null) { 932 mExtractedText = text; 933 mExtractEditText.setExtractedText(text); 934 } 935 } 936 937 /** 938 * Called when the application has reported a new selection region of 939 * the text. This is called whether or not the input method has requested 940 * extracted text updates, although if so it will not receive this call 941 * if the extracted text has changed as well. 942 * 943 * <p>The default implementation takes care of updating the cursor in 944 * the extract text, if it is being shown. 945 */ 946 public void onUpdateSelection(int oldSelStart, int oldSelEnd, 947 int newSelStart, int newSelEnd, 948 int candidatesStart, int candidatesEnd) { 949 if (mExtractEditText != null && mExtractedText != null) { 950 final int off = mExtractedText.startOffset; 951 mExtractEditText.setSelection(newSelStart-off, newSelEnd-off); 952 } 953 } 954 955 /** 956 * Called when the application has reported a new location of its text 957 * cursor. This is only called if explicitly requested by the input method. 958 * The default implementation does nothing. 959 */ 960 public void onUpdateCursor(Rect newCursor) { 961 } 962 963 /** 964 * Close this input method's soft input area, removing it from the display. 965 * The input method will continue running, but the user can no longer use 966 * it to generate input by touching the screen. 967 * @param flags Provides additional operating flags. Currently may be 968 * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY 969 * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set. 970 */ 971 public void dismissSoftInput(int flags) { 972 ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) 973 .hideSoftInputFromInputMethod(mToken, flags); 974 } 975 976 public boolean onKeyDown(int keyCode, KeyEvent event) { 977 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK 978 && event.getRepeatCount() == 0) { 979 if (mShowInputRequested) { 980 // If the soft input area is shown, back closes it and we 981 // consume the back key. 982 dismissSoftInput(0); 983 return true; 984 } 985 if (mShowCandidatesRequested) { 986 // If the candidates are shown, we just want to make sure 987 // they are now hidden but otherwise let the app execute 988 // the back. 989 // XXX this needs better interaction with the soft input 990 // implementation. 991 //setCandidatesViewShown(false); 992 } 993 } 994 return false; 995 } 996 997 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 998 return false; 999 } 1000 1001 public boolean onKeyUp(int keyCode, KeyEvent event) { 1002 return false; 1003 } 1004 1005 public boolean onTrackballEvent(MotionEvent event) { 1006 return false; 1007 } 1008 1009 public void onAppPrivateCommand(String action, Bundle data) { 1010 } 1011 1012 void startExtractingText() { 1013 if (mExtractEditText != null && getCurrentInputStarted() 1014 && isFullscreenMode()) { 1015 mExtractedToken++; 1016 ExtractedTextRequest req = new ExtractedTextRequest(); 1017 req.token = mExtractedToken; 1018 req.hintMaxLines = 10; 1019 req.hintMaxChars = 10000; 1020 mExtractedText = mInputConnection.getExtractedText(req, 1021 InputConnection.EXTRACTED_TEXT_MONITOR); 1022 if (mExtractedText != null) { 1023 mExtractEditText.setExtractedText(mExtractedText); 1024 } 1025 mExtractEditText.setInputType(getCurrentInputEditorInfo().inputType); 1026 mExtractEditText.setHint(mInputEditorInfo.hintText); 1027 } 1028 } 1029} 1030