am ed48fa89
: Merge "Touch pad improvements. Bug: 4124987" into honeycomb-mr2
* commit 'ed48fa89a8e31b04681347a9235c2a566e7dbb8e': Touch pad improvements. Bug: 4124987
This commit is contained in:
@ -111,7 +111,21 @@ public class ViewConfiguration {
|
||||
* 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 class ViewConfiguration {
|
||||
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 class ViewConfiguration {
|
||||
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
|
||||
|
@ -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 @@ static int32_t calculateEdgeFlagsUsingPointerBounds(
|
||||
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 @@ void InputReader::dump(String8& dump) {
|
||||
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 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag
|
||||
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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
|
||||
mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
|
||||
mPointerGesture.spotIdBits.clear();
|
||||
moveSpotsLocked();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3566,10 +3561,12 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
mPointerGesture.spotIdBits.markBit(lastActiveTouchId);
|
||||
mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0;
|
||||
mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
|
||||
moveSpotsLocked();
|
||||
}
|
||||
|
||||
tapped = true;
|
||||
@ -3762,7 +3758,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
#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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
// 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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
// 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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
// 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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
}
|
||||
|
||||
// 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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
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 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
|
||||
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,
|
||||
|
@ -101,8 +101,8 @@ struct InputReaderConfiguration {
|
||||
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 @@ struct InputReaderConfiguration {
|
||||
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 @@ struct InputReaderConfiguration {
|
||||
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 @@ private:
|
||||
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
|
||||
|
@ -540,8 +540,13 @@ public class InputManager {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int getTapTimeout() {
|
||||
return ViewConfiguration.getTapTimeout();
|
||||
public int getHoverTapTimeout() {
|
||||
return ViewConfiguration.getHoverTapTimeout();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int getHoverTapSlop() {
|
||||
return ViewConfiguration.getHoverTapSlop();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ -554,11 +559,6 @@ public class InputManager {
|
||||
return ViewConfiguration.getLongPressTimeout();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int getTouchSlop() {
|
||||
return ViewConfiguration.get(mContext).getScaledTouchSlop();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int getMaxEventsPerSecond() {
|
||||
int result = 0;
|
||||
|
@ -56,7 +56,7 @@ namespace android {
|
||||
// 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 @@ static struct {
|
||||
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 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
|
||||
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 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
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 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
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");
|
||||
|
||||
|
Reference in New Issue
Block a user