summaryrefslogtreecommitdiffstats
path: root/core/java/android/gesture
diff options
context:
space:
mode:
authorRomain Guy <romainguy@android.com>2009-05-24 18:40:45 -0700
committerRomain Guy <romainguy@android.com>2009-05-24 23:45:03 -0700
commit82f3495b146b267f3786997752cef25310176349 (patch)
tree1821c5b8731028212250ec07fe209a0804a0e53f /core/java/android/gesture
parentd9a874a4cb8e82ae64c1698bd71ced8d87bbc5cd (diff)
downloadframeworks_base-82f3495b146b267f3786997752cef25310176349.zip
frameworks_base-82f3495b146b267f3786997752cef25310176349.tar.gz
frameworks_base-82f3495b146b267f3786997752cef25310176349.tar.bz2
Cleanup Gestures API and make it easier to use in 3rd party apps. Also fix the events processing in the gestures overlay mechanism. Give better control of the various properties of the overlay through XML attributes.
Diffstat (limited to 'core/java/android/gesture')
-rwxr-xr-xcore/java/android/gesture/Gesture.java39
-rwxr-xr-xcore/java/android/gesture/GestureOverlayView.java564
-rw-r--r--core/java/android/gesture/GestureStroke.java59
-rwxr-xr-xcore/java/android/gesture/GestureUtilities.java2
-rw-r--r--core/java/android/gesture/TouchThroughGestureListener.java178
5 files changed, 438 insertions, 404 deletions
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index 14530a1..6aca105 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -57,6 +57,11 @@ public class Gesture implements Parcelable {
mGestureID = GESTURE_ID_BASE + sGestureCount++;
}
+ void recycle() {
+ mStrokes.clear();
+ mBoundingBox.setEmpty();
+ }
+
/**
* @return all the strokes of the gesture
*/
@@ -111,6 +116,40 @@ public class Gesture implements Parcelable {
return mBoundingBox;
}
+ public Path toPath() {
+ return toPath(null);
+ }
+
+ public Path toPath(Path path) {
+ if (path == null) path = new Path();
+
+ final ArrayList<GestureStroke> strokes = mStrokes;
+ final int count = strokes.size();
+
+ for (int i = 0; i < count; i++) {
+ path.addPath(strokes.get(i).getPath());
+ }
+
+ return path;
+ }
+
+ public Path toPath(int width, int height, int edge, int numSample) {
+ return toPath(null, width, height, edge, numSample);
+ }
+
+ public Path toPath(Path path, int width, int height, int edge, int numSample) {
+ if (path == null) path = new Path();
+
+ final ArrayList<GestureStroke> strokes = mStrokes;
+ final int count = strokes.size();
+
+ for (int i = 0; i < count; i++) {
+ path.addPath(strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample));
+ }
+
+ return path;
+ }
+
/**
* Set the id of the gesture
*
diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java
index 64ddbbd..012c01f 100755
--- a/core/java/android/gesture/GestureOverlayView.java
+++ b/core/java/android/gesture/GestureOverlayView.java
@@ -18,51 +18,62 @@ package android.gesture;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.os.SystemClock;
import com.android.internal.R;
import java.util.ArrayList;
/**
- * A (transparent) overlay for gesture input that can be placed on top of other
- * widgets.
+ * A transparent overlay for gesture input that can be placed on top of other
+ * widgets or contain other widgets.
*
+ * @attr ref android.R.styleable#GestureOverlayView_eventsInterceptionEnabled
+ * @attr ref android.R.styleable#GestureOverlayView_fadeDuration
+ * @attr ref android.R.styleable#GestureOverlayView_fadeOffset
* @attr ref android.R.styleable#GestureOverlayView_gestureStrokeWidth
+ * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeAngleThreshold
+ * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeLengthThreshold
+ * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeSquarenessThreshold
+ * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeType
* @attr ref android.R.styleable#GestureOverlayView_gestureColor
* @attr ref android.R.styleable#GestureOverlayView_uncertainGestureColor
- * @attr ref android.R.styleable#GestureOverlayView_fadeDuration
- * @attr ref android.R.styleable#GestureOverlayView_fadeOffset
*/
-public class GestureOverlayView extends View {
- private static final int TRANSPARENT_BACKGROUND = 0x00000000;
+public class GestureOverlayView extends FrameLayout {
+ public static final int GESTURE_STROKE_TYPE_SINGLE = 0;
+ public static final int GESTURE_STROKE_TYPE_MULTIPLE = 1;
+
+ private static final int FADE_ANIMATION_RATE = 16;
private static final boolean GESTURE_RENDERING_ANTIALIAS = true;
private static final boolean DITHER_FLAG = true;
- private Paint mGesturePaint;
-
- private final Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);
- private Bitmap mBitmap;
- private Canvas mBitmapCanvas;
+ private final Paint mGesturePaint = new Paint();
- private long mFadeDuration = 300;
- private long mFadeOffset = 300;
+ private long mFadeDuration = 150;
+ private long mFadeOffset = 420;
private long mFadingStart;
+ private boolean mFadingHasStarted;
- private float mGestureStroke = 12.0f;
+ private int mCurrentColor;
private int mCertainGestureColor = 0xFFFFFF00;
- private int mUncertainGestureColor = 0x3CFFFF00;
+ private int mUncertainGestureColor = 0x48FFFF00;
+ private float mGestureStrokeWidth = 12.0f;
private int mInvalidateExtraBorder = 10;
- // for rendering immediate ink feedback
+ private int mGestureStrokeType = GESTURE_STROKE_TYPE_SINGLE;
+ private float mGestureStrokeLengthThreshold = 30.0f;
+ private float mGestureStrokeSquarenessTreshold = 0.275f;
+ private float mGestureStrokeAngleThreshold = 40.0f;
+
private final Rect mInvalidRect = new Rect();
private final Path mPath = new Path();
@@ -72,41 +83,31 @@ public class GestureOverlayView extends View {
private float mCurveEndX;
private float mCurveEndY;
+ private float mTotalLength;
+ private boolean mIsGesturing = false;
+ private boolean mInterceptEvents = true;
+ private boolean mIsListeningForGestures;
+
// current gesture
- private Gesture mCurrentGesture = null;
+ private Gesture mCurrentGesture;
+ private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
// TODO: Make this a list of WeakReferences
private final ArrayList<OnGestureListener> mOnGestureListeners =
new ArrayList<OnGestureListener>();
- private final ArrayList<GesturePoint> mPointBuffer = new ArrayList<GesturePoint>(100);
+ // TODO: Make this a list of WeakReferences
+ private final ArrayList<OnGesturePerformedListener> mOnGesturePerformedListeners =
+ new ArrayList<OnGesturePerformedListener>();
+
+ private boolean mHandleGestureActions;
// fading out effect
private boolean mIsFadingOut = false;
- private float mFadingAlpha = 1;
+ private float mFadingAlpha = 1.0f;
private final AccelerateDecelerateInterpolator mInterpolator =
new AccelerateDecelerateInterpolator();
- private final Runnable mFadingOut = new Runnable() {
- public void run() {
- if (mIsFadingOut) {
- final long now = AnimationUtils.currentAnimationTimeMillis();
- final long duration = now - mFadingStart;
-
- if (duration > mFadeDuration) {
- mIsFadingOut = false;
- mPath.rewind();
- mCurrentGesture = null;
- mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
- } else {
- float interpolatedTime = Math.max(0.0f,
- Math.min(1.0f, duration / (float) mFadeDuration));
- mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime);
- postDelayed(this, 16);
- }
- invalidate();
- }
- }
- };
+ private final FadeOutRunnable mFadingOut = new FadeOutRunnable();
public GestureOverlayView(Context context) {
super(context);
@@ -123,41 +124,52 @@ public class GestureOverlayView extends View {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.GestureOverlayView, defStyle, 0);
- mGestureStroke = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth,
- mGestureStroke);
- mInvalidateExtraBorder = Math.max(1, ((int) mGestureStroke) - 1);
+ mGestureStrokeWidth = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth,
+ mGestureStrokeWidth);
+ mInvalidateExtraBorder = Math.max(1, ((int) mGestureStrokeWidth) - 1);
mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor,
mCertainGestureColor);
mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor,
mUncertainGestureColor);
mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration);
mFadeOffset = a.getInt(R.styleable.GestureOverlayView_fadeOffset, (int) mFadeOffset);
+ mGestureStrokeType = a.getInt(R.styleable.GestureOverlayView_gestureStrokeType,
+ mGestureStrokeType);
+ mGestureStrokeLengthThreshold = a.getFloat(
+ R.styleable.GestureOverlayView_gestureStrokeLengthThreshold,
+ mGestureStrokeLengthThreshold);
+ mGestureStrokeAngleThreshold = a.getFloat(
+ R.styleable.GestureOverlayView_gestureStrokeAngleThreshold,
+ mGestureStrokeAngleThreshold);
+ mGestureStrokeSquarenessTreshold = a.getFloat(
+ R.styleable.GestureOverlayView_gestureStrokeSquarenessThreshold,
+ mGestureStrokeSquarenessTreshold);
+ mInterceptEvents = a.getBoolean(R.styleable.GestureOverlayView_eventsInterceptionEnabled,
+ mInterceptEvents);
a.recycle();
init();
}
- public ArrayList<GesturePoint> getCurrentStroke() {
- return mPointBuffer;
- }
+ private void init() {
+ setWillNotDraw(false);
- public Gesture getCurrentGesture() {
- return mCurrentGesture;
+ final Paint gesturePaint = mGesturePaint;
+ gesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
+ gesturePaint.setColor(mCertainGestureColor);
+ gesturePaint.setStyle(Paint.Style.STROKE);
+ gesturePaint.setStrokeJoin(Paint.Join.ROUND);
+ gesturePaint.setStrokeCap(Paint.Cap.ROUND);
+ gesturePaint.setStrokeWidth(mGestureStrokeWidth);
+ gesturePaint.setDither(DITHER_FLAG);
+
+ mCurrentColor = mCertainGestureColor;
+ setPaintAlpha(255);
}
- /**
- * Set Gesture color
- *
- * @param color
- */
- public void setGestureDrawingColor(int color) {
- mGesturePaint.setColor(color);
- if (mCurrentGesture != null) {
- mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
- mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
- }
- invalidate();
+ public ArrayList<GesturePoint> getCurrentStroke() {
+ return mStrokeBuffer;
}
public void setGestureColor(int color) {
@@ -176,73 +188,77 @@ public class GestureOverlayView extends View {
return mCertainGestureColor;
}
- public float getGestureStroke() {
- return mGestureStroke;
+ public float getGestureStrokeWidth() {
+ return mGestureStrokeWidth;
}
- public void setGestureStroke(float gestureStroke) {
- mGestureStroke = gestureStroke;
- mInvalidateExtraBorder = Math.max(1, ((int) mGestureStroke) - 1);
- mGesturePaint.setStrokeWidth(mGestureStroke);
+ public void setGestureStrokeWidth(float gestureStrokeWidth) {
+ mGestureStrokeWidth = gestureStrokeWidth;
+ mInvalidateExtraBorder = Math.max(1, ((int) gestureStrokeWidth) - 1);
+ mGesturePaint.setStrokeWidth(gestureStrokeWidth);
}
- /**
- * Set the gesture to be shown in the view
- *
- * @param gesture
- */
- public void setCurrentGesture(Gesture gesture) {
- if (mCurrentGesture != null) {
- clear(false);
- }
+ public int getGestureStrokeType() {
+ return mGestureStrokeType;
+ }
- mCurrentGesture = gesture;
+ public void setGestureStrokeType(int gestureStrokeType) {
+ mGestureStrokeType = gestureStrokeType;
+ }
- if (gesture != null) {
- if (mBitmapCanvas != null) {
- gesture.draw(mBitmapCanvas, mGesturePaint);
- invalidate();
- }
- }
+ public float getGestureStrokeLengthThreshold() {
+ return mGestureStrokeLengthThreshold;
}
- private void init() {
- mGesturePaint = new Paint();
+ public void setGestureStrokeLengthThreshold(float gestureStrokeLengthThreshold) {
+ mGestureStrokeLengthThreshold = gestureStrokeLengthThreshold;
+ }
- final Paint gesturePaint = mGesturePaint;
- gesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
- gesturePaint.setColor(mCertainGestureColor);
- gesturePaint.setStyle(Paint.Style.STROKE);
- gesturePaint.setStrokeJoin(Paint.Join.ROUND);
- gesturePaint.setStrokeCap(Paint.Cap.ROUND);
- gesturePaint.setStrokeWidth(mGestureStroke);
- gesturePaint.setDither(DITHER_FLAG);
+ public float getGestureStrokeSquarenessTreshold() {
+ return mGestureStrokeSquarenessTreshold;
}
- @Override
- protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
- super.onSizeChanged(width, height, oldWidth, oldHeight);
+ public void setGestureStrokeSquarenessTreshold(float gestureStrokeSquarenessTreshold) {
+ mGestureStrokeSquarenessTreshold = gestureStrokeSquarenessTreshold;
+ }
- if (width <= 0 || height <= 0) {
- return;
- }
+ public float getGestureStrokeAngleThreshold() {
+ return mGestureStrokeAngleThreshold;
+ }
- int targetWidth = width > oldWidth ? width : oldWidth;
- int targetHeight = height > oldHeight ? height : oldHeight;
+ public void setGestureStrokeAngleThreshold(float gestureStrokeAngleThreshold) {
+ mGestureStrokeAngleThreshold = gestureStrokeAngleThreshold;
+ }
- if (mBitmap != null) mBitmap.recycle();
+ public boolean isEventsInterceptionEnabled() {
+ return mInterceptEvents;
+ }
- mBitmap = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
- if (mBitmapCanvas != null) {
- mBitmapCanvas.setBitmap(mBitmap);
- } else {
- mBitmapCanvas = new Canvas(mBitmap);
- }
- mBitmapCanvas.drawColor(TRANSPARENT_BACKGROUND);
+ public void setEventsInterceptionEnabled(boolean enabled) {
+ mInterceptEvents = enabled;
+ }
+
+ public Gesture getGesture() {
+ return mCurrentGesture;
+ }
+ public void setGesture(Gesture gesture) {
if (mCurrentGesture != null) {
- mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
+ clear(false);
}
+
+ setCurrentColor(mCertainGestureColor);
+ mCurrentGesture = gesture;
+
+ final Path path = mCurrentGesture.toPath();
+ final RectF bounds = new RectF();
+ path.computeBounds(bounds, true);
+
+ mPath.rewind();
+ mPath.addPath(path, (getWidth() - bounds.width()) / 2.0f,
+ (getHeight() - bounds.height()) / 2.0f);
+
+ invalidate();
}
public void addOnGestureListener(OnGestureListener listener) {
@@ -257,100 +273,172 @@ public class GestureOverlayView extends View {
mOnGestureListeners.clear();
}
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
+ public void addOnGesturePerformedListener(OnGesturePerformedListener listener) {
+ mOnGesturePerformedListeners.add(listener);
+ if (mOnGesturePerformedListeners.size() > 0) {
+ mHandleGestureActions = true;
+ }
+ }
+
+ public void removeOnGesturePerformedListener(OnGesturePerformedListener listener) {
+ mOnGesturePerformedListeners.remove(listener);
+ if (mOnGesturePerformedListeners.size() <= 0) {
+ mHandleGestureActions = false;
+ }
+ }
+
+ public void removeAllOnGesturePerformedListeners() {
+ mOnGesturePerformedListeners.clear();
+ mHandleGestureActions = false;
+ }
- // draw double buffer
- if (mIsFadingOut) {
- mBitmapPaint.setAlpha((int) (255 * mFadingAlpha));
- canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
+ public boolean isGesturing() {
+ return mIsGesturing;
+ }
+
+ private void setCurrentColor(int color) {
+ mCurrentColor = color;
+ if (mFadingHasStarted) {
+ setPaintAlpha((int) (255 * mFadingAlpha));
} else {
- mBitmapPaint.setAlpha(255);
- canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
+ setPaintAlpha(255);
+ }
+ invalidate();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+
+ if (mCurrentGesture != null) {
+ canvas.drawPath(mPath, mGesturePaint);
}
+ }
+
+ private void setPaintAlpha(int alpha) {
+ alpha += alpha >> 7;
+ final int baseAlpha = mCurrentColor >>> 24;
+ final int useAlpha = baseAlpha * alpha >> 8;
+ mGesturePaint.setColor((mCurrentColor << 8 >>> 8) | (useAlpha << 24));
+ }
- // draw the current stroke
- canvas.drawPath(mPath, mGesturePaint);
+ public void clear(boolean animated) {
+ clear(animated, false);
}
- /**
- * Clear up the overlay
- *
- * @param fadeOut whether the gesture on the overlay should fade out
- * gradually or disappear immediately
- */
- public void clear(boolean fadeOut) {
- if (fadeOut) {
+ private void clear(boolean animated, boolean fireActionPerformed) {
+ setPaintAlpha(255);
+ if (animated && mCurrentGesture != null) {
mFadingAlpha = 1.0f;
mIsFadingOut = true;
+ mFadingHasStarted = false;
+ mFadingOut.fireActionPerformed = fireActionPerformed;
removeCallbacks(mFadingOut);
mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;
postDelayed(mFadingOut, mFadeOffset);
} else {
mPath.rewind();
mCurrentGesture = null;
- if (mBitmap != null) {
- mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
- invalidate();
- }
}
}
- public void cancelFadingOut() {
+ public void cancelClearAnimation() {
+ setPaintAlpha(255);
mIsFadingOut = false;
+ mFadingHasStarted = false;
removeCallbacks(mFadingOut);
+ mPath.rewind();
+ mCurrentGesture = null;
+ }
+
+ public void cancelGesture() {
+ mIsListeningForGestures = false;
+
+ // add the stroke to the current gesture
+ mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));
+
+ // pass the event to handlers
+ final long now = SystemClock.uptimeMillis();
+ final MotionEvent event = MotionEvent.obtain(now, now,
+ MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+
+ final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
+ final int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ listeners.get(i).onGestureCancelled(this, event);
+ }
+
+ event.recycle();
+
+ clear(false);
+ mIsGesturing = false;
+ mStrokeBuffer.clear();
}
@Override
- public boolean onTouchEvent(MotionEvent event) {
- if (!isEnabled()) {
+ protected void onDetachedFromWindow() {
+ cancelClearAnimation();
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (isEnabled()) {
+ boolean cancelDispatch = (mIsGesturing || (mCurrentGesture != null &&
+ mCurrentGesture.getStrokesCount() > 0)) && mInterceptEvents;
+ processEvent(event);
+
+ if (cancelDispatch) {
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ }
+
+ super.dispatchTouchEvent(event);
return true;
}
- processEvent(event);
-
- return true;
+ return super.dispatchTouchEvent(event);
}
- public void processEvent(MotionEvent event) {
+ private boolean processEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- Rect rect = touchStart(event);
- invalidate(rect);
- break;
+ touchStart(event);
+ invalidate();
+ return true;
case MotionEvent.ACTION_MOVE:
- rect = touchMove(event);
- if (rect != null) {
- invalidate(rect);
+ if (mIsListeningForGestures) {
+ Rect rect = touchMove(event);
+ if (rect != null) {
+ invalidate(rect);
+ }
+ return true;
}
break;
case MotionEvent.ACTION_UP:
- touchUp(event, false);
- invalidate();
+ if (mIsListeningForGestures) {
+ touchUp(event, false);
+ invalidate();
+ return true;
+ }
break;
case MotionEvent.ACTION_CANCEL:
- touchUp(event, true);
- invalidate();
- break;
+ if (mIsListeningForGestures) {
+ touchUp(event, true);
+ invalidate();
+ return true;
+ }
}
+
+ return false;
}
- private Rect touchStart(MotionEvent event) {
+ private void touchStart(MotionEvent event) {
+ mIsListeningForGestures = true;
+
// pass the event to handlers
final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
final int count = listeners.size();
for (int i = 0; i < count; i++) {
- OnGestureListener listener = listeners.get(i);
- listener.onGestureStarted(this, event);
- }
-
- // if there is fading out going on, stop it.
- if (mIsFadingOut) {
- mIsFadingOut = false;
- removeCallbacks(mFadingOut);
- mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
- mCurrentGesture = null;
+ listeners.get(i).onGestureStarted(this, event);
}
float x = event.getX();
@@ -359,22 +447,39 @@ public class GestureOverlayView extends View {
mX = x;
mY = y;
+ mTotalLength = 0;
+ mIsGesturing = false;
+
+ if (mGestureStrokeType == GESTURE_STROKE_TYPE_SINGLE) {
+ if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
+ mCurrentGesture = null;
+ mPath.rewind();
+ } else if (mCurrentGesture == null || mCurrentGesture.getStrokesCount() == 0) {
+ if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
+ }
+
+ // if there is fading out going on, stop it.
+ if (mFadingHasStarted) {
+ cancelClearAnimation();
+ } else if (mIsFadingOut) {
+ setPaintAlpha(255);
+ mIsFadingOut = false;
+ mFadingHasStarted = false;
+ removeCallbacks(mFadingOut);
+ }
+
if (mCurrentGesture == null) {
mCurrentGesture = new Gesture();
}
- mPointBuffer.add(new GesturePoint(x, y, event.getEventTime()));
-
- mPath.rewind();
+ mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
mPath.moveTo(x, y);
- mInvalidRect.set((int) x - mInvalidateExtraBorder, (int) y - mInvalidateExtraBorder,
- (int) x + mInvalidateExtraBorder, (int) y + mInvalidateExtraBorder);
-
+ final int border = mInvalidateExtraBorder;
+ mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border);
+
mCurveEndX = x;
mCurveEndY = y;
-
- return mInvalidRect;
}
private Rect touchMove(MotionEvent event) {
@@ -393,36 +498,28 @@ public class GestureOverlayView extends View {
areaToRefresh = mInvalidRect;
// start with the curve end
- areaToRefresh.set(
- (int) mCurveEndX - mInvalidateExtraBorder,
- (int) mCurveEndY - mInvalidateExtraBorder,
- (int) mCurveEndX + mInvalidateExtraBorder,
- (int) mCurveEndY + mInvalidateExtraBorder);
+ final int border = mInvalidateExtraBorder;
+ areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border,
+ (int) mCurveEndX + border, (int) mCurveEndY + border);
- mCurveEndX = (x + previousX) / 2;
- mCurveEndY = (y + previousY) / 2;
+ float cX = mCurveEndX = (x + previousX) / 2;
+ float cY = mCurveEndY = (y + previousY) / 2;
- mPath.quadTo(previousX, previousY, mCurveEndX, mCurveEndY);
+ mPath.quadTo(previousX, previousY, cX, cY);
// union with the control point of the new curve
- areaToRefresh.union(
- (int) previousX - mInvalidateExtraBorder,
- (int) previousY - mInvalidateExtraBorder,
- (int) previousX + mInvalidateExtraBorder,
- (int) previousY + mInvalidateExtraBorder);
+ areaToRefresh.union((int) previousX - border, (int) previousY - border,
+ (int) previousX + border, (int) previousY + border);
// union with the end point of the new curve
- areaToRefresh.union(
- (int) mCurveEndX - mInvalidateExtraBorder,
- (int) mCurveEndY - mInvalidateExtraBorder,
- (int) mCurveEndX + mInvalidateExtraBorder,
- (int) mCurveEndY + mInvalidateExtraBorder);
+ areaToRefresh.union((int) cX - border, (int) cY - border,
+ (int) cX + border, (int) cY + border);
mX = x;
mY = y;
}
- mPointBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+ mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
// pass the event to handlers
final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
@@ -430,24 +527,48 @@ public class GestureOverlayView extends View {
for (int i = 0; i < count; i++) {
listeners.get(i).onGesture(this, event);
}
-
+
+ if (mHandleGestureActions && !mIsGesturing) {
+ mTotalLength += (float) Math.sqrt(dx * dx + dy * dy);
+
+ if (mTotalLength > mGestureStrokeLengthThreshold) {
+ final OrientedBoundingBox box =
+ GestureUtilities.computeOrientedBoundingBox(mStrokeBuffer);
+
+ float angle = Math.abs(box.orientation);
+ if (angle > 90) {
+ angle = 180 - angle;
+ }
+
+ if (box.squareness > mGestureStrokeSquarenessTreshold ||
+ angle < mGestureStrokeAngleThreshold) {
+
+ mIsGesturing = true;
+ setCurrentColor(mCertainGestureColor);
+ }
+ }
+ }
+
return areaToRefresh;
}
private void touchUp(MotionEvent event, boolean cancel) {
- // add the stroke to the current gesture
- mCurrentGesture.addStroke(new GestureStroke(mPointBuffer));
+ mIsListeningForGestures = false;
- // add the stroke to the double buffer
- mBitmapCanvas.drawPath(mPath, mGesturePaint);
+ // add the stroke to the current gesture
+ mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));
if (!cancel) {
// pass the event to handlers
final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
- final int count = listeners.size();
+ int count = listeners.size();
for (int i = 0; i < count; i++) {
listeners.get(i).onGestureEnded(this, event);
}
+
+ if (mHandleGestureActions) {
+ clear(true, mIsGesturing);
+ }
} else {
// pass the event to handlers
final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
@@ -455,15 +576,52 @@ public class GestureOverlayView extends View {
for (int i = 0; i < count; i++) {
listeners.get(i).onGestureCancelled(this, event);
}
+
+ clear(false);
}
- mPath.rewind();
- mPointBuffer.clear();
+ mIsGesturing = false;
+ mStrokeBuffer.clear();
+ }
+
+ private class FadeOutRunnable implements Runnable {
+ boolean fireActionPerformed;
+
+ public void run() {
+ if (mIsFadingOut) {
+ final long now = AnimationUtils.currentAnimationTimeMillis();
+ final long duration = now - mFadingStart;
+
+ if (duration > mFadeDuration) {
+ if (fireActionPerformed) {
+ final ArrayList<OnGesturePerformedListener> actionListeners =
+ mOnGesturePerformedListeners;
+ final int count = actionListeners.size();
+ for (int i = 0; i < count; i++) {
+ actionListeners.get(i).onGesturePerformed(GestureOverlayView.this,
+ mCurrentGesture);
+ }
+ }
+
+ mIsFadingOut = false;
+ mFadingHasStarted = false;
+ mPath.rewind();
+ mCurrentGesture = null;
+ setPaintAlpha(255);
+ } else {
+ mFadingHasStarted = true;
+ float interpolatedTime = Math.max(0.0f,
+ Math.min(1.0f, duration / (float) mFadeDuration));
+ mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime);
+ setPaintAlpha((int) (255 * mFadingAlpha));
+ postDelayed(this, FADE_ANIMATION_RATE);
+ }
+
+ invalidate();
+ }
+ }
}
- /**
- * An interface for processing gesture events
- */
public static interface OnGestureListener {
void onGestureStarted(GestureOverlayView overlay, MotionEvent event);
@@ -473,4 +631,8 @@ public class GestureOverlayView extends View {
void onGestureCancelled(GestureOverlayView overlay, MotionEvent event);
}
+
+ public static interface OnGesturePerformedListener {
+ void onGesturePerformed(GestureOverlayView overlay, Gesture gesture);
+ }
}
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index 6d022b4..0d7bc2d 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -89,37 +89,49 @@ public class GestureStroke {
*/
void draw(Canvas canvas, Paint paint) {
if (mCachedPath == null) {
- final float[] localPoints = points;
- final int count = localPoints.length;
+ makePath();
+ }
- Path path = null;
+ canvas.drawPath(mCachedPath, paint);
+ }
- float mX = 0;
- float mY = 0;
+ public Path getPath() {
+ if (mCachedPath == null) {
+ makePath();
+ }
+
+ return mCachedPath;
+ }
+
+ private void makePath() {
+ final float[] localPoints = points;
+ final int count = localPoints.length;
+
+ Path path = null;
+
+ float mX = 0;
+ float mY = 0;
- for (int i = 0; i < count; i += 2) {
- float x = localPoints[i];
- float y = localPoints[i + 1];
- if (path == null) {
- path = new Path();
- path.moveTo(x, y);
+ for (int i = 0; i < count; i += 2) {
+ float x = localPoints[i];
+ float y = localPoints[i + 1];
+ if (path == null) {
+ path = new Path();
+ path.moveTo(x, y);
+ mX = x;
+ mY = y;
+ } else {
+ float dx = Math.abs(x - mX);
+ float dy = Math.abs(y - mY);
+ if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
+ path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
- } else {
- float dx = Math.abs(x - mX);
- float dy = Math.abs(y - mY);
- if (dx >= 3 || dy >= 3) {
- path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
- mX = x;
- mY = y;
- }
}
}
-
- mCachedPath = path;
}
- canvas.drawPath(mCachedPath, paint);
+ mCachedPath = path;
}
/**
@@ -158,8 +170,7 @@ public class GestureStroke {
} else {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
- if (dx >= TOUCH_TOLERANCE ||
- dy >= TOUCH_TOLERANCE) {
+ if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java
index e47856c..0f9253d 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -388,7 +388,7 @@ final class GestureUtilities {
} else { // -PI<alpha<PI
angle = (float) Math.atan2(targetVector[1], targetVector[0]);
angle = (float) (180 * angle / Math.PI);
- android.graphics.Matrix trans = new android.graphics.Matrix();
+ Matrix trans = new Matrix();
trans.setRotate(-angle);
trans.mapPoints(points);
}
diff --git a/core/java/android/gesture/TouchThroughGestureListener.java b/core/java/android/gesture/TouchThroughGestureListener.java
deleted file mode 100644
index 09a528d..0000000
--- a/core/java/android/gesture/TouchThroughGestureListener.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2008-2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.gesture;
-
-import android.view.MotionEvent;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.lang.ref.WeakReference;
-
-/**
- * TouchThroughGesturing implements the interaction behavior that allows a user
- * to gesture over a regular UI widget such as ListView and at the same time,
- * still allows a user to perform basic interactions (clicking, scrolling and panning)
- * with the underlying widget.
- */
-public class TouchThroughGestureListener implements GestureOverlayView.OnGestureListener {
- public static final int SINGLE_STROKE = 0;
- public static final int MULTIPLE_STROKE = 1;
-
- // TODO: Add properties for all these
- private static final float STROKE_LENGTH_THRESHOLD = 30;
- private static final float SQUARENESS_THRESHOLD = 0.275f;
- private static final float ANGLE_THRESHOLD = 40;
-
- private boolean mIsGesturing = false;
-
- private float mTotalLength;
-
- private float mX;
- private float mY;
-
- private WeakReference<View> mModel;
-
- private int mGestureType = SINGLE_STROKE;
-
- // TODO: Use WeakReferences
- private final ArrayList<OnGesturePerformedListener> mPerformedListeners =
- new ArrayList<OnGesturePerformedListener>();
-
- private boolean mStealEvents = false;
-
- public TouchThroughGestureListener(View model) {
- this(model, true);
- }
-
- public TouchThroughGestureListener(View model, boolean stealEvents) {
- mModel = new WeakReference<View>(model);
- mStealEvents = stealEvents;
- }
-
- /**
- *
- * @param type SINGLE_STROKE or MULTIPLE_STROKE
- */
- public void setGestureType(int type) {
- mGestureType = type;
- }
-
- public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
- if (mGestureType == MULTIPLE_STROKE) {
- overlay.cancelFadingOut();
- }
-
- mX = event.getX();
- mY = event.getY();
- mTotalLength = 0;
- mIsGesturing = false;
-
- if (mGestureType == SINGLE_STROKE || overlay.getCurrentGesture() == null
- || overlay.getCurrentGesture().getStrokesCount() == 0) {
- overlay.setGestureDrawingColor(overlay.getUncertainGestureColor());
- }
-
- dispatchEventToModel(event);
- }
-
- private void dispatchEventToModel(MotionEvent event) {
- View v = mModel.get();
- if (v != null) v.dispatchTouchEvent(event);
- }
-
- public void onGesture(GestureOverlayView overlay, MotionEvent event) {
- //noinspection PointlessBooleanExpression
- if (!mStealEvents) {
- dispatchEventToModel(event);
- }
-
- if (mIsGesturing) {
- return;
- }
-
- final float x = event.getX();
- final float y = event.getY();
- final float dx = x - mX;
- final float dy = y - mY;
-
- mTotalLength += (float) Math.sqrt(dx * dx + dy * dy);
- mX = x;
- mY = y;
-
- if (mTotalLength > STROKE_LENGTH_THRESHOLD) {
- final OrientedBoundingBox box =
- GestureUtilities.computeOrientedBoundingBox(overlay.getCurrentStroke());
- float angle = Math.abs(box.orientation);
- if (angle > 90) {
- angle = 180 - angle;
- }
- if (box.squareness > SQUARENESS_THRESHOLD || angle < ANGLE_THRESHOLD) {
- mIsGesturing = true;
- overlay.setGestureDrawingColor(overlay.getGestureColor());
- if (mStealEvents) {
- event = MotionEvent.obtain(event.getDownTime(), System.currentTimeMillis(),
- MotionEvent.ACTION_CANCEL, x, y, event.getPressure(), event.getSize(),
- event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
- event.getDeviceId(), event.getEdgeFlags());
- }
- }
- }
-
- if (mStealEvents) {
- dispatchEventToModel(event);
- }
- }
-
- public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
- if (mIsGesturing) {
- overlay.clear(true);
-
- final ArrayList<OnGesturePerformedListener> listeners = mPerformedListeners;
- final int count = listeners.size();
-
- for (int i = 0; i < count; i++) {
- listeners.get(i).onGesturePerformed(overlay, overlay.getCurrentGesture());
- }
- } else {
- dispatchEventToModel(event);
- overlay.clear(false);
- }
- }
-
- public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {
- overlay.clear(mIsGesturing);
- if (!mIsGesturing) {
- dispatchEventToModel(event);
- }
- }
-
- public void addOnGestureActionListener(OnGesturePerformedListener listener) {
- mPerformedListeners.add(listener);
- }
-
- public void removeOnGestureActionListener(OnGesturePerformedListener listener) {
- mPerformedListeners.remove(listener);
- }
-
- public boolean isGesturing() {
- return mIsGesturing;
- }
-
- public static interface OnGesturePerformedListener {
- public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture);
- }
-}