Touch pad improvements.
Bug: 4124987
Only show one spot per touch point instead of one spot per
finger for multitouch gestures.
Tweaked the pointer acceleration curves.
Dissociated the hover/tap timeouts from the "tap" timeout
since they mean very different things.
Change-Id: I7c2cbd30feeb65ebc12f6c7e33a67dc9a9f59d4c
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 739758c..392797c 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -111,7 +111,21 @@
* double-tap.
*/
private static final int DOUBLE_TAP_TIMEOUT = 300;
-
+
+ /**
+ * Defines the maximum duration in milliseconds between a touch pad
+ * touch and release for a given touch to be considered a tap (click) as
+ * opposed to a hover movement gesture.
+ */
+ private static final int HOVER_TAP_TIMEOUT = 150;
+
+ /**
+ * Defines the maximum distance in pixels that a touch pad touch can move
+ * before being released for it to be considered a tap (click) as opposed
+ * to a hover movement gesture.
+ */
+ private static final int HOVER_TAP_SLOP = 20;
+
/**
* Defines the duration in milliseconds we want to display zoom controls in response
* to a user panning within an application.
@@ -360,7 +374,7 @@
public static int getTapTimeout() {
return TAP_TIMEOUT;
}
-
+
/**
* @return the duration in milliseconds we will wait to see if a touch event
* is a jump tap. If the user does not move within this interval, it is
@@ -378,7 +392,27 @@
public static int getDoubleTapTimeout() {
return DOUBLE_TAP_TIMEOUT;
}
-
+
+ /**
+ * @return the maximum duration in milliseconds between a touch pad
+ * touch and release for a given touch to be considered a tap (click) as
+ * opposed to a hover movement gesture.
+ * @hide
+ */
+ public static int getHoverTapTimeout() {
+ return HOVER_TAP_TIMEOUT;
+ }
+
+ /**
+ * @return the maximum distance in pixels that a touch pad touch can move
+ * before being released for it to be considered a tap (click) as opposed
+ * to a hover movement gesture.
+ * @hide
+ */
+ public static int getHoverTapSlop() {
+ return HOVER_TAP_SLOP;
+ }
+
/**
* @return Inset in pixels to look for touchable content when the user touches the edge of the
* screen
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index a22ec1c..36a5f89 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -36,6 +36,12 @@
// Log debug messages about gesture detection.
#define DEBUG_GESTURES 0
+// Specifies whether spots follow fingers or touch points.
+// If 1, show exactly one spot per finger in multitouch gestures.
+// If 0, show exactly one spot per generated touch point in multitouch gestures, so the
+// spots indicate exactly which points on screen are being touched.
+#define SPOT_FOLLOWS_FINGER 0
+
#include "InputReader.h"
#include <cutils/atomic.h>
@@ -186,23 +192,6 @@
return edgeFlags;
}
-static void clampPositionUsingPointerBounds(
- const sp<PointerControllerInterface>& pointerController, float* x, float* y) {
- float minX, minY, maxX, maxY;
- if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
- if (*x < minX) {
- *x = minX;
- } else if (*x > maxX) {
- *x = maxX;
- }
- if (*y < minY) {
- *y = minY;
- } else if (*y > maxY) {
- *y = maxY;
- }
- }
-}
-
static float calculateCommonVector(float a, float b) {
if (a > 0 && b > 0) {
return a < b ? a : b;
@@ -746,8 +735,8 @@
mConfig.pointerGestureTapSlop);
dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
- dump.appendFormat(INDENT3 "MultitouchMinSpeed: %0.1fpx/s\n",
- mConfig.pointerGestureMultitouchMinSpeed);
+ dump.appendFormat(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
+ mConfig.pointerGestureMultitouchMinDistance);
dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
mConfig.pointerGestureSwipeTransitionAngleCosine);
dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
@@ -3291,11 +3280,18 @@
cancelPreviousGesture = false;
}
- // Switch pointer presentation.
- mPointerController->setPresentation(
- mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
- ? PointerControllerInterface::PRESENTATION_SPOT
- : PointerControllerInterface::PRESENTATION_POINTER);
+ // Update the pointer presentation and spots.
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+ if (finishPreviousGesture || cancelPreviousGesture) {
+ mPointerController->clearSpots();
+ }
+ mPointerController->setSpots(mPointerGesture.spotGesture,
+ mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex,
+ mPointerGesture.spotIdBits);
+ } else {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ }
// Show or hide the pointer if needed.
switch (mPointerGesture.currentGestureMode) {
@@ -3484,7 +3480,6 @@
if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
mPointerGesture.spotIdBits.clear();
- moveSpotsLocked();
}
return true;
}
@@ -3566,10 +3561,12 @@
if (isQuietTime) {
// Case 1: Quiet time. (QUIET)
#if DEBUG_GESTURES
- LOGD("Gestures: QUIET for next %0.3fms",
- (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f);
+ LOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime
+ + mConfig->pointerGestureQuietInterval - when) * 0.000001f);
#endif
- *outFinishPreviousGesture = true;
+ if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+ *outFinishPreviousGesture = true;
+ }
mPointerGesture.activeGestureId = -1;
mPointerGesture.currentGestureMode = PointerGesture::QUIET;
@@ -3580,7 +3577,6 @@
if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
mPointerGesture.spotIdBits.clear();
- moveSpotsLocked();
}
} else if (isPointerDown(mCurrentTouch.buttonState)) {
// Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
@@ -3684,11 +3680,12 @@
mPointerGesture.spotIdToIndex[0] = 0;
mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
}
- moveSpotsLocked();
}
} else if (mCurrentTouch.pointerCount == 0) {
// Case 3. No fingers down and button is not pressed. (NEUTRAL)
- *outFinishPreviousGesture = true;
+ if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+ *outFinishPreviousGesture = true;
+ }
// Watch for taps coming out of HOVER or TAP_DRAG mode.
// Checking for taps after TAP_DRAG allows us to detect double-taps.
@@ -3730,7 +3727,6 @@
mPointerGesture.spotIdBits.markBit(lastActiveTouchId);
mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0;
mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
- moveSpotsLocked();
}
tapped = true;
@@ -3762,7 +3758,6 @@
if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
mPointerGesture.spotIdBits.clear();
- moveSpotsLocked();
}
}
} else if (mCurrentTouch.pointerCount == 1) {
@@ -3826,7 +3821,9 @@
#if DEBUG_GESTURES
LOGD("Gestures: HOVER");
#endif
- *outFinishPreviousGesture = true;
+ if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+ *outFinishPreviousGesture = true;
+ }
mPointerGesture.activeGestureId = 0;
down = false;
}
@@ -3857,7 +3854,6 @@
mPointerGesture.spotIdBits.markBit(activeTouchId);
mPointerGesture.spotIdToIndex[activeTouchId] = 0;
mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
- moveSpotsLocked();
}
} else {
// Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
@@ -3886,8 +3882,8 @@
// Reset the gesture.
#if DEBUG_GESTURES
LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
- "settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
+ "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
+ + mConfig->pointerGestureMultitouchSettleInterval - when)
* 0.000001f);
#endif
*outCancelPreviousGesture = true;
@@ -3908,8 +3904,8 @@
// for the gesture. Other spots will be positioned relative to this one.
#if DEBUG_GESTURES
LOGD("Gestures: Using active spot as reference for MULTITOUCH, "
- "settle time expired %0.3fms ago",
- (when - mPointerGesture.firstTouchTime - MULTITOUCH_SETTLE_INTERVAL)
+ "settle time expired %0.3fms ago", (when - mPointerGesture.firstTouchTime
+ - mConfig->pointerGestureMultitouchSettleInterval)
* 0.000001f);
#endif
const PointerData& d = mLastTouch.pointers[mLastTouch.idToIndex[
@@ -3924,8 +3920,8 @@
// Use the centroid and pointer location as the reference points for the gesture.
#if DEBUG_GESTURES
LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
- "settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
+ "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
+ + mConfig->pointerGestureMultitouchSettleInterval - when)
* 0.000001f);
#endif
mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
@@ -3935,68 +3931,121 @@
}
}
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
- float d;
- if (mCurrentTouch.pointerCount > 2) {
- // There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
- LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
- mCurrentTouch.pointerCount);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- } else if (((d = distance(
- mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
- mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
- > mLocked.pointerGestureMaxSwipeWidth)) {
- // There are two pointers but they are too far apart, switch to FREEFORM.
-#if DEBUG_GESTURES
- LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
- d, mLocked.pointerGestureMaxSwipeWidth);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ // Clear the reference deltas for fingers not yet included in the reference calculation.
+ for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
+ !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+
+ mPointerGesture.referenceDeltas[id].dx = 0;
+ mPointerGesture.referenceDeltas[id].dy = 0;
+ }
+ mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
+
+ // Add delta for all fingers and calculate a common movement delta.
+ float commonDeltaX = 0, commonDeltaY = 0;
+ BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
+ for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
+ bool first = (idBits == commonIdBits);
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+
+ const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
+ const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ delta.dx += cpd.x - lpd.x;
+ delta.dy += cpd.y - lpd.y;
+
+ if (first) {
+ commonDeltaX = delta.dx;
+ commonDeltaY = delta.dy;
} else {
- // There are two pointers. Wait for both pointers to start moving
- // before deciding whether this is a SWIPE or FREEFORM gesture.
- uint32_t id1 = mCurrentTouch.pointers[0].id;
- uint32_t id2 = mCurrentTouch.pointers[1].id;
+ commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
+ commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
+ }
+ }
- float vx1, vy1, vx2, vy2;
- mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1);
- mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2);
+ // Consider transitions from PRESS to SWIPE or MULTITOUCH.
+ if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
+ float dist[MAX_POINTER_ID + 1];
+ int32_t distOverThreshold = 0;
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
- float speed1 = hypotf(vx1, vy1);
- float speed2 = hypotf(vx2, vy2);
- if (speed1 >= mConfig->pointerGestureMultitouchMinSpeed
- && speed2 >= mConfig->pointerGestureMultitouchMinSpeed) {
- // Calculate the dot product of the velocity vectors.
- // When the vectors are oriented in approximately the same direction,
- // the angle betweeen them is near zero and the cosine of the angle
- // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
- float dot = vx1 * vx2 + vy1 * vy2;
- float cosine = dot / (speed1 * speed2); // denominator always > 0
- if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
- // Pointers are moving in the same direction. Switch to SWIPE.
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale,
+ delta.dy * mLocked.pointerGestureYZoomScale);
+ if (dist[id] > mConfig->pointerGestureMultitouchMinDistance) {
+ distOverThreshold += 1;
+ }
+ }
+
+ // Only transition when at least two pointers have moved further than
+ // the minimum distance threshold.
+ if (distOverThreshold >= 2) {
+ float d;
+ if (mCurrentTouch.pointerCount > 2) {
+ // There are more than two pointers, switch to FREEFORM.
#if DEBUG_GESTURES
- LOGD("Gestures: PRESS transitioned to SWIPE, "
- "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
- "cosine %0.3f >= %0.3f",
- speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
- cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+ LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+ mCurrentTouch.pointerCount);
#endif
- mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
- } else {
- // Pointers are moving in different directions. Switch to FREEFORM.
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ } else if (((d = distance(
+ mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
+ mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
+ > mLocked.pointerGestureMaxSwipeWidth)) {
+ // There are two pointers but they are too far apart for a SWIPE,
+ // switch to FREEFORM.
#if DEBUG_GESTURES
- LOGD("Gestures: PRESS transitioned to FREEFORM, "
- "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
- "cosine %0.3f < %0.3f",
- speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
- cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+ LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+ d, mLocked.pointerGestureMaxSwipeWidth);
#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ } else {
+ // There are two pointers. Wait for both pointers to start moving
+ // before deciding whether this is a SWIPE or FREEFORM gesture.
+ uint32_t id1 = mCurrentTouch.pointers[0].id;
+ uint32_t id2 = mCurrentTouch.pointers[1].id;
+ float dist1 = dist[id1];
+ float dist2 = dist[id2];
+ if (dist1 >= mConfig->pointerGestureMultitouchMinDistance
+ && dist2 >= mConfig->pointerGestureMultitouchMinDistance) {
+ // Calculate the dot product of the displacement vectors.
+ // When the vectors are oriented in approximately the same direction,
+ // the angle betweeen them is near zero and the cosine of the angle
+ // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
+ PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+ PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+ float dot = delta1.dx * delta2.dx + delta1.dy * delta2.dy;
+ float cosine = dot / (dist1 * dist2); // denominator always > 0
+ if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
+ // Pointers are moving in the same direction. Switch to SWIPE.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS transitioned to SWIPE, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f >= %0.3f",
+ dist1, mConfig->pointerGestureMultitouchMinDistance,
+ dist2, mConfig->pointerGestureMultitouchMinDistance,
+ cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+#endif
+ mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+ } else {
+ // Pointers are moving in different directions. Switch to FREEFORM.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS transitioned to FREEFORM, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f < %0.3f",
+ dist1, mConfig->pointerGestureMultitouchMinDistance,
+ dist2, mConfig->pointerGestureMultitouchMinDistance,
+ cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ }
}
}
}
@@ -4013,67 +4062,28 @@
}
}
- // Clear the reference deltas for fingers not yet included in the reference calculation.
- for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
- !idBits.isEmpty(); ) {
- uint32_t id = idBits.firstMarkedBit();
- idBits.clearBit(id);
-
- mPointerGesture.referenceDeltas[id].dx = 0;
- mPointerGesture.referenceDeltas[id].dy = 0;
- }
- mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
-
- // Move the reference points based on the overall group motion of the fingers.
- // The objective is to calculate a vector delta that is common to the movement
- // of all fingers.
- BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
- if (!commonIdBits.isEmpty()) {
- float commonDeltaX = 0, commonDeltaY = 0;
- for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
- bool first = (idBits == commonIdBits);
+ // Move the reference points based on the overall group motion of the fingers
+ // except in PRESS mode while waiting for a transition to occur.
+ if (mPointerGesture.currentGestureMode != PointerGesture::PRESS
+ && (commonDeltaX || commonDeltaY)) {
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
- const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
- const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx += cpd.x - lpd.x;
- delta.dy += cpd.y - lpd.y;
-
- if (first) {
- commonDeltaX = delta.dx;
- commonDeltaY = delta.dy;
- } else {
- commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
- commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
- }
+ delta.dx = 0;
+ delta.dy = 0;
}
- if (commonDeltaX || commonDeltaY) {
- for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.firstMarkedBit();
- idBits.clearBit(id);
+ mPointerGesture.referenceTouchX += commonDeltaX;
+ mPointerGesture.referenceTouchY += commonDeltaY;
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx = 0;
- delta.dy = 0;
- }
+ commonDeltaX *= mLocked.pointerGestureXMovementScale;
+ commonDeltaY *= mLocked.pointerGestureYMovementScale;
+ mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
- mPointerGesture.referenceTouchX += commonDeltaX;
- mPointerGesture.referenceTouchY += commonDeltaY;
-
- commonDeltaX *= mLocked.pointerGestureXMovementScale;
- commonDeltaY *= mLocked.pointerGestureYMovementScale;
- mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
- mPointerGesture.referenceGestureX += commonDeltaX;
- mPointerGesture.referenceGestureY += commonDeltaY;
-
- clampPositionUsingPointerBounds(mPointerController,
- &mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
- }
+ mPointerGesture.referenceGestureX += commonDeltaX;
+ mPointerGesture.referenceGestureY += commonDeltaY;
}
// Report gestures.
@@ -4225,9 +4235,10 @@
}
// Update spot locations for PRESS, SWIPE and FREEFORM.
- // We use the same calculation as we do to calculate the gesture pointers
- // for FREEFORM so that the spots smoothly track gestures.
if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+#if SPOT_FOLLOWS_FINGER
+ // Use the same calculation as we do to calculate the gesture pointers
+ // for FREEFORM so that the spots smoothly track fingers across gestures.
mPointerGesture.spotIdBits.clear();
for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
uint32_t id = mCurrentTouch.pointers[i].id;
@@ -4244,7 +4255,19 @@
mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
}
- moveSpotsLocked();
+#else
+ // Show one spot per generated touch point.
+ // This may cause apparent discontinuities in spot motion when transitioning
+ // from PRESS to FREEFORM.
+ mPointerGesture.spotIdBits = mPointerGesture.currentGestureIdBits;
+ for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+ mPointerGesture.spotIdToIndex[id] = index;
+ mPointerGesture.spotCoords[index] = mPointerGesture.currentGestureCoords[index];
+ }
+#endif
}
}
@@ -4281,11 +4304,6 @@
return true;
}
-void TouchInputMapper::moveSpotsLocked() {
- mPointerController->setSpots(mPointerGesture.spotGesture,
- mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex, mPointerGesture.spotIdBits);
-}
-
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
int32_t action, int32_t flags, uint32_t metaState, int32_t edgeFlags,
const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 5028b60..9bb37874 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -101,8 +101,8 @@
nsecs_t pointerGestureMultitouchSettleInterval;
// The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
- // both of the pointers are moving at least this fast.
- float pointerGestureMultitouchMinSpeed; // in pixels per second
+ // at least two pointers have moved at least this far from their starting place.
+ float pointerGestureMultitouchMinDistance; // in pixels
// The transition from PRESS to SWIPE gesture mode can only occur when the
// cosine of the angle between the two vectors is greater than or equal to than this value
@@ -134,7 +134,7 @@
filterTouchEvents(false),
filterJumpyTouchEvents(false),
virtualKeyQuietTime(0),
- pointerVelocityControlParameters(1.0f, 80.0f, 400.0f, 4.0f),
+ pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
@@ -142,10 +142,10 @@
pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
pointerGestureTapSlop(10.0f), // 10 pixels
pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
- pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second
+ pointerGestureMultitouchMinDistance(15), // 15 pixels
pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees
- pointerGestureSwipeMaxWidthRatio(0.333f),
- pointerGestureMovementSpeedRatio(0.3f),
+ pointerGestureSwipeMaxWidthRatio(0.25f),
+ pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f) { }
};
@@ -1192,7 +1192,6 @@
void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
bool preparePointerGestures(nsecs_t when,
bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout);
- void moveSpotsLocked();
// Dispatches a motion event.
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 4eda684..3b01217 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -540,8 +540,13 @@
}
@SuppressWarnings("unused")
- public int getTapTimeout() {
- return ViewConfiguration.getTapTimeout();
+ public int getHoverTapTimeout() {
+ return ViewConfiguration.getHoverTapTimeout();
+ }
+
+ @SuppressWarnings("unused")
+ public int getHoverTapSlop() {
+ return ViewConfiguration.getHoverTapSlop();
}
@SuppressWarnings("unused")
@@ -555,11 +560,6 @@
}
@SuppressWarnings("unused")
- public int getTouchSlop() {
- return ViewConfiguration.get(mContext).getScaledTouchSlop();
- }
-
- @SuppressWarnings("unused")
public int getMaxEventsPerSecond() {
int result = 0;
try {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 2704647..608ea1a 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -56,7 +56,7 @@
// The exponent used to calculate the pointer speed scaling factor.
// The scaling factor is calculated as 2 ^ (speed * exponent),
// where the speed ranges from -7 to + 7 and is supplied by the user.
-static const float POINTER_SPEED_EXPONENT = 1.0f / 3;
+static const float POINTER_SPEED_EXPONENT = 1.0f / 4;
static struct {
jclass clazz;
@@ -77,10 +77,10 @@
jmethodID getKeyRepeatTimeout;
jmethodID getKeyRepeatDelay;
jmethodID getMaxEventsPerSecond;
- jmethodID getTapTimeout;
+ jmethodID getHoverTapTimeout;
+ jmethodID getHoverTapSlop;
jmethodID getDoubleTapTimeout;
jmethodID getLongPressTimeout;
- jmethodID getTouchSlop;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
} gCallbacksClassInfo;
@@ -412,32 +412,32 @@
env->DeleteLocalRef(excludedDeviceNames);
}
- jint tapTimeout = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.getTapTimeout);
- if (!checkAndClearExceptionFromCallback(env, "getTapTimeout")) {
+ jint hoverTapTimeout = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.getHoverTapTimeout);
+ if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) {
jint doubleTapTimeout = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.getDoubleTapTimeout);
if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) {
jint longPressTimeout = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.getLongPressTimeout);
if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) {
- outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(tapTimeout);
+ outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout);
// We must ensure that the tap-drag interval is significantly shorter than
// the long-press timeout because the tap is held down for the entire duration
// of the double-tap timeout.
jint tapDragInterval = max(min(longPressTimeout - 100,
- doubleTapTimeout), tapTimeout);
+ doubleTapTimeout), hoverTapTimeout);
outConfig->pointerGestureTapDragInterval =
milliseconds_to_nanoseconds(tapDragInterval);
}
}
}
- jint touchSlop = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.getTouchSlop);
- if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) {
- outConfig->pointerGestureTapSlop = touchSlop;
+ jint hoverTapSlop = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.getHoverTapSlop);
+ if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) {
+ outConfig->pointerGestureTapSlop = hoverTapSlop;
}
{ // acquire lock
@@ -1348,8 +1348,11 @@
GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, gCallbacksClassInfo.clazz,
"getKeyRepeatDelay", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getTapTimeout, gCallbacksClassInfo.clazz,
- "getTapTimeout", "()I");
+ GET_METHOD_ID(gCallbacksClassInfo.getHoverTapTimeout, gCallbacksClassInfo.clazz,
+ "getHoverTapTimeout", "()I");
+
+ GET_METHOD_ID(gCallbacksClassInfo.getHoverTapSlop, gCallbacksClassInfo.clazz,
+ "getHoverTapSlop", "()I");
GET_METHOD_ID(gCallbacksClassInfo.getDoubleTapTimeout, gCallbacksClassInfo.clazz,
"getDoubleTapTimeout", "()I");
@@ -1357,9 +1360,6 @@
GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, gCallbacksClassInfo.clazz,
"getLongPressTimeout", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getTouchSlop, gCallbacksClassInfo.clazz,
- "getTouchSlop", "()I");
-
GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
"getMaxEventsPerSecond", "()I");