Merge change 2402 into donut

* changes:
  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.
This commit is contained in:
Android (Google) Code Review
2009-05-24 23:47:30 -07:00
18 changed files with 916 additions and 777 deletions

View File

@@ -3529,61 +3529,6 @@
visibility="public" visibility="public"
> >
</field> </field>
<field name="donut_resource_pad34"
type="int"
transient="false"
volatile="false"
value="16843390"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="donut_resource_pad35"
type="int"
transient="false"
volatile="false"
value="16843389"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="donut_resource_pad36"
type="int"
transient="false"
volatile="false"
value="16843388"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="donut_resource_pad37"
type="int"
transient="false"
volatile="false"
value="16843387"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="donut_resource_pad38"
type="int"
transient="false"
volatile="false"
value="16843386"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="donut_resource_pad4" <field name="donut_resource_pad4"
type="int" type="int"
transient="false" transient="false"
@@ -3947,6 +3892,17 @@
visibility="public" visibility="public"
> >
</field> </field>
<field name="eventsInterceptionEnabled"
type="int"
transient="false"
volatile="false"
value="16843390"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="excludeFromRecents" <field name="excludeFromRecents"
type="int" type="int"
transient="false" transient="false"
@@ -4420,6 +4376,50 @@
visibility="public" visibility="public"
> >
</field> </field>
<field name="gestureStrokeAngleThreshold"
type="int"
transient="false"
volatile="false"
value="16843389"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="gestureStrokeLengthThreshold"
type="int"
transient="false"
volatile="false"
value="16843387"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="gestureStrokeSquarenessThreshold"
type="int"
transient="false"
volatile="false"
value="16843388"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="gestureStrokeType"
type="int"
transient="false"
volatile="false"
value="16843386"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="gestureStrokeWidth" <field name="gestureStrokeWidth"
type="int" type="int"
transient="false" transient="false"
@@ -46303,6 +46303,70 @@
<parameter name="color" type="int"> <parameter name="color" type="int">
</parameter> </parameter>
</method> </method>
<method name="toPath"
return="android.graphics.Path"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="toPath"
return="android.graphics.Path"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="path" type="android.graphics.Path">
</parameter>
</method>
<method name="toPath"
return="android.graphics.Path"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="width" type="int">
</parameter>
<parameter name="height" type="int">
</parameter>
<parameter name="edge" type="int">
</parameter>
<parameter name="numSample" type="int">
</parameter>
</method>
<method name="toPath"
return="android.graphics.Path"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="path" type="android.graphics.Path">
</parameter>
<parameter name="width" type="int">
</parameter>
<parameter name="height" type="int">
</parameter>
<parameter name="edge" type="int">
</parameter>
<parameter name="numSample" type="int">
</parameter>
</method>
<method name="writeToParcel" <method name="writeToParcel"
return="void" return="void"
abstract="false" abstract="false"
@@ -46543,7 +46607,7 @@
</field> </field>
</class> </class>
<class name="GestureOverlayView" <class name="GestureOverlayView"
extends="android.view.View" extends="android.widget.FrameLayout"
abstract="false" abstract="false"
static="false" static="false"
final="false" final="false"
@@ -46599,7 +46663,31 @@
<parameter name="listener" type="android.gesture.GestureOverlayView.OnGestureListener"> <parameter name="listener" type="android.gesture.GestureOverlayView.OnGestureListener">
</parameter> </parameter>
</method> </method>
<method name="cancelFadingOut" <method name="addOnGesturePerformedListener"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturePerformedListener">
</parameter>
</method>
<method name="cancelClearAnimation"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="cancelGesture"
return="void" return="void"
abstract="false" abstract="false"
native="false" native="false"
@@ -46620,11 +46708,11 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="fadeOut" type="boolean"> <parameter name="animated" type="boolean">
</parameter> </parameter>
</method> </method>
<method name="getCurrentGesture" <method name="getCurrentStroke"
return="android.gesture.Gesture" return="java.util.ArrayList&lt;android.gesture.GesturePoint&gt;"
abstract="false" abstract="false"
native="false" native="false"
synchronized="false" synchronized="false"
@@ -46634,8 +46722,8 @@
visibility="public" visibility="public"
> >
</method> </method>
<method name="getCurrentStroke" <method name="getGesture"
return="java.util.ArrayList&lt;android.gesture.GesturePoint&gt;" return="android.gesture.Gesture"
abstract="false" abstract="false"
native="false" native="false"
synchronized="false" synchronized="false"
@@ -46656,7 +46744,51 @@
visibility="public" visibility="public"
> >
</method> </method>
<method name="getGestureStroke" <method name="getGestureStrokeAngleThreshold"
return="float"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getGestureStrokeLengthThreshold"
return="float"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getGestureStrokeSquarenessTreshold"
return="float"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getGestureStrokeType"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getGestureStrokeWidth"
return="float" return="float"
abstract="false" abstract="false"
native="false" native="false"
@@ -46678,7 +46810,29 @@
visibility="public" visibility="public"
> >
</method> </method>
<method name="processEvent" <method name="isEventsInterceptionEnabled"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="isGesturing"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="removeAllOnGestureListeners"
return="void" return="void"
abstract="false" abstract="false"
native="false" native="false"
@@ -46688,10 +46842,8 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="event" type="android.view.MotionEvent">
</parameter>
</method> </method>
<method name="removeAllOnGestureListeners" <method name="removeAllOnGesturePerformedListeners"
return="void" return="void"
abstract="false" abstract="false"
native="false" native="false"
@@ -46715,7 +46867,33 @@
<parameter name="listener" type="android.gesture.GestureOverlayView.OnGestureListener"> <parameter name="listener" type="android.gesture.GestureOverlayView.OnGestureListener">
</parameter> </parameter>
</method> </method>
<method name="setCurrentGesture" <method name="removeOnGesturePerformedListener"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturePerformedListener">
</parameter>
</method>
<method name="setEventsInterceptionEnabled"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="enabled" type="boolean">
</parameter>
</method>
<method name="setGesture"
return="void" return="void"
abstract="false" abstract="false"
native="false" native="false"
@@ -46741,7 +46919,7 @@
<parameter name="color" type="int"> <parameter name="color" type="int">
</parameter> </parameter>
</method> </method>
<method name="setGestureDrawingColor" <method name="setGestureStrokeAngleThreshold"
return="void" return="void"
abstract="false" abstract="false"
native="false" native="false"
@@ -46751,10 +46929,10 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="color" type="int"> <parameter name="gestureStrokeAngleThreshold" type="float">
</parameter> </parameter>
</method> </method>
<method name="setGestureStroke" <method name="setGestureStrokeLengthThreshold"
return="void" return="void"
abstract="false" abstract="false"
native="false" native="false"
@@ -46764,7 +46942,46 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="gestureStroke" type="float"> <parameter name="gestureStrokeLengthThreshold" type="float">
</parameter>
</method>
<method name="setGestureStrokeSquarenessTreshold"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="gestureStrokeSquarenessTreshold" type="float">
</parameter>
</method>
<method name="setGestureStrokeType"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="gestureStrokeType" type="int">
</parameter>
</method>
<method name="setGestureStrokeWidth"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="gestureStrokeWidth" type="float">
</parameter> </parameter>
</method> </method>
<method name="setUncertainGestureColor" <method name="setUncertainGestureColor"
@@ -46780,6 +46997,28 @@
<parameter name="color" type="int"> <parameter name="color" type="int">
</parameter> </parameter>
</method> </method>
<field name="GESTURE_STROKE_TYPE_MULTIPLE"
type="int"
transient="false"
volatile="false"
value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="GESTURE_STROKE_TYPE_SINGLE"
type="int"
transient="false"
volatile="false"
value="0"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</class> </class>
<interface name="GestureOverlayView.OnGestureListener" <interface name="GestureOverlayView.OnGestureListener"
abstract="true" abstract="true"
@@ -46849,6 +47088,29 @@
</parameter> </parameter>
</method> </method>
</interface> </interface>
<interface name="GestureOverlayView.OnGesturePerformedListener"
abstract="true"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<method name="onGesturePerformed"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="overlay" type="android.gesture.GestureOverlayView">
</parameter>
<parameter name="gesture" type="android.gesture.Gesture">
</parameter>
</method>
</interface>
<class name="GesturePoint" <class name="GesturePoint"
extends="java.lang.Object" extends="java.lang.Object"
abstract="false" abstract="false"
@@ -46942,6 +47204,17 @@
visibility="public" visibility="public"
> >
</method> </method>
<method name="getPath"
return="android.graphics.Path"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="toPath" <method name="toPath"
return="android.graphics.Path" return="android.graphics.Path"
abstract="false" abstract="false"
@@ -47152,194 +47425,6 @@
> >
</field> </field>
</class> </class>
<class name="TouchThroughGestureListener"
extends="java.lang.Object"
abstract="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<implements name="android.gesture.GestureOverlayView.OnGestureListener">
</implements>
<constructor name="TouchThroughGestureListener"
type="android.gesture.TouchThroughGestureListener"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="model" type="android.view.View">
</parameter>
</constructor>
<constructor name="TouchThroughGestureListener"
type="android.gesture.TouchThroughGestureListener"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="model" type="android.view.View">
</parameter>
<parameter name="stealEvents" type="boolean">
</parameter>
</constructor>
<method name="addOnGestureActionListener"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="listener" type="android.gesture.TouchThroughGestureListener.OnGesturePerformedListener">
</parameter>
</method>
<method name="isGesturing"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="onGesture"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="overlay" type="android.gesture.GestureOverlayView">
</parameter>
<parameter name="event" type="android.view.MotionEvent">
</parameter>
</method>
<method name="onGestureCancelled"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="overlay" type="android.gesture.GestureOverlayView">
</parameter>
<parameter name="event" type="android.view.MotionEvent">
</parameter>
</method>
<method name="onGestureEnded"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="overlay" type="android.gesture.GestureOverlayView">
</parameter>
<parameter name="event" type="android.view.MotionEvent">
</parameter>
</method>
<method name="onGestureStarted"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="overlay" type="android.gesture.GestureOverlayView">
</parameter>
<parameter name="event" type="android.view.MotionEvent">
</parameter>
</method>
<method name="removeOnGestureActionListener"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="listener" type="android.gesture.TouchThroughGestureListener.OnGesturePerformedListener">
</parameter>
</method>
<method name="setGestureType"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="type" type="int">
</parameter>
</method>
<field name="MULTIPLE_STROKE"
type="int"
transient="false"
volatile="false"
value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="SINGLE_STROKE"
type="int"
transient="false"
volatile="false"
value="0"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</class>
<interface name="TouchThroughGestureListener.OnGesturePerformedListener"
abstract="true"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<method name="onGesturePerformed"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="overlay" type="android.gesture.GestureOverlayView">
</parameter>
<parameter name="gesture" type="android.gesture.Gesture">
</parameter>
</method>
</interface>
</package> </package>
<package name="android.graphics" <package name="android.graphics"
> >

View File

@@ -57,6 +57,11 @@ public class Gesture implements Parcelable {
mGestureID = GESTURE_ID_BASE + sGestureCount++; mGestureID = GESTURE_ID_BASE + sGestureCount++;
} }
void recycle() {
mStrokes.clear();
mBoundingBox.setEmpty();
}
/** /**
* @return all the strokes of the gesture * @return all the strokes of the gesture
*/ */
@@ -111,6 +116,40 @@ public class Gesture implements Parcelable {
return mBoundingBox; 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 * Set the id of the gesture
* *

View File

@@ -18,51 +18,62 @@ package android.gesture;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import android.os.SystemClock;
import com.android.internal.R; import com.android.internal.R;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* A (transparent) overlay for gesture input that can be placed on top of other * A transparent overlay for gesture input that can be placed on top of other
* widgets. * widgets or contain other widgets.
* *
* @attr ref android.R.styleable#GestureOverlayView_gestureStrokeWidth * @attr ref android.R.styleable#GestureOverlayView_eventsInterceptionEnabled
* @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_fadeDuration
* @attr ref android.R.styleable#GestureOverlayView_fadeOffset * @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
*/ */
public class GestureOverlayView extends View { public class GestureOverlayView extends FrameLayout {
private static final int TRANSPARENT_BACKGROUND = 0x00000000; 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 GESTURE_RENDERING_ANTIALIAS = true;
private static final boolean DITHER_FLAG = true; private static final boolean DITHER_FLAG = true;
private Paint mGesturePaint; private final Paint mGesturePaint = new Paint();
private final Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG); private long mFadeDuration = 150;
private Bitmap mBitmap; private long mFadeOffset = 420;
private Canvas mBitmapCanvas;
private long mFadeDuration = 300;
private long mFadeOffset = 300;
private long mFadingStart; private long mFadingStart;
private boolean mFadingHasStarted;
private float mGestureStroke = 12.0f; private int mCurrentColor;
private int mCertainGestureColor = 0xFFFFFF00; private int mCertainGestureColor = 0xFFFFFF00;
private int mUncertainGestureColor = 0x3CFFFF00; private int mUncertainGestureColor = 0x48FFFF00;
private float mGestureStrokeWidth = 12.0f;
private int mInvalidateExtraBorder = 10; 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 Rect mInvalidRect = new Rect();
private final Path mPath = new Path(); private final Path mPath = new Path();
@@ -72,41 +83,31 @@ public class GestureOverlayView extends View {
private float mCurveEndX; private float mCurveEndX;
private float mCurveEndY; private float mCurveEndY;
private float mTotalLength;
private boolean mIsGesturing = false;
private boolean mInterceptEvents = true;
private boolean mIsListeningForGestures;
// current gesture // 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 // TODO: Make this a list of WeakReferences
private final ArrayList<OnGestureListener> mOnGestureListeners = private final ArrayList<OnGestureListener> mOnGestureListeners =
new ArrayList<OnGestureListener>(); 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 // fading out effect
private boolean mIsFadingOut = false; private boolean mIsFadingOut = false;
private float mFadingAlpha = 1; private float mFadingAlpha = 1.0f;
private final AccelerateDecelerateInterpolator mInterpolator = private final AccelerateDecelerateInterpolator mInterpolator =
new AccelerateDecelerateInterpolator(); new AccelerateDecelerateInterpolator();
private final Runnable mFadingOut = new Runnable() { private final FadeOutRunnable mFadingOut = new FadeOutRunnable();
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();
}
}
};
public GestureOverlayView(Context context) { public GestureOverlayView(Context context) {
super(context); super(context);
@@ -123,41 +124,52 @@ public class GestureOverlayView extends View {
TypedArray a = context.obtainStyledAttributes(attrs, TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.GestureOverlayView, defStyle, 0); R.styleable.GestureOverlayView, defStyle, 0);
mGestureStroke = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth, mGestureStrokeWidth = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth,
mGestureStroke); mGestureStrokeWidth);
mInvalidateExtraBorder = Math.max(1, ((int) mGestureStroke) - 1); mInvalidateExtraBorder = Math.max(1, ((int) mGestureStrokeWidth) - 1);
mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor, mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor,
mCertainGestureColor); mCertainGestureColor);
mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor, mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor,
mUncertainGestureColor); mUncertainGestureColor);
mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration); mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration);
mFadeOffset = a.getInt(R.styleable.GestureOverlayView_fadeOffset, (int) mFadeOffset); 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(); a.recycle();
init(); init();
} }
private void init() {
setWillNotDraw(false);
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);
}
public ArrayList<GesturePoint> getCurrentStroke() { public ArrayList<GesturePoint> getCurrentStroke() {
return mPointBuffer; return mStrokeBuffer;
}
public Gesture getCurrentGesture() {
return mCurrentGesture;
}
/**
* 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 void setGestureColor(int color) { public void setGestureColor(int color) {
@@ -176,73 +188,77 @@ public class GestureOverlayView extends View {
return mCertainGestureColor; return mCertainGestureColor;
} }
public float getGestureStroke() { public float getGestureStrokeWidth() {
return mGestureStroke; return mGestureStrokeWidth;
} }
public void setGestureStroke(float gestureStroke) { public void setGestureStrokeWidth(float gestureStrokeWidth) {
mGestureStroke = gestureStroke; mGestureStrokeWidth = gestureStrokeWidth;
mInvalidateExtraBorder = Math.max(1, ((int) mGestureStroke) - 1); mInvalidateExtraBorder = Math.max(1, ((int) gestureStrokeWidth) - 1);
mGesturePaint.setStrokeWidth(mGestureStroke); mGesturePaint.setStrokeWidth(gestureStrokeWidth);
} }
/** public int getGestureStrokeType() {
* Set the gesture to be shown in the view return mGestureStrokeType;
* }
* @param gesture
*/ public void setGestureStrokeType(int gestureStrokeType) {
public void setCurrentGesture(Gesture gesture) { mGestureStrokeType = gestureStrokeType;
}
public float getGestureStrokeLengthThreshold() {
return mGestureStrokeLengthThreshold;
}
public void setGestureStrokeLengthThreshold(float gestureStrokeLengthThreshold) {
mGestureStrokeLengthThreshold = gestureStrokeLengthThreshold;
}
public float getGestureStrokeSquarenessTreshold() {
return mGestureStrokeSquarenessTreshold;
}
public void setGestureStrokeSquarenessTreshold(float gestureStrokeSquarenessTreshold) {
mGestureStrokeSquarenessTreshold = gestureStrokeSquarenessTreshold;
}
public float getGestureStrokeAngleThreshold() {
return mGestureStrokeAngleThreshold;
}
public void setGestureStrokeAngleThreshold(float gestureStrokeAngleThreshold) {
mGestureStrokeAngleThreshold = gestureStrokeAngleThreshold;
}
public boolean isEventsInterceptionEnabled() {
return mInterceptEvents;
}
public void setEventsInterceptionEnabled(boolean enabled) {
mInterceptEvents = enabled;
}
public Gesture getGesture() {
return mCurrentGesture;
}
public void setGesture(Gesture gesture) {
if (mCurrentGesture != null) { if (mCurrentGesture != null) {
clear(false); clear(false);
} }
setCurrentColor(mCertainGestureColor);
mCurrentGesture = gesture; mCurrentGesture = gesture;
if (gesture != null) { final Path path = mCurrentGesture.toPath();
if (mBitmapCanvas != null) { final RectF bounds = new RectF();
gesture.draw(mBitmapCanvas, mGesturePaint); path.computeBounds(bounds, true);
invalidate();
}
}
}
private void init() { mPath.rewind();
mGesturePaint = new Paint(); mPath.addPath(path, (getWidth() - bounds.width()) / 2.0f,
(getHeight() - bounds.height()) / 2.0f);
final Paint gesturePaint = mGesturePaint; invalidate();
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);
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
if (width <= 0 || height <= 0) {
return;
}
int targetWidth = width > oldWidth ? width : oldWidth;
int targetHeight = height > oldHeight ? height : oldHeight;
if (mBitmap != null) mBitmap.recycle();
mBitmap = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
if (mBitmapCanvas != null) {
mBitmapCanvas.setBitmap(mBitmap);
} else {
mBitmapCanvas = new Canvas(mBitmap);
}
mBitmapCanvas.drawColor(TRANSPARENT_BACKGROUND);
if (mCurrentGesture != null) {
mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
}
} }
public void addOnGestureListener(OnGestureListener listener) { public void addOnGestureListener(OnGestureListener listener) {
@@ -257,100 +273,172 @@ public class GestureOverlayView extends View {
mOnGestureListeners.clear(); mOnGestureListeners.clear();
} }
@Override public void addOnGesturePerformedListener(OnGesturePerformedListener listener) {
protected void onDraw(Canvas canvas) { mOnGesturePerformedListeners.add(listener);
super.onDraw(canvas); if (mOnGesturePerformedListeners.size() > 0) {
mHandleGestureActions = true;
// draw double buffer
if (mIsFadingOut) {
mBitmapPaint.setAlpha((int) (255 * mFadingAlpha));
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
} else {
mBitmapPaint.setAlpha(255);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
} }
// draw the current stroke
canvas.drawPath(mPath, mGesturePaint);
} }
/** public void removeOnGesturePerformedListener(OnGesturePerformedListener listener) {
* Clear up the overlay mOnGesturePerformedListeners.remove(listener);
* if (mOnGesturePerformedListeners.size() <= 0) {
* @param fadeOut whether the gesture on the overlay should fade out mHandleGestureActions = false;
* gradually or disappear immediately }
*/ }
public void clear(boolean fadeOut) {
if (fadeOut) { public void removeAllOnGesturePerformedListeners() {
mOnGesturePerformedListeners.clear();
mHandleGestureActions = false;
}
public boolean isGesturing() {
return mIsGesturing;
}
private void setCurrentColor(int color) {
mCurrentColor = color;
if (mFadingHasStarted) {
setPaintAlpha((int) (255 * mFadingAlpha));
} else {
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));
}
public void clear(boolean animated) {
clear(animated, false);
}
private void clear(boolean animated, boolean fireActionPerformed) {
setPaintAlpha(255);
if (animated && mCurrentGesture != null) {
mFadingAlpha = 1.0f; mFadingAlpha = 1.0f;
mIsFadingOut = true; mIsFadingOut = true;
mFadingHasStarted = false;
mFadingOut.fireActionPerformed = fireActionPerformed;
removeCallbacks(mFadingOut); removeCallbacks(mFadingOut);
mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset; mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;
postDelayed(mFadingOut, mFadeOffset); postDelayed(mFadingOut, mFadeOffset);
} else { } else {
mPath.rewind(); mPath.rewind();
mCurrentGesture = null; mCurrentGesture = null;
if (mBitmap != null) {
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
invalidate();
}
} }
} }
public void cancelFadingOut() { public void cancelClearAnimation() {
setPaintAlpha(255);
mIsFadingOut = false; mIsFadingOut = false;
mFadingHasStarted = false;
removeCallbacks(mFadingOut); 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 @Override
public boolean onTouchEvent(MotionEvent event) { protected void onDetachedFromWindow() {
if (!isEnabled()) { 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; return true;
} }
processEvent(event); return super.dispatchTouchEvent(event);
return true;
} }
public void processEvent(MotionEvent event) { private boolean processEvent(MotionEvent event) {
switch (event.getAction()) { switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
Rect rect = touchStart(event); touchStart(event);
invalidate(rect); invalidate();
break; return true;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
rect = touchMove(event); if (mIsListeningForGestures) {
if (rect != null) { Rect rect = touchMove(event);
invalidate(rect); if (rect != null) {
invalidate(rect);
}
return true;
} }
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
touchUp(event, false); if (mIsListeningForGestures) {
invalidate(); touchUp(event, false);
invalidate();
return true;
}
break; break;
case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_CANCEL:
touchUp(event, true); if (mIsListeningForGestures) {
invalidate(); touchUp(event, true);
break; invalidate();
return true;
}
} }
return false;
} }
private Rect touchStart(MotionEvent event) { private void touchStart(MotionEvent event) {
mIsListeningForGestures = true;
// pass the event to handlers // pass the event to handlers
final ArrayList<OnGestureListener> listeners = mOnGestureListeners; final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
final int count = listeners.size(); final int count = listeners.size();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
OnGestureListener listener = listeners.get(i); listeners.get(i).onGestureStarted(this, event);
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;
} }
float x = event.getX(); float x = event.getX();
@@ -359,22 +447,39 @@ public class GestureOverlayView extends View {
mX = x; mX = x;
mY = y; 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) { if (mCurrentGesture == null) {
mCurrentGesture = new Gesture(); mCurrentGesture = new Gesture();
} }
mPointBuffer.add(new GesturePoint(x, y, event.getEventTime())); mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
mPath.rewind();
mPath.moveTo(x, y); mPath.moveTo(x, y);
mInvalidRect.set((int) x - mInvalidateExtraBorder, (int) y - mInvalidateExtraBorder, final int border = mInvalidateExtraBorder;
(int) x + mInvalidateExtraBorder, (int) y + mInvalidateExtraBorder); mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border);
mCurveEndX = x; mCurveEndX = x;
mCurveEndY = y; mCurveEndY = y;
return mInvalidRect;
} }
private Rect touchMove(MotionEvent event) { private Rect touchMove(MotionEvent event) {
@@ -393,36 +498,28 @@ public class GestureOverlayView extends View {
areaToRefresh = mInvalidRect; areaToRefresh = mInvalidRect;
// start with the curve end // start with the curve end
areaToRefresh.set( final int border = mInvalidateExtraBorder;
(int) mCurveEndX - mInvalidateExtraBorder, areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border,
(int) mCurveEndY - mInvalidateExtraBorder, (int) mCurveEndX + border, (int) mCurveEndY + border);
(int) mCurveEndX + mInvalidateExtraBorder,
(int) mCurveEndY + mInvalidateExtraBorder);
mCurveEndX = (x + previousX) / 2; float cX = mCurveEndX = (x + previousX) / 2;
mCurveEndY = (y + previousY) / 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 // union with the control point of the new curve
areaToRefresh.union( areaToRefresh.union((int) previousX - border, (int) previousY - border,
(int) previousX - mInvalidateExtraBorder, (int) previousX + border, (int) previousY + border);
(int) previousY - mInvalidateExtraBorder,
(int) previousX + mInvalidateExtraBorder,
(int) previousY + mInvalidateExtraBorder);
// union with the end point of the new curve // union with the end point of the new curve
areaToRefresh.union( areaToRefresh.union((int) cX - border, (int) cY - border,
(int) mCurveEndX - mInvalidateExtraBorder, (int) cX + border, (int) cY + border);
(int) mCurveEndY - mInvalidateExtraBorder,
(int) mCurveEndX + mInvalidateExtraBorder,
(int) mCurveEndY + mInvalidateExtraBorder);
mX = x; mX = x;
mY = y; mY = y;
} }
mPointBuffer.add(new GesturePoint(x, y, event.getEventTime())); mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
// pass the event to handlers // pass the event to handlers
final ArrayList<OnGestureListener> listeners = mOnGestureListeners; final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
@@ -430,24 +527,48 @@ public class GestureOverlayView extends View {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
listeners.get(i).onGesture(this, event); 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; return areaToRefresh;
} }
private void touchUp(MotionEvent event, boolean cancel) { private void touchUp(MotionEvent event, boolean cancel) {
// add the stroke to the current gesture mIsListeningForGestures = false;
mCurrentGesture.addStroke(new GestureStroke(mPointBuffer));
// add the stroke to the double buffer // add the stroke to the current gesture
mBitmapCanvas.drawPath(mPath, mGesturePaint); mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));
if (!cancel) { if (!cancel) {
// pass the event to handlers // pass the event to handlers
final ArrayList<OnGestureListener> listeners = mOnGestureListeners; final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
final int count = listeners.size(); int count = listeners.size();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
listeners.get(i).onGestureEnded(this, event); listeners.get(i).onGestureEnded(this, event);
} }
if (mHandleGestureActions) {
clear(true, mIsGesturing);
}
} else { } else {
// pass the event to handlers // pass the event to handlers
final ArrayList<OnGestureListener> listeners = mOnGestureListeners; final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
@@ -455,15 +576,52 @@ public class GestureOverlayView extends View {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
listeners.get(i).onGestureCancelled(this, event); listeners.get(i).onGestureCancelled(this, event);
} }
clear(false);
} }
mPath.rewind(); mIsGesturing = false;
mPointBuffer.clear(); 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 { public static interface OnGestureListener {
void onGestureStarted(GestureOverlayView overlay, MotionEvent event); void onGestureStarted(GestureOverlayView overlay, MotionEvent event);
@@ -473,4 +631,8 @@ public class GestureOverlayView extends View {
void onGestureCancelled(GestureOverlayView overlay, MotionEvent event); void onGestureCancelled(GestureOverlayView overlay, MotionEvent event);
} }
public static interface OnGesturePerformedListener {
void onGesturePerformed(GestureOverlayView overlay, Gesture gesture);
}
} }

View File

@@ -89,39 +89,51 @@ public class GestureStroke {
*/ */
void draw(Canvas canvas, Paint paint) { void draw(Canvas canvas, Paint paint) {
if (mCachedPath == null) { if (mCachedPath == null) {
final float[] localPoints = points; makePath();
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);
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); canvas.drawPath(mCachedPath, paint);
} }
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);
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;
}
}
}
mCachedPath = path;
}
/** /**
* Convert the stroke to a Path based on the number of points * Convert the stroke to a Path based on the number of points
* *
@@ -158,8 +170,7 @@ public class GestureStroke {
} else { } else {
float dx = Math.abs(x - mX); float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY); float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
dy >= TOUCH_TOLERANCE) {
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x; mX = x;
mY = y; mY = y;

View File

@@ -388,7 +388,7 @@ final class GestureUtilities {
} else { // -PI<alpha<PI } else { // -PI<alpha<PI
angle = (float) Math.atan2(targetVector[1], targetVector[0]); angle = (float) Math.atan2(targetVector[1], targetVector[0]);
angle = (float) (180 * angle / Math.PI); angle = (float) (180 * angle / Math.PI);
android.graphics.Matrix trans = new android.graphics.Matrix(); Matrix trans = new Matrix();
trans.setRotate(-angle); trans.setRotate(-angle);
trans.mapPoints(points); trans.mapPoints(points);
} }

View File

@@ -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);
}
}

View File

@@ -49,7 +49,6 @@ import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.gesture.GestureOverlayView; import android.gesture.GestureOverlayView;
import android.gesture.TouchThroughGestureListener;
import android.gesture.Gesture; import android.gesture.Gesture;
import android.gesture.LetterRecognizer; import android.gesture.LetterRecognizer;
import android.gesture.Prediction; import android.gesture.Prediction;
@@ -472,7 +471,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private ViewTreeObserver.OnGlobalLayoutListener mGesturesLayoutListener; private ViewTreeObserver.OnGlobalLayoutListener mGesturesLayoutListener;
private boolean mGlobalLayoutListenerAddedGestures; private boolean mGlobalLayoutListenerAddedGestures;
private boolean mInstallGesturesOverlay; private boolean mInstallGesturesOverlay;
private TouchThroughGestureListener mGesturesListener;
private boolean mPreviousGesturing; private boolean mPreviousGesturing;
private boolean mGlobalLayoutListenerAddedFilter; private boolean mGlobalLayoutListenerAddedFilter;
@@ -736,10 +734,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mGesturesPopup = p; mGesturesPopup = p;
mGesturesOverlay.removeAllOnGestureListeners(); mGesturesOverlay.removeAllOnGestureListeners();
mGesturesListener = new TouchThroughGestureListener(null); mGesturesOverlay.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);
mGesturesListener.setGestureType(TouchThroughGestureListener.MULTIPLE_STROKE); mGesturesOverlay.addOnGesturePerformedListener(new GesturesProcessor());
mGesturesListener.addOnGestureActionListener(new GesturesProcessor());
mGesturesOverlay.addOnGestureListener(mGesturesListener);
mPreviousGesturing = false; mPreviousGesturing = false;
} }
@@ -756,19 +752,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
@Override @Override
public boolean dispatchTouchEvent(MotionEvent ev) { public boolean dispatchTouchEvent(MotionEvent ev) {
if (mGestures != GESTURES_NONE) { if (mGestures != GESTURES_NONE) {
mGesturesOverlay.processEvent(ev); if (ev.getAction() != MotionEvent.ACTION_DOWN || mFastScroller == null ||
!mFastScroller.isPointInside(ev.getX(), ev.getY())) {
final boolean isGesturing = mGesturesListener.isGesturing(); mGesturesOverlay.dispatchTouchEvent(ev);
if (!isGesturing) { final boolean isGesturing = mGesturesOverlay.isGesturing();
mPreviousGesturing = isGesturing;
return super.dispatchTouchEvent(ev); if (!isGesturing) {
} else if (!mPreviousGesturing){ mPreviousGesturing = isGesturing;
mPreviousGesturing = isGesturing; return super.dispatchTouchEvent(ev);
final MotionEvent event = MotionEvent.obtain(ev); } else if (!mPreviousGesturing){
event.setAction(MotionEvent.ACTION_CANCEL); mPreviousGesturing = isGesturing;
super.dispatchTouchEvent(event); final MotionEvent event = MotionEvent.obtain(ev);
return true; event.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(event);
return true;
}
} }
} }
@@ -2130,13 +2130,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
@Override @Override
public boolean onTouchEvent(MotionEvent ev) { public boolean onTouchEvent(MotionEvent ev) {
if (mFastScroller != null) { if (mFastScroller != null) {
boolean intercepted = mFastScroller.onTouchEvent(ev); boolean intercepted = mFastScroller.onTouchEvent(ev);
if (intercepted) { if (intercepted) {
return true; return true;
} }
} }
final int action = ev.getAction(); final int action = ev.getAction();
final int x = (int) ev.getX(); final int x = (int) ev.getX();
final int y = (int) ev.getY(); final int y = (int) ev.getY();
@@ -3848,8 +3848,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
} }
} }
private class GesturesProcessor implements private class GesturesProcessor implements GestureOverlayView.OnGesturePerformedListener {
TouchThroughGestureListener.OnGesturePerformedListener {
private static final double SCORE_THRESHOLD = 0.1; private static final double SCORE_THRESHOLD = 0.1;

View File

@@ -402,8 +402,7 @@ class FastScroller {
boolean onInterceptTouchEvent(MotionEvent ev) { boolean onInterceptTouchEvent(MotionEvent ev) {
if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) { if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) {
if (ev.getX() > mList.getWidth() - mThumbW && ev.getY() >= mThumbY && if (isPointInside(ev.getX(), ev.getY())) {
ev.getY() <= mThumbY + mThumbH) {
setState(STATE_DRAGGING); setState(STATE_DRAGGING);
return true; return true;
} }
@@ -415,11 +414,11 @@ class FastScroller {
if (mState == STATE_NONE) { if (mState == STATE_NONE) {
return false; return false;
} }
if (me.getAction() == MotionEvent.ACTION_DOWN) {
if (me.getX() > mList.getWidth() - mThumbW final int action = me.getAction();
&& me.getY() >= mThumbY
&& me.getY() <= mThumbY + mThumbH) { if (action == MotionEvent.ACTION_DOWN) {
if (isPointInside(me.getX(), me.getY())) {
setState(STATE_DRAGGING); setState(STATE_DRAGGING);
if (mListAdapter == null && mList != null) { if (mListAdapter == null && mList != null) {
getSectionsFromIndexer(); getSectionsFromIndexer();
@@ -428,7 +427,7 @@ class FastScroller {
cancelFling(); cancelFling();
return true; return true;
} }
} else if (me.getAction() == MotionEvent.ACTION_UP) { } else if (action == MotionEvent.ACTION_UP) {
if (mState == STATE_DRAGGING) { if (mState == STATE_DRAGGING) {
setState(STATE_VISIBLE); setState(STATE_VISIBLE);
final Handler handler = mHandler; final Handler handler = mHandler;
@@ -436,7 +435,7 @@ class FastScroller {
handler.postDelayed(mScrollFade, 1000); handler.postDelayed(mScrollFade, 1000);
return true; return true;
} }
} else if (me.getAction() == MotionEvent.ACTION_MOVE) { } else if (action == MotionEvent.ACTION_MOVE) {
if (mState == STATE_DRAGGING) { if (mState == STATE_DRAGGING) {
final int viewHeight = mList.getHeight(); final int viewHeight = mList.getHeight();
// Jitter // Jitter
@@ -460,6 +459,10 @@ class FastScroller {
return false; return false;
} }
boolean isPointInside(float x, float y) {
return x > mList.getWidth() - mThumbW && y >= mThumbY && y <= mThumbY + mThumbH;
}
public class ScrollFade implements Runnable { public class ScrollFade implements Runnable {
long mStartTime; long mStartTime;

View File

@@ -353,25 +353,24 @@ public class FrameLayout extends ViewGroup {
if (mForeground != null) { if (mForeground != null) {
final Drawable foreground = mForeground; final Drawable foreground = mForeground;
if (mForegroundBoundsChanged) { if (mForegroundBoundsChanged) {
mForegroundBoundsChanged = false; mForegroundBoundsChanged = false;
if (foreground != null) { final Rect selfBounds = mSelfBounds;
final Rect selfBounds = mSelfBounds; final Rect overlayBounds = mOverlayBounds;
final Rect overlayBounds = mOverlayBounds;
final int w = mRight-mLeft; final int w = mRight-mLeft;
final int h = mBottom-mTop; final int h = mBottom-mTop;
if (mForegroundInPadding) {
selfBounds.set(0, 0, w, h);
} else {
selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
}
Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), if (mForegroundInPadding) {
foreground.getIntrinsicHeight(), selfBounds, overlayBounds); selfBounds.set(0, 0, w, h);
foreground.setBounds(overlayBounds); } else {
selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
} }
Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
foreground.setBounds(overlayBounds);
} }
foreground.draw(canvas); foreground.draw(canvas);

View File

@@ -2091,6 +2091,22 @@
<!-- Duration, in milliseconds, of the fade out effect after the user is done <!-- Duration, in milliseconds, of the fade out effect after the user is done
drawing a gesture. --> drawing a gesture. -->
<attr name="fadeDuration" format="integer" /> <attr name="fadeDuration" format="integer" />
<!-- Defines the type of strokes that define a gesture. -->
<attr name="gestureStrokeType">
<!-- A gesture is made of only one stroke. -->
<enum name="single" value="0" />
<!-- A gesture is made of multiple strokes. -->
<enum name="multiple" value="1" />
</attr>
<!-- Minimum length of a stroke before it is recognized as a gesture. -->
<attr name="gestureStrokeLengthThreshold" format="float" />
<!-- Squareness threshold of a stroke before it is recognized as a gesture. -->
<attr name="gestureStrokeSquarenessThreshold" format="float" />
<!-- Minimum curve angle a stroke must contain before it is recognized as a gesture. -->
<attr name="gestureStrokeAngleThreshold" format="float" />
<!-- Defines whether the overlay should intercept the motion events when a gesture
is recognized. -->
<attr name="eventsInterceptionEnabled" format="boolean" />
</declare-styleable> </declare-styleable>
<!-- ======================================= --> <!-- ======================================= -->

View File

@@ -1107,6 +1107,11 @@
<public type="attr" name="fadeOffset" /> <public type="attr" name="fadeOffset" />
<public type="attr" name="fadeDuration" /> <public type="attr" name="fadeDuration" />
<public type="attr" name="gestures" /> <public type="attr" name="gestures" />
<public type="attr" name="gestureStrokeType" />
<public type="attr" name="gestureStrokeLengthThreshold" />
<public type="attr" name="gestureStrokeSquarenessThreshold" />
<public type="attr" name="gestureStrokeAngleThreshold" />
<public type="attr" name="eventsInterceptionEnabled" />
<public-padding type="attr" name="donut_resource_pad" end="0x0101029f" /> <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />

View File

@@ -174,14 +174,18 @@
<style name="Widget.GestureOverlayView"> <style name="Widget.GestureOverlayView">
<item name="android:gestureStrokeWidth">12.0</item> <item name="android:gestureStrokeWidth">12.0</item>
<item name="android:gestureColor">#ffffff00</item> <item name="android:gestureColor">#ffffff00</item>
<item name="android:uncertainGestureColor">#3cffff00</item> <item name="android:uncertainGestureColor">#48ffff00</item>
<item name="android:fadeOffset">300</item> <item name="android:fadeOffset">420</item>
<item name="android:fadeDuration">300</item> <item name="android:fadeDuration">150</item>
<item name="android:gestureStrokeLengthThreshold">30.0</item>
<item name="android:gestureStrokeSquarenessThreshold">0.275</item>
<item name="android:gestureStrokeAngleThreshold">40.0</item>
<item name="android:eventsInterceptionEnabled">true</item>
</style> </style>
<style name="Widget.GestureOverlayView.White"> <style name="Widget.GestureOverlayView.White">
<item name="android:gestureColor">#ff00ff00</item> <item name="android:gestureColor">#ff00ff00</item>
<item name="android:uncertainGestureColor">#3c00ff00</item> <item name="android:uncertainGestureColor">#4800ff00</item>
</style> </style>
<style name="Widget.Button"> <style name="Widget.Button">

View File

@@ -17,7 +17,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"> android:layout_height="fill_parent">
<Spinner <Spinner
android:id="@+id/spinner" android:id="@+id/spinner"
android:layout_width="fill_parent" android:layout_width="fill_parent"
@@ -29,6 +30,6 @@
android:id="@+id/drawingpad" android:id="@+id/drawingpad"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"/> android:layout_weight="1" />
</LinearLayout> </LinearLayout>

View File

@@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"> android:layout_height="fill_parent">
<Spinner <Spinner
android:id="@+id/spinner" android:id="@+id/spinner"
@@ -30,7 +30,7 @@
android:id="@+id/drawingpad" android:id="@+id/drawingpad"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"/> android:layout_weight="1" />
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"

View File

@@ -14,7 +14,14 @@
limitations under the License. limitations under the License.
--> -->
<ListView xmlns:android="http://schemas.android.com/apk/res/android" <android.gesture.GestureOverlayView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list" android:id="@+id/overlay"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" /> android:layout_height="fill_parent">
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</android.gesture.GestureOverlayView>

View File

@@ -25,7 +25,6 @@ import android.os.Bundle;
import android.provider.Contacts.People; import android.provider.Contacts.People;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ListView; import android.widget.ListView;
@@ -34,16 +33,12 @@ import android.gesture.Gesture;
import android.gesture.GestureOverlayView; import android.gesture.GestureOverlayView;
import android.gesture.LetterRecognizer; import android.gesture.LetterRecognizer;
import android.gesture.Prediction; import android.gesture.Prediction;
import android.gesture.TouchThroughGestureListener;
import java.util.ArrayList; import java.util.ArrayList;
public class ContactListGestureOverlay extends Activity { public class ContactListGestureOverlay extends Activity {
private static final String LOG_TAG = "ContactListGestureOverlay";
private static final String LOGTAG = "ContactListGestureOverlay";
private static final String SORT_ORDER = People.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; private static final String SORT_ORDER = People.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
private static final String[] CONTACTS_PROJECTION = new String[] { private static final String[] CONTACTS_PROJECTION = new String[] {
People._ID, // 0 People._ID, // 0
People.DISPLAY_NAME, // 1 People.DISPLAY_NAME, // 1
@@ -51,11 +46,9 @@ public class ContactListGestureOverlay extends Activity {
private ContactAdapter mContactAdapter; private ContactAdapter mContactAdapter;
private TouchThroughGestureListener mGestureProcessor;
private LetterRecognizer mRecognizer;
private ListView mContactList; private ListView mContactList;
private LetterRecognizer mRecognizer;
private GestureOverlayView mOverlay;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@@ -63,10 +56,10 @@ public class ContactListGestureOverlay extends Activity {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.overlaydemo); setContentView(R.layout.overlaydemo);
setProgressBarIndeterminateVisibility(true);
// create a letter recognizer // create a letter recognizer
mRecognizer = LetterRecognizer.getLetterRecognizer(this, LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE); mRecognizer = LetterRecognizer.getLetterRecognizer(this,
LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE);
mOverlay = (GestureOverlayView) findViewById(R.id.overlay);
// load the contact list // load the contact list
mContactList = (ListView) findViewById(R.id.list); mContactList = (ListView) findViewById(R.id.list);
@@ -74,13 +67,14 @@ public class ContactListGestureOverlay extends Activity {
mContactList.setTextFilterEnabled(true); mContactList.setTextFilterEnabled(true);
mContactList.setOnItemClickListener(new AdapterView.OnItemClickListener() { mContactList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) { public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
if (!mGestureProcessor.isGesturing()) { if (!mOverlay.isGesturing()) {
Intent intent = new Intent(Intent.ACTION_VIEW, ContentUris.withAppendedId( Intent intent = new Intent(Intent.ACTION_VIEW, ContentUris.withAppendedId(
People.CONTENT_URI, id)); People.CONTENT_URI, id));
startActivity(intent); startActivity(intent);
} }
} }
}); });
ContentResolver resolver = getContentResolver(); ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(People.CONTENT_URI, CONTACTS_PROJECTION, null, null, Cursor cursor = resolver.query(People.CONTENT_URI, CONTACTS_PROJECTION, null, null,
SORT_ORDER); SORT_ORDER);
@@ -91,21 +85,16 @@ public class ContactListGestureOverlay extends Activity {
mContactAdapter = new ContactAdapter(this, list); mContactAdapter = new ContactAdapter(this, list);
mContactList.setAdapter(mContactAdapter); mContactList.setAdapter(mContactAdapter);
setProgressBarIndeterminateVisibility(false); mOverlay.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);
mOverlay.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() {
// add a gesture overlay on top of the ListView
GestureOverlayView overlay = new GestureOverlayView(this);
mGestureProcessor = new TouchThroughGestureListener(mContactList);
mGestureProcessor.setGestureType(TouchThroughGestureListener.MULTIPLE_STROKE);
mGestureProcessor.addOnGestureActionListener(new TouchThroughGestureListener.OnGesturePerformedListener() {
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) { public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<Prediction> predictions = mRecognizer.recognize(gesture); ArrayList<Prediction> predictions = mRecognizer.recognize(gesture);
if (!predictions.isEmpty()) { if (!predictions.isEmpty()) {
Log.v(LOGTAG, "1st Prediction : " + predictions.get(0).name + Log.v(LOG_TAG, "1st Prediction : " + predictions.get(0).name +
" @" + predictions.get(0).score); " @" + predictions.get(0).score);
Log.v(LOGTAG, "2nd Prediction : " + predictions.get(1).name + Log.v(LOG_TAG, "2nd Prediction : " + predictions.get(1).name +
" @" + predictions.get(1).score); " @" + predictions.get(1).score);
Log.v(LOGTAG, "3rd Prediction : " + predictions.get(2).name + Log.v(LOG_TAG, "3rd Prediction : " + predictions.get(2).name +
" @" + predictions.get(2).score); " @" + predictions.get(2).score);
int index = mContactAdapter.search(predictions.get(0).name); int index = mContactAdapter.search(predictions.get(0).name);
if (index != -1) { if (index != -1) {
@@ -114,9 +103,5 @@ public class ContactListGestureOverlay extends Activity {
} }
} }
}); });
overlay.addOnGestureListener(mGestureProcessor);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT);
this.addContentView(overlay, params);
} }
} }

View File

@@ -47,8 +47,9 @@ public class GestureEntry extends Activity {
private static final String PARCEL_KEY = "gesture"; private static final String PARCEL_KEY = "gesture";
static final String GESTURE_FILE_NAME = Environment.getExternalStorageDirectory().getAbsolutePath() static final String GESTURE_FILE_NAME =
+ File.separator + "demo_library.gestures"; Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator +
"demo_library.gestures";
private static final int DIALOG_NEW_ENTRY = 1; private static final int DIALOG_NEW_ENTRY = 1;
@@ -82,7 +83,7 @@ public class GestureEntry extends Activity {
// correct the recognition result by adding the new example // correct the recognition result by adding the new example
if (!mChangedByRecognizer) { if (!mChangedByRecognizer) {
mGestureLibrary.addGesture(parent.getSelectedItem().toString(), mGesturePad mGestureLibrary.addGesture(parent.getSelectedItem().toString(), mGesturePad
.getCurrentGesture()); .getGesture());
} else { } else {
mChangedByRecognizer = false; mChangedByRecognizer = false;
} }
@@ -99,7 +100,7 @@ public class GestureEntry extends Activity {
mGesturePad.setBackgroundColor(Color.BLACK); mGesturePad.setBackgroundColor(Color.BLACK);
mGesturePad.addOnGestureListener(new GestureOverlayView.OnGestureListener() { mGesturePad.addOnGestureListener(new GestureOverlayView.OnGestureListener() {
public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) { public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
recognize(overlay.getCurrentGesture()); recognize(overlay.getGesture());
} }
public void onGesture(GestureOverlayView overlay, MotionEvent event) { public void onGesture(GestureOverlayView overlay, MotionEvent event) {
@@ -116,7 +117,7 @@ public class GestureEntry extends Activity {
if (savedInstanceState != null) { if (savedInstanceState != null) {
Gesture gesture = (Gesture) savedInstanceState.getParcelable(PARCEL_KEY); Gesture gesture = (Gesture) savedInstanceState.getParcelable(PARCEL_KEY);
if (gesture != null) { if (gesture != null) {
mGesturePad.setCurrentGesture(gesture); mGesturePad.setGesture(gesture);
} }
} }
} }
@@ -133,7 +134,7 @@ public class GestureEntry extends Activity {
.findViewById(R.id.gesturename_edit); .findViewById(R.id.gesturename_edit);
String text = edittext.getText().toString().trim(); String text = edittext.getText().toString().trim();
if (text.length() > 0) { if (text.length() > 0) {
mGestureLibrary.addGesture(text, mGesturePad.getCurrentGesture()); mGestureLibrary.addGesture(text, mGesturePad.getGesture());
} }
} }
}).setNegativeButton(R.string.newgesture_dialog_cancel, }).setNegativeButton(R.string.newgesture_dialog_cancel,
@@ -157,7 +158,7 @@ public class GestureEntry extends Activity {
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case NEW_ID: case NEW_ID:
if (mGesturePad.getCurrentGesture() != null) { if (mGesturePad.getGesture() != null) {
showDialog(DIALOG_NEW_ENTRY); showDialog(DIALOG_NEW_ENTRY);
} }
break; break;
@@ -190,7 +191,7 @@ public class GestureEntry extends Activity {
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
Gesture gesture = mGesturePad.getCurrentGesture(); Gesture gesture = mGesturePad.getGesture();
if (gesture != null) { if (gesture != null) {
outState.putParcelable(PARCEL_KEY, gesture); outState.putParcelable(PARCEL_KEY, gesture);
} }

View File

@@ -78,7 +78,7 @@ public class GestureLibViewer extends Activity {
mCurrentGestureIndex--; mCurrentGestureIndex--;
} }
gesture = mGestures.get(mCurrentGestureIndex); gesture = mGestures.get(mCurrentGestureIndex);
mGesturePad.setCurrentGesture(gesture); mGesturePad.setGesture(gesture);
mGesturePad.invalidate(); mGesturePad.invalidate();
} }
} }
@@ -109,7 +109,7 @@ public class GestureLibViewer extends Activity {
mGestures = mGesureLibrary.getGestures(list.get(0)); mGestures = mGesureLibrary.getGestures(list.get(0));
mCurrentGestureIndex = 0; mCurrentGestureIndex = 0;
Gesture gesture = mGestures.get(mCurrentGestureIndex); Gesture gesture = mGestures.get(mCurrentGestureIndex);
mGesturePad.setCurrentGesture(gesture); mGesturePad.setGesture(gesture);
} }
mGestureCategory.setOnItemSelectedListener(new OnItemSelectedListener() { mGestureCategory.setOnItemSelectedListener(new OnItemSelectedListener() {
@@ -118,7 +118,7 @@ public class GestureLibViewer extends Activity {
if (!mGestures.isEmpty()) { if (!mGestures.isEmpty()) {
mCurrentGestureIndex = 0; mCurrentGestureIndex = 0;
Gesture gesture = mGestures.get(mCurrentGestureIndex); Gesture gesture = mGestures.get(mCurrentGestureIndex);
mGesturePad.setCurrentGesture(gesture); mGesturePad.setGesture(gesture);
} }
mGesturePad.invalidate(); mGesturePad.invalidate();
} }
@@ -139,7 +139,7 @@ public class GestureLibViewer extends Activity {
} }
mCurrentGestureIndex++; mCurrentGestureIndex++;
Gesture gesture = mGestures.get(mCurrentGestureIndex); Gesture gesture = mGestures.get(mCurrentGestureIndex);
mGesturePad.setCurrentGesture(gesture); mGesturePad.setGesture(gesture);
mGesturePad.invalidate(); mGesturePad.invalidate();
} }
}); });
@@ -150,7 +150,7 @@ public class GestureLibViewer extends Activity {
if (mCurrentGestureIndex >= 1 && !mGestures.isEmpty()) { if (mCurrentGestureIndex >= 1 && !mGestures.isEmpty()) {
mCurrentGestureIndex--; mCurrentGestureIndex--;
Gesture gesture = mGestures.get(mCurrentGestureIndex); Gesture gesture = mGestures.get(mCurrentGestureIndex);
mGesturePad.setCurrentGesture(gesture); mGesturePad.setGesture(gesture);
mGesturePad.invalidate(); mGesturePad.invalidate();
} }
} }