diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-01-09 17:51:23 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-01-09 17:51:23 -0800 |
| commit | b798689749c64baba81f02e10cf2157c747d6b46 (patch) | |
| tree | da394a395ddb1a6cf69193314846b03fe47a397e /core/java/android/inputmethodservice | |
| parent | f013e1afd1e68af5e3b868c26a653bbfb39538f8 (diff) | |
| download | frameworks_base-b798689749c64baba81f02e10cf2157c747d6b46.zip frameworks_base-b798689749c64baba81f02e10cf2157c747d6b46.tar.gz frameworks_base-b798689749c64baba81f02e10cf2157c747d6b46.tar.bz2 | |
auto import from //branches/cupcake/...@125939
Diffstat (limited to 'core/java/android/inputmethodservice')
4 files changed, 208 insertions, 38 deletions
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index 40c03cd..5a85c66 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -91,7 +91,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub case DO_UPDATE_SELECTION: { HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; mInputMethodSession.updateSelection(args.argi1, args.argi2, - args.argi3, args.argi4); + args.argi3, args.argi4, args.argi5, args.argi6); mCaller.recycleArgs(args); return; } @@ -135,9 +135,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } public void updateSelection(int oldSelStart, int oldSelEnd, - int newSelStart, int newSelEnd) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIIII(DO_UPDATE_SELECTION, - oldSelStart, oldSelEnd, newSelStart, newSelEnd)); + int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { + mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION, + oldSelStart, oldSelEnd, newSelStart, newSelEnd, + candidatesStart, candidatesEnd)); } public void updateCursor(Rect newCursor) { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 9ebf127..0588bea 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -48,6 +48,132 @@ import android.widget.FrameLayout; * which final implementations can derive from and customize. See the * base class {@link AbstractInputMethodService} and the {@link InputMethod} * interface for more information on the basics of writing input methods. + * + * <p>An input method has significant discretion in how it goes about its + * work: the {@link android.inputmethodservice.InputMethodService} provides + * a basic framework for standard UI elements (input view, candidates view, + * and running in fullscreen mode), but it is up to a particular implementor + * to decide how to use them. For example, one input method could implement + * an input area with a keyboard, another could allow the user to draw text, + * while a third could have no input area (and thus not be visible to the + * user) but instead listen to audio and perform text to speech conversion.</p> + * + * <p>In the implementation provided here, all of these elements are placed + * together in a single window managed by the InputMethodService. It will + * execute callbacks as it needs information about them, and provides APIs for + * programmatic control over them. They layout of these elements is explicitly + * defined:</p> + * + * <ul> + * <li>The soft input view, if available, is placed at the bottom of the + * screen. + * <li>The candidates view, if currently shown, is placed above the soft + * input view. + * <li>If not running fullscreen, the application is moved or resized to be + * above these views; if running fullscreen, the window will completely cover + * the application and its top part will contain the extract text of what is + * currently being edited by the application. + * </ul> + * + * + * <a name="SoftInputView"></a> + * <h3>Soft Input View</h3> + * + * <p>Central to most input methods is the soft input view. This is where most + * user interaction occurs: pressing on soft keys, drawing characters, or + * however else your input method wants to generate text. Most implementations + * will simply have their own view doing all of this work, and return a new + * instance of it when {@link #onCreateInputView()} is called. At that point, + * as long as the input view is visible, you will see user interaction in + * that view and can call back on the InputMethodService to interact with the + * application as appropriate.</p> + * + * <p>There are some situations where you want to decide whether or not your + * soft input view should be shown to the user. This is done by implementing + * the {@link #onEvaluateInputViewShown()} to return true or false based on + * whether it should be shown in the current environment. If any of your + * state has changed that may impact this, call + * {@link #updateInputViewShown()} to have it re-evaluated. The default + * implementation always shows the input view unless there is a hard + * keyboard available, which is the appropriate behavior for most input + * methods.</p> + * + * + * <a name="CandidatesView"></a> + * <h3>Candidates View</h3> + * + * <p>Often while the user is generating raw text, an input method wants to + * provide them with a list of possible interpretations of that text that can + * be selected for use. This is accomplished with the candidates view, and + * like the soft input view you implement {@link #onCreateCandidatesView()} + * to instantiate your own view implementing your candidates UI.</p> + * + * <p>Management of the candidates view is a little different than the input + * view, because the candidates view tends to be more transient, being shown + * only when there are possible candidates for the current text being entered + * by the user. To control whether the candidates view is shown, you use + * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate + * view tends to be shown and hidden a lot, it does not impact the application + * UI in the same way as the soft input view: it will never cause application + * windows to resize, only cause them to be panned if needed for the user to + * see the current focus.</p> + * + * + * <a name="FullscreenMode"></a> + * <h3>Fullscreen Mode</h3> + * + * <p>Sometimes your input method UI is too large to integrate with the + * application UI, so you just want to take over the screen. This is + * accomplished by switching to full-screen mode, causing the input method + * window to fill the entire screen and add its own "extracted text" editor + * showing the user the text that is being typed. Unlike the other UI elements, + * there is a standard implementation for the extract editor that you should + * not need to change. The editor is placed at the top of the IME, above the + * input and candidates views.</p> + * + * <p>Similar to the input view, you control whether the IME is running in + * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()} + * to return true or false based on + * whether it should be fullscreen in the current environment. If any of your + * state has changed that may impact this, call + * {@link #updateFullscreenMode()} to have it re-evaluated. The default + * implementation selects fullscreen mode when the screen is in a landscape + * orientation, which is appropriate behavior for most input methods that have + * a significant input area.</p> + * + * <p>When in fullscreen mode, you have some special requirements because the + * user can not see the application UI. In particular, you should implement + * {@link #onDisplayCompletions(CompletionInfo[])} to show completions + * generated by your application, typically in your candidates view like you + * would normally show candidates. + * + * + * <a name="GeneratingText"></a> + * <h3>Generating Text</h3> + * + * <p>The key part of an IME is of course generating text for the application. + * This is done through calls to the + * {@link android.view.inputmethod.InputConnection} interface to the + * application, which can be retrieved from {@link #getCurrentInputConnection()}. + * This interface allows you to generate raw key events or, if the target + * supports it, directly edit in strings of candidates and committed text.</p> + * + * <p>Information about what the target is expected and supports can be found + * through the {@link android.view.inputmethod.EditorInfo} class, which is + * retrieved with {@link #getCurrentInputEditorInfo()} method. The most + * important part of this is {@link android.view.inputmethod.EditorInfo#inputType + * EditorInfo.inputType}; in particular, if this is + * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL}, + * then the target does not support complex edits and you need to only deliver + * raw key events to it. An input method will also want to look at other + * values here, to for example detect password mode, auto complete text views, + * phone number entry, etc.</p> + * + * <p>When the user switches between input targets, you will receive calls to + * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}. + * You can use these to reset and initialize your input state for the current + * target. For example, you will often want to clear any input state, and + * update a soft keyboard to be appropriate for the new inputType.</p> */ public class InputMethodService extends AbstractInputMethodService { static final String TAG = "InputMethodService"; @@ -68,7 +194,7 @@ public class InputMethodService extends AbstractInputMethodService { InputBinding mInputBinding; InputConnection mInputConnection; boolean mInputStarted; - EditorInfo mInputInfo; + EditorInfo mInputEditorInfo; boolean mShowInputRequested; boolean mShowCandidatesRequested; @@ -210,12 +336,13 @@ public class InputMethodService extends AbstractInputMethodService { * InputMethodService.onUpdateSelection()}. */ public void updateSelection(int oldSelStart, int oldSelEnd, - int newSelStart, int newSelEnd) { + int newSelStart, int newSelEnd, + int candidatesStart, int candidatesEnd) { if (!isEnabled()) { return; } InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd, - newSelStart, newSelEnd); + newSelStart, newSelEnd, candidatesStart, candidatesEnd); } /** @@ -303,6 +430,7 @@ public class InputMethodService extends AbstractInputMethodService { Context.LAYOUT_INFLATER_SERVICE); mWindow = new SoftInputWindow(this); initViews(); + mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); } void initViews() { @@ -384,8 +512,8 @@ public class InputMethodService extends AbstractInputMethodService { return mInputStarted; } - public EditorInfo getCurrentInputInfo() { - return mInputInfo; + public EditorInfo getCurrentInputEditorInfo() { + return mInputEditorInfo; } /** @@ -459,14 +587,14 @@ public class InputMethodService extends AbstractInputMethodService { int[] loc = mTmpLocation; if (mInputFrame.getVisibility() == View.VISIBLE) { mInputFrame.getLocationInWindow(loc); - outInsets.contentTopInsets = loc[1]; + } else { + loc[1] = 0; } + outInsets.contentTopInsets = loc[1]; if (mCandidatesFrame.getVisibility() == View.VISIBLE) { mCandidatesFrame.getLocationInWindow(loc); - outInsets.visibleTopInsets = loc[1]; - } else { - outInsets.visibleTopInsets = loc[1]; } + outInsets.visibleTopInsets = loc[1]; outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE; } @@ -712,7 +840,7 @@ public class InputMethodService extends AbstractInputMethodService { if (doShowInput) { if (mInputStarted) { if (DEBUG) Log.v(TAG, "showWindow: starting input view"); - onStartInputView(mInputInfo, false); + onStartInputView(mInputEditorInfo, false); } startExtractingText(); } @@ -744,11 +872,11 @@ public class InputMethodService extends AbstractInputMethodService { void doStartInput(EditorInfo attribute, boolean restarting) { mInputStarted = true; - mInputInfo = attribute; + mInputEditorInfo = attribute; onStartInput(attribute, restarting); if (mWindowVisible) { if (mWindowCreated) { - onStartInputView(mInputInfo, restarting); + onStartInputView(mInputEditorInfo, restarting); } startExtractingText(); } @@ -795,7 +923,8 @@ public class InputMethodService extends AbstractInputMethodService { * the extract text, if it is being shown. */ public void onUpdateSelection(int oldSelStart, int oldSelEnd, - int newSelStart, int newSelEnd) { + int newSelStart, int newSelEnd, + int candidatesStart, int candidatesEnd) { if (mExtractEditText != null && mExtractedText != null) { final int off = mExtractedText.startOffset; mExtractEditText.setSelection(newSelStart-off, newSelEnd-off); @@ -821,10 +950,22 @@ public class InputMethodService extends AbstractInputMethodService { } public boolean onKeyDown(int keyCode, KeyEvent event) { - if (mWindowVisible && event.getKeyCode() == KeyEvent.KEYCODE_BACK + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { - dismissSoftInput(); - return true; + if (mShowInputRequested) { + // If the soft input area is shown, back closes it and we + // consume the back key. + dismissSoftInput(); + return true; + } + if (mShowCandidatesRequested) { + // If the candidates are shown, we just want to make sure + // they are now hidden but otherwise let the app execute + // the back. + // XXX this needs better interaction with the soft input + // implementation. + //setCandidatesViewShown(false); + } } return false; } @@ -857,8 +998,8 @@ public class InputMethodService extends AbstractInputMethodService { if (mExtractedText != null) { mExtractEditText.setExtractedText(mExtractedText); } - mExtractEditText.setInputType(getCurrentInputInfo().inputType); - mExtractEditText.setHint(mInputInfo.hintText); + mExtractEditText.setInputType(getCurrentInputEditorInfo().inputType); + mExtractEditText.setHint(mInputEditorInfo.hintText); } } } diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java index 75a2911..cfd3188 100755 --- a/core/java/android/inputmethodservice/Keyboard.java +++ b/core/java/android/inputmethodservice/Keyboard.java @@ -438,7 +438,6 @@ public class Keyboard { } } - /** * Returns the square of the distance between the center of the key and the given point. * @param x the x-coordinate of the point @@ -446,9 +445,9 @@ public class Keyboard { * @return the square of the distance of the point from the center of the key */ public int squaredDistanceFrom(int x, int y) { - float xDist = Math.abs((this.x + this.x + width) / 2f - x); - float yDist = Math.abs((this.y + this.y + height) / 2f - y); - return (int) (xDist * xDist + yDist * yDist); + int xDist = this.x + width / 2 - x; + int yDist = this.y + height / 2 - y; + return xDist * xDist + yDist * yDist; } /** @@ -749,7 +748,8 @@ public class Keyboard { if (value.type == TypedValue.TYPE_DIMENSION) { return a.getDimensionPixelOffset(index, defValue); } else if (value.type == TypedValue.TYPE_FRACTION) { - return (int) a.getFraction(index, base, base, defValue); + // Round it to avoid values like 47.9999 from getting truncated + return Math.round(a.getFraction(index, base, base, defValue)); } return defValue; } diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 56473da..3b5d741 100755 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -24,6 +24,7 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.Typeface; import android.graphics.Paint.Align; import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard.Key; @@ -37,6 +38,7 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.PopupWindow; @@ -132,6 +134,7 @@ public class KeyboardView extends View implements View.OnClickListener { private static final int MSG_REMOVE_PREVIEW = 1; private static final int MSG_REPEAT = 2; + private static final int MSG_LONGPRESS = 3; private int mVerticalCorrection; private int mProximityThreshold; @@ -178,6 +181,7 @@ public class KeyboardView extends View implements View.OnClickListener { private static final int REPEAT_INTERVAL = 50; // ~20 keys per second private static final int REPEAT_START_DELAY = 400; + private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); private Vibrator mVibrator; private long[] mVibratePattern = new long[] {1, 20}; @@ -206,6 +210,9 @@ public class KeyboardView extends View implements View.OnClickListener { sendMessageDelayed(repeat, REPEAT_INTERVAL); } break; + case MSG_LONGPRESS: + openPopupIfRequired((MotionEvent) msg.obj); + break; } } @@ -308,27 +315,28 @@ public class KeyboardView extends View implements View.OnClickListener { @Override public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { - if (velocityX > 400 && Math.abs(velocityY) < 400) { + final float absX = Math.abs(velocityX); + final float absY = Math.abs(velocityY); + if (velocityX > 500 && absY < absX) { swipeRight(); return true; - } else if (velocityX < -400 && Math.abs(velocityY) < 400) { + } else if (velocityX < -500 && absY < absX) { swipeLeft(); return true; - } else if (velocityY < -400 && Math.abs(velocityX) < 400) { + } else if (velocityY < -500 && absX < absY) { swipeUp(); return true; - } else if (velocityY > 400 && Math.abs(velocityX) < 400) { + } else if (velocityY > 500 && absX < 200) { swipeDown(); return true; + } else if (absX > 800 || absY > 800) { + return true; } return false; } - - @Override - public void onLongPress(MotionEvent me) { - openPopupIfRequired(me); - } }); + + mGestureDetector.setIsLongpressEnabled(false); } public void setOnKeyboardActionListener(OnKeyboardActionListener listener) { @@ -351,6 +359,9 @@ public class KeyboardView extends View implements View.OnClickListener { * @param keyboard the keyboard to display in this view */ public void setKeyboard(Keyboard keyboard) { + if (mKeyboard != null) { + showPreview(NOT_A_KEY); + } mKeyboard = keyboard; requestLayout(); invalidate(); @@ -518,10 +529,10 @@ public class KeyboardView extends View implements View.OnClickListener { // For characters, use large font. For labels like "Done", use small font. if (label.length() > 1 && key.codes.length < 2) { paint.setTextSize(mLabelTextSize); - paint.setFakeBoldText(true); + paint.setTypeface(Typeface.DEFAULT_BOLD); } else { paint.setTextSize(mKeyTextSize); - paint.setFakeBoldText(false); + paint.setTypeface(Typeface.DEFAULT); } // Draw a drop shadow for the text paint.setShadowLayer(3f, 0, 0, 0xCC000000); @@ -878,6 +889,7 @@ public class KeyboardView extends View implements View.OnClickListener { if (mGestureDetector.onTouchEvent(me)) { showPreview(NOT_A_KEY); mHandler.removeMessages(MSG_REPEAT); + mHandler.removeMessages(MSG_LONGPRESS); return true; } @@ -907,12 +919,17 @@ public class KeyboardView extends View implements View.OnClickListener { Message msg = mHandler.obtainMessage(MSG_REPEAT); mHandler.sendMessageDelayed(msg, REPEAT_START_DELAY); } + if (mCurrentKey != NOT_A_KEY) { + Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me); + mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT); + } showPreview(keyIndex); playKeyClick(); vibrate(); break; case MotionEvent.ACTION_MOVE: + boolean continueLongPress = false; if (keyIndex != NOT_A_KEY) { if (mCurrentKey == NOT_A_KEY) { mCurrentKey = keyIndex; @@ -920,6 +937,7 @@ public class KeyboardView extends View implements View.OnClickListener { } else { if (keyIndex == mCurrentKey) { mCurrentKeyTime += eventTime - mLastMoveTime; + continueLongPress = true; } else { resetMultiTap(); mLastKey = mCurrentKey; @@ -936,11 +954,21 @@ public class KeyboardView extends View implements View.OnClickListener { mRepeatKeyIndex = NOT_A_KEY; } } + if (!continueLongPress) { + // Cancel old longpress + mHandler.removeMessages(MSG_LONGPRESS); + // Start new longpress if key has changed + if (keyIndex != NOT_A_KEY) { + Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me); + mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT); + } + } showPreview(keyIndex); break; case MotionEvent.ACTION_UP: mHandler.removeMessages(MSG_REPEAT); + mHandler.removeMessages(MSG_LONGPRESS); if (keyIndex == mCurrentKey) { mCurrentKeyTime += eventTime - mLastMoveTime; } else { |
