diff options
-rw-r--r-- | core/java/android/view/KeyEvent.java | 13 | ||||
-rw-r--r-- | core/java/android/view/View.java | 26 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 43 |
3 files changed, 61 insertions, 21 deletions
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 73fad08..5a5fc10 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1837,6 +1837,19 @@ public class KeyEvent extends InputEvent implements Parcelable { } } + /** Whether key will, by default, trigger a click on the focused view. + * @hide + */ + public static final boolean isConfirmKey(int keyCode) { + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_CENTER: + case KeyEvent.KEYCODE_ENTER: + return true; + default: + return false; + } + } + /** {@inheritDoc} */ @Override public final int getDeviceId() { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 95a2469..188ddf2 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7945,21 +7945,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public boolean onKeyDown(int keyCode, KeyEvent event) { boolean result = false; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_ENTER: { - if ((mViewFlags & ENABLED_MASK) == DISABLED) { - return true; - } - // Long clickable items don't necessarily have to be clickable - if (((mViewFlags & CLICKABLE) == CLICKABLE || - (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) && - (event.getRepeatCount() == 0)) { - setPressed(true); - checkForLongClick(0); - return true; - } - break; + if (KeyEvent.isConfirmKey(event.getKeyCode())) { + if ((mViewFlags & ENABLED_MASK) == DISABLED) { + return true; + } + // Long clickable items don't necessarily have to be clickable + if (((mViewFlags & CLICKABLE) == CLICKABLE || + (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) && + (event.getRepeatCount() == 0)) { + setPressed(true); + checkForLongClick(0); + return true; } } return result; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b0f67ac..075719c 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -228,6 +228,7 @@ public final class ViewRootImpl implements ViewParent, InputStage mFirstInputStage; InputStage mFirstPostImeInputStage; + SyntheticInputStage mSyntheticInputStage; boolean mWindowAttributesChanged = false; int mWindowAttributesChangesFlag = 0; @@ -589,8 +590,8 @@ public final class ViewRootImpl implements ViewParent, // Set up the input pipeline. CharSequence counterSuffix = attrs.getTitle(); - InputStage syntheticStage = new SyntheticInputStage(); - InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage); + mSyntheticInputStage = new SyntheticInputStage(); + InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, "aq:native-post-ime:" + counterSuffix); InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); @@ -3773,6 +3774,9 @@ public final class ViewRootImpl implements ViewParent, private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; + // The synthetic stage occasionally needs to know about keys in order to debounce taps + mSyntheticInputStage.notifyKeyEvent(event); + // Deliver the key to the view hierarchy. if (mView.dispatchKeyEvent(event)) { return FINISH_HANDLED; @@ -3945,6 +3949,10 @@ public final class ViewRootImpl implements ViewParent, } super.onDeliverToNext(q); } + + public void notifyKeyEvent(KeyEvent e) { + mTouchNavigation.notifyKeyEvent(e); + } } /** @@ -4375,6 +4383,9 @@ public final class ViewRootImpl implements ViewParent, // Tap timeout in milliseconds. private static final int TAP_TIMEOUT = 250; + // Debounce timeout for touch nav devices with a button under their pad, in milliseconds + private static final int DEBOUNCE_TIME = 250; + // The maximum distance traveled for a gesture to be considered a tap in millimeters. private static final int TAP_SLOP_MILLIMETERS = 5; @@ -4409,6 +4420,9 @@ public final class ViewRootImpl implements ViewParent, private int mConfigTapTimeout; private float mConfigTapSlop; + // Amount of time to wait between button presses and tap generation for debouncing + private int mConfigDebounceTime; + // The scaled tick distance. A movement of this amount should generally translate // into a single dpad event in a given direction. private float mConfigTickDistance; @@ -4454,6 +4468,11 @@ public final class ViewRootImpl implements ViewParent, private boolean mFlinging; private float mFlingVelocity; + // The last time a confirm key was pressed on the touch nav device + private long mLastConfirmKeyTime = Long.MAX_VALUE; + + private boolean mHasButtonUnderPad; + public SyntheticTouchNavigationHandler() { super(true); } @@ -4497,6 +4516,8 @@ public final class ViewRootImpl implements ViewParent, MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; mConfigMaxFlingVelocity = MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; + mConfigDebounceTime = DEBOUNCE_TIME; + mHasButtonUnderPad = device.hasButtonUnderPad(); if (LOCAL_DEBUG) { Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId @@ -4567,10 +4588,13 @@ public final class ViewRootImpl implements ViewParent, if (!mConsumedMovement && Math.hypot(mLastX - mStartX, mLastY - mStartY) < mConfigTapSlop && time <= mStartTime + mConfigTapTimeout) { - // It's a tap! - finishKeys(time); - sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState); - sendKeyUp(time); + if (!mHasButtonUnderPad || + time >= mLastConfirmKeyTime + mConfigDebounceTime) { + // It's a tap! + finishKeys(time); + sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState); + sendKeyUp(time); + } } else if (mConsumedMovement && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { // It might be a fling. @@ -4603,6 +4627,13 @@ public final class ViewRootImpl implements ViewParent, } } + public void notifyKeyEvent(KeyEvent e) { + final int keyCode = e.getKeyCode(); + if (KeyEvent.isConfirmKey(e.getKeyCode())) { + mLastConfirmKeyTime = e.getDownTime(); + } + } + private void finishKeys(long time) { cancelFling(); sendKeyUp(time); |