am 510caf30: Merge "Add doubletap swipe to scalegesturedetector" into klp-dev

* commit '510caf30d2f7b240ee0cb470f52299af8576ea8d':
  Add doubletap swipe to scalegesturedetector
This commit is contained in:
Mindy Pereira
2013-09-03 10:49:12 -07:00
committed by Android Git Automerger
2 changed files with 120 additions and 13 deletions

View File

@@ -27175,6 +27175,7 @@ package android.view {
method public long getTimeDelta();
method public boolean isInProgress();
method public boolean onTouchEvent(android.view.MotionEvent);
method public void setQuickScaleEnabled(boolean);
}
public static abstract interface ScaleGestureDetector.OnScaleGestureListener {

View File

@@ -18,6 +18,8 @@ package android.view;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.util.FloatMath;
@@ -128,6 +130,8 @@ public class ScaleGestureDetector {
private float mFocusX;
private float mFocusY;
private boolean mDoubleTapScales;
private float mCurrSpan;
private float mPrevSpan;
private float mInitialSpan;
@@ -148,9 +152,14 @@ public class ScaleGestureDetector {
private int mTouchHistoryDirection;
private long mTouchHistoryLastAcceptedTime;
private int mTouchMinMajor;
private MotionEvent mDoubleTapEvent;
private int mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
private final Handler mHandler;
private static final long TOUCH_STABILIZE_TIME = 128; // ms
private static final int TOUCH_MIN_MAJOR = 48; // dp
private static final int DOUBLE_TAP_MODE_NONE = 0;
private static final int DOUBLE_TAP_MODE_IN_PROGRESS = 1;
/**
* Consistency verifier for debugging purposes.
@@ -158,8 +167,37 @@ public class ScaleGestureDetector {
private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
private GestureDetector mGestureDetector;
private boolean mEventBeforeOrAboveStartingGestureEvent;
/**
* Creates a ScaleGestureDetector with the supplied listener.
* You may only use this constructor from a {@link android.os.Looper Looper} thread.
*
* @param context the application's context
* @param listener the listener invoked for all the callbacks, this must
* not be null.
*
* @throws NullPointerException if {@code listener} is null.
*/
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
this(context, listener, null);
}
/**
* Creates a ScaleGestureDetector with the supplied listener.
* @see android.os.Handler#Handler()
*
* @param context the application's context
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use for running deferred listener events.
*
* @throws NullPointerException if {@code listener} is null.
*/
public ScaleGestureDetector(Context context, OnScaleGestureListener listener,
Handler handler) {
mContext = context;
mListener = listener;
mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
@@ -167,8 +205,12 @@ public class ScaleGestureDetector {
final Resources res = context.getResources();
mTouchMinMajor = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_minScalingTouchMajor);
mMinSpan = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_minScalingSpan);
mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan);
mHandler = handler;
// Quick scale is enabled by default after JB_MR2
if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
setQuickScaleEnabled(true);
}
}
/**
@@ -263,8 +305,14 @@ public class ScaleGestureDetector {
final int action = event.getActionMasked();
// Forward the event to check for double tap gesture
if (mDoubleTapScales) {
mGestureDetector.onTouchEvent(event);
}
final boolean streamComplete = action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_CANCEL;
if (action == MotionEvent.ACTION_DOWN || streamComplete) {
// Reset any scale in progress with the listener.
// If it's an ACTION_DOWN we're beginning a new event stream.
@@ -273,6 +321,7 @@ public class ScaleGestureDetector {
mListener.onScaleEnd(this);
mInProgress = false;
mInitialSpan = 0;
mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
}
if (streamComplete) {
@@ -284,21 +333,37 @@ public class ScaleGestureDetector {
final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_UP ||
action == MotionEvent.ACTION_POINTER_DOWN;
final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
final int skipIndex = pointerUp ? event.getActionIndex() : -1;
// Determine focal point
float sumX = 0, sumY = 0;
final int count = event.getPointerCount();
for (int i = 0; i < count; i++) {
if (skipIndex == i) continue;
sumX += event.getX(i);
sumY += event.getY(i);
}
final int div = pointerUp ? count - 1 : count;
final float focusX = sumX / div;
final float focusY = sumY / div;
final float focusX;
final float focusY;
if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS) {
// In double tap mode, the focal pt is always where the double tap
// gesture started
focusX = mDoubleTapEvent.getX();
focusY = mDoubleTapEvent.getY();
if (event.getY() < focusY) {
mEventBeforeOrAboveStartingGestureEvent = true;
} else {
mEventBeforeOrAboveStartingGestureEvent = false;
}
} else {
for (int i = 0; i < count; i++) {
if (skipIndex == i) continue;
sumX += event.getX(i);
sumY += event.getY(i);
}
focusX = sumX / div;
focusY = sumY / div;
}
addTouchHistory(event);
@@ -320,7 +385,12 @@ public class ScaleGestureDetector {
// the focal point.
final float spanX = devX * 2;
final float spanY = devY * 2;
final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
final float span;
if (inDoubleTapMode()) {
span = spanY;
} else {
span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
}
// Dispatch begin/end events as needed.
// If the configuration changes, notify the app to reset its current state by beginning
@@ -328,10 +398,11 @@ public class ScaleGestureDetector {
final boolean wasInProgress = mInProgress;
mFocusX = focusX;
mFocusY = focusY;
if (mInProgress && (span < mMinSpan || configChanged)) {
if (!inDoubleTapMode() && mInProgress && (span < mMinSpan || configChanged)) {
mListener.onScaleEnd(this);
mInProgress = false;
mInitialSpan = span;
mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
}
if (configChanged) {
mPrevSpanX = mCurrSpanX = spanX;
@@ -354,6 +425,7 @@ public class ScaleGestureDetector {
mCurrSpan = span;
boolean updatePrev = true;
if (mInProgress) {
updatePrev = mListener.onScale(this);
}
@@ -369,6 +441,34 @@ public class ScaleGestureDetector {
return true;
}
private boolean inDoubleTapMode() {
return mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS;
}
/**
* Set whether the associated {@link OnScaleGestureListener} should receive onScale callbacks
* when the user performs a doubleTap followed by a swipe. Note that this is enabled by default
* if the app targets API 19 and newer.
* @param scales true to enable quick scaling, false to disable
*/
public void setQuickScaleEnabled(boolean scales) {
mDoubleTapScales = scales;
if (mDoubleTapScales && mGestureDetector == null) {
GestureDetector.SimpleOnGestureListener gestureListener =
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
// Double tap: start watching for a swipe
mDoubleTapEvent = e;
mDoubleTapMode = DOUBLE_TAP_MODE_IN_PROGRESS;
return true;
}
};
mGestureDetector = new GestureDetector(mContext, gestureListener, mHandler);
}
}
/**
* Returns {@code true} if a scale gesture is in progress.
*/
@@ -472,6 +572,12 @@ public class ScaleGestureDetector {
* @return The current scaling factor.
*/
public float getScaleFactor() {
if (inDoubleTapMode() && mEventBeforeOrAboveStartingGestureEvent) {
// Drag is moving up; the further away from the gesture
// start, the smaller the span should be, the closer,
// the larger the span, and therefore the larger the scale
return (1 / mCurrSpan) / (1 / mPrevSpan);
}
return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
}
@@ -493,4 +599,4 @@ public class ScaleGestureDetector {
public long getEventTime() {
return mCurrTime;
}
}
}