diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-02 22:54:33 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-02 22:54:33 -0800 |
| commit | 3dec7d563a2f3e1eb967ce2054a00b6620e3558c (patch) | |
| tree | aa3b0365c47cb3c1607c0dc76c8d32b4046fc287 /core/java/android/inputmethodservice | |
| parent | 15ab3eae2ec3d73b3e8aa60b33ae41445bf83f4b (diff) | |
| download | frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.zip frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.tar.gz frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.tar.bz2 | |
auto import from //depot/cupcake/@137055
Diffstat (limited to 'core/java/android/inputmethodservice')
3 files changed, 278 insertions, 17 deletions
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 52f8209..0295f69 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -98,6 +98,13 @@ public class ExtractEditText extends EditText { } /** + * Return true if the edit text is currently showing a scroll bar. + */ + public boolean hasVerticalScrollBar() { + return computeVerticalScrollRange() > computeVerticalScrollExtent(); + } + + /** * Pretend like the window this view is in always has focus, so its * highlight and cursor will be displayed. */ diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 4be1fc7..1e2e2f3 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -26,7 +26,9 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; +import android.os.SystemClock; import android.provider.Settings; +import android.text.InputType; import android.text.Layout; import android.text.Spannable; import android.text.method.MovementMethod; @@ -49,6 +51,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.EditorInfo; +import android.widget.Button; import android.widget.FrameLayout; import java.io.FileDescriptor; @@ -241,6 +244,8 @@ public class InputMethodService extends AbstractInputMethodService { boolean mIsFullscreen; View mExtractView; ExtractEditText mExtractEditText; + ViewGroup mExtractAccessories; + Button mExtractAction; ExtractedText mExtractedText; int mExtractedToken; @@ -271,6 +276,21 @@ public class InputMethodService extends AbstractInputMethodService { } }; + final View.OnClickListener mActionClickListener = new View.OnClickListener() { + public void onClick(View v) { + final EditorInfo ei = getCurrentInputEditorInfo(); + final InputConnection ic = getCurrentInputConnection(); + if (ei != null && ic != null) { + if (ei.actionId != 0) { + ic.performEditorAction(ei.actionId); + } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION) + != EditorInfo.IME_ACTION_NONE) { + ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION); + } + } + } + }; + /** * Concrete implementation of * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides @@ -522,6 +542,8 @@ public class InputMethodService extends AbstractInputMethodService { mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea); mExtractView = null; mExtractEditText = null; + mExtractAccessories = null; + mExtractAction = null; mFullscreenApplied = false; mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea); @@ -703,7 +725,7 @@ public class InputMethodService extends AbstractInputMethodService { setExtractView(v); } } - startExtractingText(); + startExtractingText(false); } } @@ -907,9 +929,17 @@ public class InputMethodService extends AbstractInputMethodService { mExtractEditText = (ExtractEditText)view.findViewById( com.android.internal.R.id.inputExtractEditText); mExtractEditText.setIME(this); - startExtractingText(); + mExtractAction = (Button)view.findViewById( + com.android.internal.R.id.inputExtractAction); + if (mExtractAction != null) { + mExtractAccessories = (ViewGroup)view.findViewById( + com.android.internal.R.id.inputExtractAccessories); + } + startExtractingText(false); } else { mExtractEditText = null; + mExtractAccessories = null; + mExtractAction = null; } } @@ -1166,7 +1196,7 @@ public class InputMethodService extends AbstractInputMethodService { } if (doShowInput) { - startExtractingText(); + startExtractingText(false); } if (!wasVisible) { @@ -1276,7 +1306,7 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); mInputViewStarted = true; onStartInputView(mInputEditorInfo, restarting); - startExtractingText(); + startExtractingText(true); } else if (mCandidatesVisibility == View.VISIBLE) { if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); mCandidatesViewStarted = true; @@ -1453,6 +1483,25 @@ public class InputMethodService extends AbstractInputMethodService { static final int MOVEMENT_DOWN = -1; static final int MOVEMENT_UP = -2; + void reportExtractedMovement(int keyCode, int count) { + int dx = 0, dy = 0; + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + dx = -count; + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + dx = count; + break; + case KeyEvent.KEYCODE_DPAD_UP: + dy = -count; + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + dy = count; + break; + } + onExtractedCursorMovement(dx, dy); + } + boolean doMovementKey(int keyCode, KeyEvent event, int count) { final ExtractEditText eet = mExtractEditText; if (isFullscreenMode() && isInputViewShown() && eet != null) { @@ -1467,6 +1516,7 @@ public class InputMethodService extends AbstractInputMethodService { if (count == MOVEMENT_DOWN) { if (movement.onKeyDown(eet, (Spannable)eet.getText(), keyCode, event)) { + reportExtractedMovement(keyCode, 1); return true; } } else if (count == MOVEMENT_UP) { @@ -1475,7 +1525,9 @@ public class InputMethodService extends AbstractInputMethodService { return true; } } else { - if (!movement.onKeyOther(eet, (Spannable)eet.getText(), event)) { + if (movement.onKeyOther(eet, (Spannable)eet.getText(), event)) { + reportExtractedMovement(keyCode, count); + } else { KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); if (movement.onKeyDown(eet, (Spannable)eet.getText(), keyCode, down)) { @@ -1488,6 +1540,7 @@ public class InputMethodService extends AbstractInputMethodService { movement.onKeyUp(eet, (Spannable)eet.getText(), keyCode, up); } + reportExtractedMovement(keyCode, count); } } } @@ -1507,6 +1560,97 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Send the given key event code (as defined by {@link KeyEvent}) to the + * current input connection is a key down + key up event pair. The sent + * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD} + * set, so that the recipient can identify them as coming from a software + * input method, and + * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so + * that they don't impact the current touch mode of the UI. + * + * @param keyEventCode The raw key code to send, as defined by + * {@link KeyEvent}. + */ + public void sendDownUpKeyEvents(int keyEventCode) { + InputConnection ic = getCurrentInputConnection(); + if (ic == null) return; + long eventTime = SystemClock.uptimeMillis(); + ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, + KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); + ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, + KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); + } + + /** + * Ask the input target to execute its default action via + * {@link InputConnection#performEditorAction + * InputConnection.performEditorAction()}. + * + * @param fromEnterKey If true, this will be executed as if the user had + * pressed an enter key on the keyboard, that is it will <em>not</em> + * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION + * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be + * sent regardless of how the editor has set that flag. + * + * @return Returns a boolean indicating whether an action has been sent. + * If false, either the editor did not specify a default action or it + * does not want an action from the enter key. If true, the action was + * sent (or there was no input connection at all). + */ + public boolean sendDefaultEditorAction(boolean fromEnterKey) { + EditorInfo ei = getCurrentInputEditorInfo(); + if (ei != null && + (!fromEnterKey || (ei.imeOptions & + EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) && + (ei.imeOptions & EditorInfo.IME_MASK_ACTION) != + EditorInfo.IME_ACTION_NONE) { + // If the enter key was pressed, and the editor has a default + // action associated with pressing enter, then send it that + // explicit action instead of the key event. + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION); + } + return true; + } + + return false; + } + + /** + * Send the given UTF-16 character to the current input connection. Most + * characters will be delivered simply by calling + * {@link InputConnection#commitText InputConnection.commitText()} with + * the character; some, however, may be handled different. In particular, + * the enter character ('\n') will either be delivered as an action code + * or a raw key event, as appropriate. + * + * @param charCode The UTF-16 character code to send. + */ + public void sendKeyChar(char charCode) { + switch (charCode) { + case '\n': // Apps may be listening to an enter key to perform an action + if (!sendDefaultEditorAction(true)) { + sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); + } + break; + default: + // Make sure that digits go through any text watcher on the client side. + if (charCode >= '0' && charCode <= '9') { + sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0); + } else { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.commitText(String.valueOf((char) charCode), 1); + } + } + break; + } + } + + /** * This is called when the user has moved the cursor in the extracted * text view, when running in fullsreen mode. The default implementation * performs the corresponding selection change on the underlying text @@ -1522,11 +1666,36 @@ public class InputMethodService extends AbstractInputMethodService { /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides - * the candidates view when this happens. Re-implement this to provide - * whatever behavior you want. + * the candidates view when this happens, but only if the extracted text + * editor has a vertical scroll bar because its text doesn't fit. + * Re-implement this to provide whatever behavior you want. */ public void onExtractedTextClicked() { - setCandidatesViewShown(false); + if (mExtractEditText == null) { + return; + } + if (mExtractEditText.hasVerticalScrollBar()) { + setCandidatesViewShown(false); + } + } + + /** + * This is called when the user has performed a cursor movement in the + * extracted text view, when it is running in fullscreen mode. The default + * implementation hides the candidates view when a vertical movement + * happens, but only if the extracted text editor has a vertical scroll bar + * because its text doesn't fit. + * Re-implement this to provide whatever behavior you want. + * @param dx The amount of cursor movement in the x dimension. + * @param dy The amount of cursor movement in the y dimension. + */ + public void onExtractedCursorMovement(int dx, int dy) { + if (mExtractEditText == null || dy == 0) { + return; + } + if (mExtractEditText.hasVerticalScrollBar()) { + setCandidatesViewShown(false); + } } /** @@ -1545,7 +1714,74 @@ public class InputMethodService extends AbstractInputMethodService { return true; } - void startExtractingText() { + /** + * Return text that can be used as a button label for the given + * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null + * if there is no action requested. Note that there is no guarantee that + * the returned text will be relatively short, so you probably do not + * want to use it as text on a soft keyboard key label. + * + * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}. + * + * @return Returns a label to use, or null if there is no action. + */ + public CharSequence getTextForImeAction(int imeOptions) { + switch (imeOptions&EditorInfo.IME_MASK_ACTION) { + case EditorInfo.IME_ACTION_NONE: + return null; + case EditorInfo.IME_ACTION_GO: + return getText(com.android.internal.R.string.ime_action_go); + case EditorInfo.IME_ACTION_SEARCH: + return getText(com.android.internal.R.string.ime_action_search); + case EditorInfo.IME_ACTION_SEND: + return getText(com.android.internal.R.string.ime_action_send); + case EditorInfo.IME_ACTION_NEXT: + return getText(com.android.internal.R.string.ime_action_next); + default: + return getText(com.android.internal.R.string.ime_action_default); + } + } + + /** + * Called when it is time to update the actions available from a full-screen + * IME. You do not need to deal with this if you are using the standard + * full screen extract UI. If replacing it, you will need to re-implement + * this to put the action in your own UI and handle it. + */ + public void onUpdateExtractingAccessories(EditorInfo ei) { + if (mExtractAccessories == null) { + return; + } + final boolean hasAction = ei.actionLabel != null || ( + (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE && + (ei.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0); + if (hasAction) { + mExtractAccessories.setVisibility(View.VISIBLE); + if (ei.actionLabel != null) { + mExtractAction.setText(ei.actionLabel); + } else { + mExtractAction.setText(getTextForImeAction(ei.imeOptions)); + } + mExtractAction.setOnClickListener(mActionClickListener); + } else { + mExtractAccessories.setVisibility(View.GONE); + mExtractAction.setOnClickListener(null); + } + } + + /** + * This is called when, while currently displayed in extract mode, the + * current input target changes. The default implementation will + * auto-hide the IME if the new target is not a full editor, since this + * can be an confusing experience for the user. + */ + public void onExtractingInputChanged(EditorInfo ei) { + if (ei.inputType == InputType.TYPE_NULL) { + dismissSoftInput(InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + void startExtractingText(boolean inputChanged) { final ExtractEditText eet = mExtractEditText; if (eet != null && getCurrentInputStarted() && isFullscreenMode()) { @@ -1557,9 +1793,13 @@ public class InputMethodService extends AbstractInputMethodService { req.hintMaxChars = 10000; mExtractedText = getCurrentInputConnection().getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR); + + final EditorInfo ei = getCurrentInputEditorInfo(); + try { eet.startInternalChanges(); - int inputType = getCurrentInputEditorInfo().inputType; + onUpdateExtractingAccessories(ei); + int inputType = ei.inputType; if ((inputType&EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) { @@ -1567,7 +1807,7 @@ public class InputMethodService extends AbstractInputMethodService { } } eet.setInputType(inputType); - eet.setHint(mInputEditorInfo.hintText); + eet.setHint(ei.hintText); if (mExtractedText != null) { eet.setEnabled(true); eet.setExtractedText(mExtractedText); @@ -1578,6 +1818,10 @@ public class InputMethodService extends AbstractInputMethodService { } finally { eet.finishInternalChanges(); } + + if (inputChanged) { + onExtractingInputChanged(ei); + } } } diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 886e688..c838779 100755 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -74,7 +74,6 @@ public class KeyboardView extends View implements View.OnClickListener { * For keys that repeat, this is only called once. * @param primaryCode the unicode of the key being pressed. If the touch is not on a valid * key, the value will be zero. - * @hide Pending API Council approval */ void onPress(int primaryCode); @@ -82,7 +81,6 @@ public class KeyboardView extends View implements View.OnClickListener { * Called when the user releases a key. This is sent after the {@link #onKey} is called. * For keys that repeat, this is only called once. * @param primaryCode the code of the key that was released - * @hide Pending API Council approval */ void onRelease(int primaryCode); @@ -99,6 +97,12 @@ public class KeyboardView extends View implements View.OnClickListener { void onKey(int primaryCode, int[] keyCodes); /** + * Sends a sequence of characters to the listener. + * @param text the sequence of characters to be displayed. + */ + void onText(CharSequence text); + + /** * Called when the user quickly moves the finger from right to left. */ void swipeLeft(); @@ -394,6 +398,7 @@ public class KeyboardView extends View implements View.OnClickListener { requestLayout(); invalidate(); computeProximityThreshold(keyboard); + mMiniKeyboardCache.clear(); // Not really necessary to do every time, but will free up views } /** @@ -699,9 +704,7 @@ public class KeyboardView extends View implements View.OnClickListener { if (index != NOT_A_KEY && index < mKeys.length) { final Key key = mKeys[index]; if (key.text != null) { - for (int i = 0; i < key.text.length(); i++) { - mKeyboardActionListener.onKey(key.text.charAt(i), key.codes); - } + mKeyboardActionListener.onText(key.text); mKeyboardActionListener.onRelease(NOT_A_KEY); } else { int code = key.codes[0]; @@ -792,7 +795,7 @@ public class KeyboardView extends View implements View.OnClickListener { mPreviewText.setCompoundDrawables(null, null, null, null); mPreviewText.setText(getPreviewText(key)); if (key.label.length() > 1 && key.codes.length < 2) { - mPreviewText.setTextSize(mLabelTextSize); + mPreviewText.setTextSize(mKeyTextSize); mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); } else { mPreviewText.setTextSize(mPreviewTextSizeLarge); @@ -896,6 +899,11 @@ public class KeyboardView extends View implements View.OnClickListener { dismissPopupKeyboard(); } + public void onText(CharSequence text) { + mKeyboardActionListener.onText(text); + dismissPopupKeyboard(); + } + public void swipeLeft() { } public void swipeRight() { } public void swipeUp() { } @@ -1102,6 +1110,8 @@ public class KeyboardView extends View implements View.OnClickListener { mHandler.removeMessages(MSG_SHOW_PREVIEW); dismissPopupKeyboard(); + + mMiniKeyboardCache.clear(); } @Override |
