diff options
Diffstat (limited to 'core/java/android/inputmethodservice')
5 files changed, 345 insertions, 84 deletions
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index eedcc35..3619653 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -45,6 +45,9 @@ public abstract class AbstractInputMethodService extends Service implements KeyEvent.Callback { private InputMethod mInputMethod; + final KeyEvent.DispatcherState mDispatcherState + = new KeyEvent.DispatcherState(); + /** * Base class for derived classes to implement their {@link InputMethod} * interface. This takes care of basic maintenance of the input method, @@ -129,7 +132,8 @@ public abstract class AbstractInputMethodService extends Service * callbacks on the service, and tell the client when this is done. */ public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) { - boolean handled = event.dispatch(AbstractInputMethodService.this); + boolean handled = event.dispatch(AbstractInputMethodService.this, + mDispatcherState, this); if (callback != null) { callback.finishedEvent(seq, handled); } @@ -148,6 +152,16 @@ public abstract class AbstractInputMethodService extends Service } /** + * Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState} + * for used for processing events from the target application. + * Normally you will not need to use this directly, but + * just use the standard high-level event callbacks like {@link #onKeyDown}. + */ + public KeyEvent.DispatcherState getKeyDispatcherState() { + return mDispatcherState; + } + + /** * Called by the framework during initialization, when the InputMethod * interface for this service needs to be created. */ diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6ee92ce..5499bba 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -554,7 +554,7 @@ public class InputMethodService extends AbstractInputMethodService { mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); mInflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); - mWindow = new SoftInputWindow(this, mTheme); + mWindow = new SoftInputWindow(this, mTheme, mDispatcherState); initViews(); mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); } @@ -1557,6 +1557,28 @@ public class InputMethodService extends AbstractInputMethodService { mImm.showSoftInputFromInputMethod(mToken, flags); } + private boolean handleBack(boolean doIt) { + if (mShowInputRequested) { + // If the soft input area is shown, back closes it and we + // consume the back key. + if (doIt) requestHideSelf(0); + return true; + } else if (mWindowVisible) { + if (mCandidatesVisibility == View.VISIBLE) { + // If we are showing candidates even if no input area, then + // hide them. + if (doIt) setCandidatesViewShown(false); + } else { + // If we have the window visible for some other reason -- + // most likely to show candidates -- then just get rid + // of it. This really shouldn't happen, but just in case... + if (doIt) hideWindow(); + } + return true; + } + return false; + } + /** * Override this to intercept key down events before they are processed by the * application. If you return true, the application will not itself @@ -1564,38 +1586,33 @@ public class InputMethodService extends AbstractInputMethodService { * will occur as if the IME had not seen the event at all. * * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK - * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In - * additional, in fullscreen mode only, it will consume DPAD movement + * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to + * possibly hide it when the key goes up (if not canceled or long pressed). In + * addition, in fullscreen mode only, it will consume DPAD movement * events to move the cursor in the extracted text view, not allowing * them to perform navigation in the underlying application. */ public boolean onKeyDown(int keyCode, KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_BACK - && event.getRepeatCount() == 0) { - if (mShowInputRequested) { - // If the soft input area is shown, back closes it and we - // consume the back key. - requestHideSelf(0); + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (handleBack(false)) { + event.startTracking(); return true; - } else if (mWindowVisible) { - if (mCandidatesVisibility == View.VISIBLE) { - // If we are showing candidates even if no input area, then - // hide them. - setCandidatesViewShown(false); - return true; - } else { - // If we have the window visible for some other reason -- - // most likely to show candidates -- then just get rid - // of it. This really shouldn't happen, but just in case... - hideWindow(); - return true; - } } + return false; } return doMovementKey(keyCode, event, MOVEMENT_DOWN); } /** + * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent) + * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle + * the event). + */ + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + return false; + } + + /** * Override this to intercept special key multiple events before they are * processed by the * application. If you return true, the application will not itself @@ -1617,12 +1634,18 @@ public class InputMethodService extends AbstractInputMethodService { * process the event. If you return true, the normal application processing * will occur as if the IME had not seen the event at all. * - * <p>The default implementation always returns false, except when - * in fullscreen mode, where it will consume DPAD movement + * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK + * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In + * addition, in fullscreen mode only, it will consume DPAD movement * events to move the cursor in the extracted text view, not allowing * them to perform navigation in the underlying application. */ public boolean onKeyUp(int keyCode, KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isTracking() + && !event.isCanceled()) { + return handleBack(true); + } + return doMovementKey(keyCode, event, MOVEMENT_UP); } @@ -1992,8 +2015,9 @@ public class InputMethodService extends AbstractInputMethodService { req.flags = InputConnection.GET_TEXT_WITH_STYLES; req.hintMaxLines = 10; req.hintMaxChars = 10000; - mExtractedText = getCurrentInputConnection().getExtractedText(req, - InputConnection.GET_EXTRACTED_TEXT_MONITOR); + InputConnection ic = getCurrentInputConnection(); + mExtractedText = ic == null? null + : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR); final EditorInfo ei = getCurrentInputEditorInfo(); diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java index fea63be..4814b0a 100755 --- a/core/java/android/inputmethodservice/Keyboard.java +++ b/core/java/android/inputmethodservice/Keyboard.java @@ -142,7 +142,7 @@ public class Keyboard { private int[][] mGridNeighbors; private int mProximityThreshold; /** Number of key widths from current touch point to search for nearest keys. */ - private static float SEARCH_DISTANCE = 1.4f; + private static float SEARCH_DISTANCE = 1.8f; /** * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate. diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 9c9c143..0f7ef22 100755 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -30,7 +30,6 @@ import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard.Key; import android.os.Handler; import android.os.Message; -import android.os.SystemClock; import android.util.AttributeSet; import android.util.TypedValue; import android.view.GestureDetector; @@ -38,6 +37,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.PopupWindow; import android.widget.TextView; @@ -163,8 +163,8 @@ public class KeyboardView extends View implements View.OnClickListener { private static final int MSG_REPEAT = 3; private static final int MSG_LONGPRESS = 4; - private static final int DELAY_BEFORE_PREVIEW = 40; - private static final int DELAY_AFTER_PREVIEW = 60; + private static final int DELAY_BEFORE_PREVIEW = 0; + private static final int DELAY_AFTER_PREVIEW = 70; private int mVerticalCorrection; private int mProximityThreshold; @@ -191,6 +191,7 @@ public class KeyboardView extends View implements View.OnClickListener { private int mLastCodeX; private int mLastCodeY; private int mCurrentKey = NOT_A_KEY; + private int mDownKey = NOT_A_KEY; private long mLastKeyTime; private long mCurrentKeyTime; private int[] mKeyIndices = new int[12]; @@ -202,13 +203,21 @@ public class KeyboardView extends View implements View.OnClickListener { private boolean mAbortKey; private Key mInvalidatedKey; private Rect mClipRegion = new Rect(0, 0, 0, 0); - + private boolean mPossiblePoly; + private SwipeTracker mSwipeTracker = new SwipeTracker(); + private int mSwipeThreshold; + private boolean mDisambiguateSwipe; + + // Variables for dealing with multiple pointers + private int mOldPointerCount = 1; + private float mOldPointerX; + private float mOldPointerY; + private Drawable mKeyBackground; 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 = 800; - // Deemed to be too short : ViewConfiguration.getLongPressTimeout(); + private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); private static int MAX_NEARBY_KEYS = 12; private int[] mDistances = new int[MAX_NEARBY_KEYS]; @@ -227,6 +236,8 @@ public class KeyboardView extends View implements View.OnClickListener { private Rect mDirtyRect = new Rect(); /** The keyboard bitmap for faster updates */ private Bitmap mBuffer; + /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */ + private boolean mKeyboardChanged; /** The canvas for the above mutable keyboard bitmap */ private Canvas mCanvas; @@ -340,11 +351,15 @@ public class KeyboardView extends View implements View.OnClickListener { mPaint.setAntiAlias(true); mPaint.setTextSize(keyTextSize); mPaint.setTextAlign(Align.CENTER); + mPaint.setAlpha(255); mPadding = new Rect(0, 0, 0, 0); mMiniKeyboardCache = new HashMap<Key,View>(); mKeyBackground.getPadding(mPadding); - + + mSwipeThreshold = (int) (500 * getResources().getDisplayMetrics().density); + mDisambiguateSwipe = getResources().getBoolean( + com.android.internal.R.bool.config_swipeDisambiguation); resetMultiTap(); initGestureDetector(); } @@ -354,22 +369,49 @@ public class KeyboardView extends View implements View.OnClickListener { @Override public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { + if (mPossiblePoly) return false; final float absX = Math.abs(velocityX); final float absY = Math.abs(velocityY); - if (velocityX > 500 && absY < absX) { - swipeRight(); - return true; - } else if (velocityX < -500 && absY < absX) { - swipeLeft(); - return true; - } else if (velocityY < -500 && absX < absY) { - swipeUp(); - return true; - } else if (velocityY > 500 && absX < 200) { - swipeDown(); - return true; - } else if (absX > 800 || absY > 800) { - return true; + float deltaX = me2.getX() - me1.getX(); + float deltaY = me2.getY() - me1.getY(); + int travelX = getWidth() / 2; // Half the keyboard width + int travelY = getHeight() / 2; // Half the keyboard height + mSwipeTracker.computeCurrentVelocity(1000); + final float endingVelocityX = mSwipeTracker.getXVelocity(); + final float endingVelocityY = mSwipeTracker.getYVelocity(); + boolean sendDownKey = false; + if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) { + if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) { + sendDownKey = true; + } else { + swipeRight(); + return true; + } + } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) { + if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) { + sendDownKey = true; + } else { + swipeLeft(); + return true; + } + } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) { + if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) { + sendDownKey = true; + } else { + swipeUp(); + return true; + } + } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) { + if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) { + sendDownKey = true; + } else { + swipeDown(); + return true; + } + } + + if (sendDownKey) { + detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime()); } return false; } @@ -401,16 +443,20 @@ public class KeyboardView extends View implements View.OnClickListener { if (mKeyboard != null) { showPreview(NOT_A_KEY); } + // Remove any pending messages + removeMessages(); mKeyboard = keyboard; List<Key> keys = mKeyboard.getKeys(); mKeys = keys.toArray(new Key[keys.size()]); requestLayout(); - // Release buffer, just in case the new keyboard has a different size. - // It will be reallocated on the next draw. - mBuffer = null; + // Hint to reallocate the buffer if the size changed + mKeyboardChanged = true; invalidateAllKeys(); computeProximityThreshold(keyboard); mMiniKeyboardCache.clear(); // Not really necessary to do every time, but will free up views + // Switching to a different keyboard should abort any pending keys so that the key up + // doesn't get delivered to the old or new keyboard + mAbortKey = true; // Until the next ACTION_DOWN } /** @@ -564,17 +610,21 @@ public class KeyboardView extends View implements View.OnClickListener { @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - if (mDrawPending || mBuffer == null) { + if (mDrawPending || mBuffer == null || mKeyboardChanged) { onBufferDraw(); } canvas.drawBitmap(mBuffer, 0, 0, null); } - + private void onBufferDraw() { - if (mBuffer == null) { - mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); - mCanvas = new Canvas(mBuffer); + if (mBuffer == null || mKeyboardChanged) { + if (mBuffer == null || mKeyboardChanged && + (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) { + mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + mCanvas = new Canvas(mBuffer); + } invalidateAllKeys(); + mKeyboardChanged = false; } final Canvas canvas = mCanvas; canvas.clipRect(mDirtyRect, Op.REPLACE); @@ -590,7 +640,6 @@ public class KeyboardView extends View implements View.OnClickListener { final Key[] keys = mKeys; final Key invalidKey = mInvalidatedKey; - paint.setAlpha(255); paint.setColor(mKeyTextColor); boolean drawSingleKey = false; if (invalidKey != null && canvas.getClipBounds(clipRegion)) { @@ -611,7 +660,7 @@ public class KeyboardView extends View implements View.OnClickListener { } int[] drawableState = key.getCurrentDrawableState(); keyBackground.setState(drawableState); - + // Switch the character to uppercase if shift is pressed String label = key.label == null? null : adjustCase(key.label).toString(); @@ -680,7 +729,6 @@ public class KeyboardView extends View implements View.OnClickListener { private int getKeyIndices(int x, int y, int[] allKeys) { final Key[] keys = mKeys; - final boolean shifted = mKeyboard.isShifted(); int primaryIndex = NOT_A_KEY; int closestKey = NOT_A_KEY; int closestKeyDist = mProximityThreshold + 1; @@ -730,8 +778,7 @@ public class KeyboardView extends View implements View.OnClickListener { return primaryIndex; } - private void detectAndSendKey(int x, int y, long eventTime) { - int index = mCurrentKey; + private void detectAndSendKey(int index, int x, int y, long eventTime) { if (index != NOT_A_KEY && index < mKeys.length) { final Key key = mKeys[index]; if (key.text != null) { @@ -817,6 +864,7 @@ public class KeyboardView extends View implements View.OnClickListener { private void showKey(final int keyIndex) { final PopupWindow previewPopup = mPreviewPopup; final Key[] keys = mKeys; + if (keyIndex < 0 || keyIndex >= mKeys.length) return; Key key = keys[keyIndex]; if (key.icon != null) { mPreviewText.setCompoundDrawables(null, null, null, @@ -1011,19 +1059,69 @@ public class KeyboardView extends View implements View.OnClickListener { } return false; } - + + private long mOldEventTime; + private boolean mUsedVelocity; + @Override public boolean onTouchEvent(MotionEvent me) { + // Convert multi-pointer up/down events to single up/down events to + // deal with the typical multi-pointer behavior of two-thumb typing + final int pointerCount = me.getPointerCount(); + final int action = me.getAction(); + boolean result = false; + final long now = me.getEventTime(); + + if (pointerCount != mOldPointerCount) { + if (pointerCount == 1) { + // Send a down event for the latest pointer + MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, + me.getX(), me.getY(), me.getMetaState()); + result = onModifiedTouchEvent(down, false); + down.recycle(); + // If it's an up action, then deliver the up as well. + if (action == MotionEvent.ACTION_UP) { + result = onModifiedTouchEvent(me, true); + } + } else { + // Send an up event for the last pointer + MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, + mOldPointerX, mOldPointerY, me.getMetaState()); + result = onModifiedTouchEvent(up, true); + up.recycle(); + } + } else { + if (pointerCount == 1) { + result = onModifiedTouchEvent(me, false); + mOldPointerX = me.getX(); + mOldPointerY = me.getY(); + } else { + // Don't do anything when 2 pointers are down and moving. + result = true; + } + } + mOldPointerCount = pointerCount; + + return result; + } + + private boolean onModifiedTouchEvent(MotionEvent me, boolean possiblePoly) { int touchX = (int) me.getX() - mPaddingLeft; int touchY = (int) me.getY() + mVerticalCorrection - mPaddingTop; - int action = me.getAction(); - long eventTime = me.getEventTime(); + final int action = me.getAction(); + final long eventTime = me.getEventTime(); + mOldEventTime = eventTime; int keyIndex = getKeyIndices(touchX, touchY, null); - + mPossiblePoly = possiblePoly; + + // Track the last few movements to look for spurious swipes. + if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear(); + mSwipeTracker.addMovement(me); + if (mGestureDetector.onTouchEvent(me)) { showPreview(NOT_A_KEY); mHandler.removeMessages(MSG_REPEAT); - mHandler.removeMessages(MSG_LONGPRESS); + mHandler.removeMessages(MSG_LONGPRESS); return true; } @@ -1044,6 +1142,7 @@ public class KeyboardView extends View implements View.OnClickListener { mCurrentKeyTime = 0; mLastKey = NOT_A_KEY; mCurrentKey = keyIndex; + mDownKey = keyIndex; mDownTime = me.getEventTime(); mLastMoveTime = mDownTime; checkMultiTap(eventTime, keyIndex); @@ -1072,7 +1171,7 @@ public class KeyboardView extends View implements View.OnClickListener { if (keyIndex == mCurrentKey) { mCurrentKeyTime += eventTime - mLastMoveTime; continueLongPress = true; - } else { + } else if (mRepeatKeyIndex == NOT_A_KEY) { resetMultiTap(); mLastKey = mCurrentKey; mLastCodeX = mLastX; @@ -1083,10 +1182,6 @@ public class KeyboardView extends View implements View.OnClickListener { mCurrentKeyTime = 0; } } - if (keyIndex != mRepeatKeyIndex) { - mHandler.removeMessages(MSG_REPEAT); - mRepeatKeyIndex = NOT_A_KEY; - } } if (!continueLongPress) { // Cancel old longpress @@ -1097,13 +1192,11 @@ public class KeyboardView extends View implements View.OnClickListener { mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT); } } - showPreview(keyIndex); + showPreview(mCurrentKey); break; case MotionEvent.ACTION_UP: - mHandler.removeMessages(MSG_SHOW_PREVIEW); - mHandler.removeMessages(MSG_REPEAT); - mHandler.removeMessages(MSG_LONGPRESS); + removeMessages(); if (keyIndex == mCurrentKey) { mCurrentKeyTime += eventTime - mLastMoveTime; } else { @@ -1122,11 +1215,17 @@ public class KeyboardView extends View implements View.OnClickListener { Arrays.fill(mKeyIndices, NOT_A_KEY); // If we're not on a repeating key (which sends on a DOWN event) if (mRepeatKeyIndex == NOT_A_KEY && !mMiniKeyboardOnScreen && !mAbortKey) { - detectAndSendKey(touchX, touchY, eventTime); + detectAndSendKey(mCurrentKey, touchX, touchY, eventTime); } invalidateKey(keyIndex); mRepeatKeyIndex = NOT_A_KEY; break; + case MotionEvent.ACTION_CANCEL: + removeMessages(); + mAbortKey = true; + showPreview(NOT_A_KEY); + invalidateKey(mCurrentKey); + break; } mLastX = touchX; mLastY = touchY; @@ -1135,7 +1234,7 @@ public class KeyboardView extends View implements View.OnClickListener { private boolean repeatKey() { Key key = mKeys[mRepeatKeyIndex]; - detectAndSendKey(key.x, key.y, mLastTapTime); + detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime); return true; } @@ -1159,16 +1258,20 @@ public class KeyboardView extends View implements View.OnClickListener { if (mPreviewPopup.isShowing()) { mPreviewPopup.dismiss(); } - mHandler.removeMessages(MSG_REPEAT); - mHandler.removeMessages(MSG_LONGPRESS); - mHandler.removeMessages(MSG_SHOW_PREVIEW); + removeMessages(); dismissPopupKeyboard(); mBuffer = null; mCanvas = null; mMiniKeyboardCache.clear(); } - + + private void removeMessages() { + mHandler.removeMessages(MSG_REPEAT); + mHandler.removeMessages(MSG_LONGPRESS); + mHandler.removeMessages(MSG_SHOW_PREVIEW); + } + @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); @@ -1216,4 +1319,114 @@ public class KeyboardView extends View implements View.OnClickListener { resetMultiTap(); } } + + private static class SwipeTracker { + + static final int NUM_PAST = 4; + static final int LONGEST_PAST_TIME = 200; + + final float mPastX[] = new float[NUM_PAST]; + final float mPastY[] = new float[NUM_PAST]; + final long mPastTime[] = new long[NUM_PAST]; + + float mYVelocity; + float mXVelocity; + + public void clear() { + mPastTime[0] = 0; + } + + public void addMovement(MotionEvent ev) { + long time = ev.getEventTime(); + final int N = ev.getHistorySize(); + for (int i=0; i<N; i++) { + addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i), + ev.getHistoricalEventTime(i)); + } + addPoint(ev.getX(), ev.getY(), time); + } + + private void addPoint(float x, float y, long time) { + int drop = -1; + int i; + final long[] pastTime = mPastTime; + for (i=0; i<NUM_PAST; i++) { + if (pastTime[i] == 0) { + break; + } else if (pastTime[i] < time-LONGEST_PAST_TIME) { + drop = i; + } + } + if (i == NUM_PAST && drop < 0) { + drop = 0; + } + if (drop == i) drop--; + final float[] pastX = mPastX; + final float[] pastY = mPastY; + if (drop >= 0) { + final int start = drop+1; + final int count = NUM_PAST-drop-1; + System.arraycopy(pastX, start, pastX, 0, count); + System.arraycopy(pastY, start, pastY, 0, count); + System.arraycopy(pastTime, start, pastTime, 0, count); + i -= (drop+1); + } + pastX[i] = x; + pastY[i] = y; + pastTime[i] = time; + i++; + if (i < NUM_PAST) { + pastTime[i] = 0; + } + } + + public void computeCurrentVelocity(int units) { + computeCurrentVelocity(units, Float.MAX_VALUE); + } + + public void computeCurrentVelocity(int units, float maxVelocity) { + final float[] pastX = mPastX; + final float[] pastY = mPastY; + final long[] pastTime = mPastTime; + + final float oldestX = pastX[0]; + final float oldestY = pastY[0]; + final long oldestTime = pastTime[0]; + float accumX = 0; + float accumY = 0; + int N=0; + while (N < NUM_PAST) { + if (pastTime[N] == 0) { + break; + } + N++; + } + + for (int i=1; i < N; i++) { + final int dur = (int)(pastTime[i] - oldestTime); + if (dur == 0) continue; + float dist = pastX[i] - oldestX; + float vel = (dist/dur) * units; // pixels/frame. + if (accumX == 0) accumX = vel; + else accumX = (accumX + vel) * .5f; + + dist = pastY[i] - oldestY; + vel = (dist/dur) * units; // pixels/frame. + if (accumY == 0) accumY = vel; + else accumY = (accumY + vel) * .5f; + } + mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) + : Math.min(accumX, maxVelocity); + mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) + : Math.min(accumY, maxVelocity); + } + + public float getXVelocity() { + return mXVelocity; + } + + public float getYVelocity() { + return mYVelocity; + } + } } diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index d91ace6..6a54846 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.os.IBinder; import android.view.Gravity; +import android.view.KeyEvent; import android.view.WindowManager; /** @@ -30,7 +31,8 @@ import android.view.WindowManager; * always visible. */ class SoftInputWindow extends Dialog { - + final KeyEvent.DispatcherState mDispatcherState; + public void setToken(IBinder token) { WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.token = token; @@ -49,11 +51,19 @@ class SoftInputWindow extends Dialog { * using styles. This theme is applied on top of the current theme in * <var>context</var>. If 0, the default dialog theme will be used. */ - public SoftInputWindow(Context context, int theme) { + public SoftInputWindow(Context context, int theme, + KeyEvent.DispatcherState dispatcherState) { super(context, theme); + mDispatcherState = dispatcherState; initDockWindow(); } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + mDispatcherState.reset(); + } + /** * Get the size of the DockWindow. * |