Revert "Squashed commit of the following:"
This reverts commit f8173411cc.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Copyright (C) 2008-2009 The Android Open Source Project
|
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,12 +14,11 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.android.gesture.example"
|
package="com.android.gesture"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:versionName="1.0.0">
|
android:versionName="1.0.0">
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
|
||||||
<application android:icon="@drawable/icon" android:label="@string/app_name">
|
<application android:icon="@drawable/icon" android:label="@string/app_name">
|
||||||
<activity android:name="com.android.gesture.example.GestureEntry"
|
<activity android:name="com.android.gesture.example.GestureEntryDemo"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@@ -27,12 +26,5 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.android.gesture.example.GestureLibViewer"/>
|
<activity android:name="com.android.gesture.example.GestureLibViewer"/>
|
||||||
<activity android:name="com.android.gesture.example.ContactListGestureOverlay"
|
|
||||||
android:label="@string/overlay_name">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
5
tests/sketch/res/layout/demo.xml
Normal file → Executable file
5
tests/sketch/res/layout/demo.xml
Normal file → Executable file
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<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"
|
||||||
@@ -25,7 +24,7 @@
|
|||||||
android:drawSelectorOnTop="true"
|
android:drawSelectorOnTop="true"
|
||||||
android:prompt="@string/recognition_result"/>
|
android:prompt="@string/recognition_result"/>
|
||||||
|
|
||||||
<com.android.gesture.GestureOverlay
|
<com.android.gesture.GesturePad
|
||||||
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"
|
||||||
|
|||||||
5
tests/sketch/res/layout/gestureviewer.xml
Normal file → Executable file
5
tests/sketch/res/layout/gestureviewer.xml
Normal file → Executable file
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<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"
|
||||||
@@ -26,7 +25,7 @@
|
|||||||
android:drawSelectorOnTop="true"
|
android:drawSelectorOnTop="true"
|
||||||
android:prompt="@string/recognition_result"/>
|
android:prompt="@string/recognition_result"/>
|
||||||
|
|
||||||
<com.android.gesture.GestureOverlay
|
<com.android.gesture.GesturePad
|
||||||
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"
|
||||||
|
|||||||
2
tests/sketch/res/layout/newgesture_dialog.xml
Normal file → Executable file
2
tests/sketch/res/layout/newgesture_dialog.xml
Normal file → Executable file
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
>
|
|
||||||
<ListView
|
|
||||||
android:id="@+id/list"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="0dip"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Gesture Demo</string>
|
<string name="app_name">Gesture Demo</string>
|
||||||
<string name="overlay_name">Overlay Demo</string>
|
|
||||||
<string name="recognition_result">Recognition Result</string>
|
<string name="recognition_result">Recognition Result</string>
|
||||||
<string name="clear">Clear</string>
|
<string name="clear">Clear</string>
|
||||||
<string name="newgesture">Add</string>
|
<string name="newgesture">Add</string>
|
||||||
|
|||||||
@@ -18,255 +18,320 @@ package com.android.gesture;
|
|||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.PointF;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import com.android.gesture.recognizer.RecognitionUtil;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A gesture can have a single or multiple strokes
|
* A single stroke gesture.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class Gesture implements Parcelable {
|
public class Gesture implements Parcelable {
|
||||||
|
|
||||||
private static final long GESTURE_ID_BASE = System.currentTimeMillis();
|
private RectF mBBX;
|
||||||
|
private float mLength = 0;
|
||||||
|
private int mColor;
|
||||||
|
private float mWidth;
|
||||||
|
private ArrayList<PointF> mPtsBuffer = new ArrayList<PointF>();
|
||||||
|
private long mTimestamp = 0;
|
||||||
|
private long mID;
|
||||||
|
|
||||||
private static final int BITMAP_RENDERING_WIDTH = 2;
|
private static final long systemStartupTime = System.currentTimeMillis();
|
||||||
|
private static int instanceCount = 0;
|
||||||
private static final boolean BITMAP_RENDERING_ANTIALIAS = true;
|
|
||||||
|
|
||||||
private static final boolean BITMAP_RENDERING_DITHER = true;
|
|
||||||
|
|
||||||
private static int sGestureCount = 0;
|
|
||||||
|
|
||||||
private RectF mBoundingBox;
|
|
||||||
|
|
||||||
// the same as its instance ID
|
|
||||||
private long mGestureID;
|
|
||||||
|
|
||||||
private ArrayList<GestureStroke> mStrokes = new ArrayList<GestureStroke>();
|
|
||||||
|
|
||||||
public Gesture() {
|
public Gesture() {
|
||||||
mGestureID = GESTURE_ID_BASE + sGestureCount++;
|
mID = systemStartupTime + instanceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setColor(int c) {
|
||||||
* @return all the strokes of the gesture
|
mColor = c;
|
||||||
*/
|
|
||||||
public ArrayList<GestureStroke> getStrokes() {
|
|
||||||
return mStrokes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setStrokeWidth(float w) {
|
||||||
* @return the number of strokes included by this gesture
|
mWidth = w;
|
||||||
*/
|
|
||||||
public int getStrokesCount() {
|
|
||||||
return mStrokes.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public int getColor() {
|
||||||
* Add a stroke to the gesture
|
return mColor;
|
||||||
*
|
}
|
||||||
* @param stroke
|
|
||||||
*/
|
|
||||||
public void addStroke(GestureStroke stroke) {
|
|
||||||
mStrokes.add(stroke);
|
|
||||||
|
|
||||||
if (mBoundingBox == null) {
|
public float getStrokeWidth() {
|
||||||
mBoundingBox = new RectF(stroke.boundingBox);
|
return mWidth;
|
||||||
} else {
|
}
|
||||||
mBoundingBox.union(stroke.boundingBox);
|
|
||||||
|
public ArrayList<PointF> getPoints() {
|
||||||
|
return this.mPtsBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int numOfPoints() {
|
||||||
|
return this.mPtsBuffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPoint(float x, float y) {
|
||||||
|
mPtsBuffer.add(new PointF(x, y));
|
||||||
|
if (mBBX == null) {
|
||||||
|
mBBX = new RectF();
|
||||||
|
mBBX.top = y;
|
||||||
|
mBBX.left = x;
|
||||||
|
mBBX.right = x;
|
||||||
|
mBBX.bottom = y;
|
||||||
|
mLength = 0;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
PointF lst = mPtsBuffer.get(mPtsBuffer.size()-2);
|
||||||
|
mLength += Math.sqrt(Math.pow(x-lst.x, 2)+Math.pow(y-lst.y, 2));
|
||||||
|
mBBX.union(x, y);
|
||||||
|
}
|
||||||
|
mTimestamp = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the total length of the gesture. When there are multiple strokes in
|
|
||||||
* the gesture, this returns the sum of the lengths of all the strokes
|
|
||||||
*
|
|
||||||
* @return the length of the gesture
|
* @return the length of the gesture
|
||||||
*/
|
*/
|
||||||
public float getLength() {
|
public float getLength() {
|
||||||
int len = 0;
|
return this.mLength;
|
||||||
ArrayList<GestureStroke> strokes = mStrokes;
|
|
||||||
int count = strokes.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureStroke stroke = strokes.get(i);
|
|
||||||
len += stroke.length;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public RectF getBBX() {
|
||||||
* @return the bounding box of the gesture
|
return mBBX;
|
||||||
*/
|
|
||||||
public RectF getBoundingBox() {
|
|
||||||
return mBoundingBox;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setID(long id) {
|
||||||
* Set the id of the gesture
|
mID = id;
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
void setID(long id) {
|
|
||||||
mGestureID = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the id of the gesture
|
|
||||||
*/
|
|
||||||
public long getID() {
|
public long getID() {
|
||||||
return mGestureID;
|
return mID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimeStamp() {
|
||||||
|
return mTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(long t) {
|
||||||
|
this.mTimestamp = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* draw the gesture
|
* draw the gesture
|
||||||
*
|
|
||||||
* @param canvas
|
* @param canvas
|
||||||
*/
|
*/
|
||||||
void draw(Canvas canvas, Paint paint) {
|
public void draw(Canvas canvas) {
|
||||||
ArrayList<GestureStroke> strokes = mStrokes;
|
|
||||||
int count = strokes.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureStroke stroke = strokes.get(i);
|
|
||||||
stroke.draw(canvas, paint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a bitmap of the gesture with a transparent background
|
|
||||||
*
|
|
||||||
* @param width width of the target bitmap
|
|
||||||
* @param height height of the target bitmap
|
|
||||||
* @param edge the edge
|
|
||||||
* @param numSample
|
|
||||||
* @param color
|
|
||||||
* @return the bitmap
|
|
||||||
*/
|
|
||||||
public Bitmap toBitmap(int width, int height, int edge, int numSample, int color) {
|
|
||||||
RectF bbx = getBoundingBox();
|
|
||||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
||||||
Canvas canvas = new Canvas(bitmap);
|
|
||||||
canvas.translate(edge, edge);
|
|
||||||
Paint paint = new Paint();
|
Paint paint = new Paint();
|
||||||
paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS);
|
paint.setAntiAlias(true);
|
||||||
paint.setDither(BITMAP_RENDERING_DITHER);
|
paint.setDither(true);
|
||||||
paint.setColor(color);
|
paint.setColor(mColor);
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
paint.setStrokeJoin(Paint.Join.ROUND);
|
paint.setStrokeJoin(Paint.Join.ROUND);
|
||||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
paint.setStrokeWidth(BITMAP_RENDERING_WIDTH);
|
paint.setStrokeWidth(mWidth);
|
||||||
ArrayList<GestureStroke> strokes = mStrokes;
|
|
||||||
int count = strokes.size();
|
Path path = null;
|
||||||
for (int i = 0; i < count; i++) {
|
float mX = 0, mY = 0;
|
||||||
GestureStroke stroke = strokes.get(i);
|
Iterator<PointF> it = mPtsBuffer.iterator();
|
||||||
Path path = stroke.toPath(width - 2 * edge, height - 2 * edge, numSample);
|
while (it.hasNext()) {
|
||||||
canvas.drawPath(path, paint);
|
PointF p = it.next();
|
||||||
|
float x = p.x;
|
||||||
|
float y = p.y;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmap;
|
canvas.drawPath(path, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a bitmap of the gesture with a transparent background
|
* convert the gesture to a Path
|
||||||
*
|
* @param width the width of the bounding box of the target path
|
||||||
* @param width
|
* @param height the height of the bounding box of the target path
|
||||||
* @param height
|
* @param numSample the num of points needed
|
||||||
|
* @return the path
|
||||||
|
*/
|
||||||
|
public Path toPath(float width, float height, int numSample) {
|
||||||
|
float[] pts = RecognitionUtil.resample(this, numSample);
|
||||||
|
RectF rect = this.getBBX();
|
||||||
|
float scale = height / rect.height();
|
||||||
|
Matrix matrix = new Matrix();
|
||||||
|
matrix.setTranslate(-rect.left, -rect.top);
|
||||||
|
Matrix scalem = new Matrix();
|
||||||
|
scalem.setScale(scale, scale);
|
||||||
|
matrix.postConcat(scalem);
|
||||||
|
Matrix translate = new Matrix();
|
||||||
|
matrix.postConcat(translate);
|
||||||
|
matrix.mapPoints(pts);
|
||||||
|
|
||||||
|
Path path = null;
|
||||||
|
float mX = 0, mY = 0;
|
||||||
|
for (int i=0; i<pts.length-1; i+=2) {
|
||||||
|
float x = pts[i];
|
||||||
|
float y = pts[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a bitmap thumbnail of the gesture with a transparent background
|
||||||
|
* @param w
|
||||||
|
* @param h
|
||||||
* @param edge
|
* @param edge
|
||||||
* @param color
|
* @param numSample
|
||||||
* @return the bitmap
|
* @param foreground
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
public Bitmap toBitmap(int width, int height, int edge, int color) {
|
public Bitmap toBitmap(int w, int h,
|
||||||
RectF bbx = getBoundingBox();
|
int edge, int numSample) {
|
||||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
RectF bbx = this.getBBX();
|
||||||
Canvas canvas = new Canvas(bitmap);
|
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||||
canvas.translate(edge, edge);
|
Path path = this.toPath(w - 2 * edge, h - 2 * edge, numSample);
|
||||||
|
Canvas c = new Canvas(bitmap);
|
||||||
|
//c.drawColor(background);
|
||||||
|
c.translate(edge, edge);
|
||||||
Paint paint = new Paint();
|
Paint paint = new Paint();
|
||||||
paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS);
|
paint.setAntiAlias(true);
|
||||||
paint.setDither(BITMAP_RENDERING_DITHER);
|
paint.setDither(true);
|
||||||
paint.setColor(color);
|
paint.setColor(mColor);
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
paint.setStrokeJoin(Paint.Join.ROUND);
|
paint.setStrokeJoin(Paint.Join.ROUND);
|
||||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
paint.setStrokeWidth(BITMAP_RENDERING_WIDTH);
|
paint.setStrokeWidth(2);
|
||||||
ArrayList<GestureStroke> strokes = mStrokes;
|
c.drawPath(path, paint);
|
||||||
int count = strokes.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureStroke stroke = strokes.get(i);
|
|
||||||
stroke.draw(canvas, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the gesture as XML
|
* save the gesture as XML
|
||||||
*
|
|
||||||
* @param namespace
|
* @param namespace
|
||||||
* @param serializer
|
* @param serializer
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
void toXML(String namespace, XmlSerializer serializer) throws IOException {
|
public void toXML(String namespace, XmlSerializer serializer) throws IOException {
|
||||||
serializer.startTag(namespace, GestureConstants.XML_TAG_GESTURE);
|
serializer.startTag(namespace, "stroke");
|
||||||
serializer.attribute(namespace, GestureConstants.XML_TAG_ID, Long.toString(mGestureID));
|
serializer.attribute(namespace, "timestamp", Long.toString(mTimestamp));
|
||||||
ArrayList<GestureStroke> strokes = mStrokes;
|
serializer.attribute(namespace, "id", Long.toString(mID));
|
||||||
int count = strokes.size();
|
serializer.attribute(namespace, "color", Integer.toString(mColor));
|
||||||
for (int i = 0; i < count; i++) {
|
serializer.attribute(namespace, "width", Float.toString(mWidth));
|
||||||
GestureStroke stroke = strokes.get(i);
|
Iterator it = this.mPtsBuffer.iterator();
|
||||||
stroke.toXML(namespace, serializer);
|
String pts = "";
|
||||||
|
while (it.hasNext()) {
|
||||||
|
PointF fp = (PointF)it.next();
|
||||||
|
if (pts.length() > 0)
|
||||||
|
pts += ",";
|
||||||
|
pts += fp.x + "," + fp.y;
|
||||||
}
|
}
|
||||||
serializer.endTag(namespace, GestureConstants.XML_TAG_GESTURE);
|
serializer.text(pts);
|
||||||
|
serializer.endTag(namespace, "stroke");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the gesture from a string
|
|
||||||
*
|
|
||||||
* @param str
|
|
||||||
*/
|
|
||||||
public void createFromString(String str) {
|
public void createFromString(String str) {
|
||||||
int startIndex = 0;
|
StringTokenizer st = new StringTokenizer(str, "#");
|
||||||
int endIndex;
|
|
||||||
while ((endIndex = str.indexOf(GestureConstants.STRING_GESTURE_DELIIMITER, startIndex + 1)) != -1) {
|
String para = st.nextToken();
|
||||||
String token = str.substring(startIndex, endIndex);
|
StringTokenizer innerst = new StringTokenizer(para, ",");
|
||||||
if (startIndex > 0) { // stroke tokens
|
this.mBBX = new RectF();
|
||||||
addStroke(GestureStroke.createFromString(token));
|
this.mBBX.left = Float.parseFloat(innerst.nextToken());
|
||||||
} else { // id token
|
this.mBBX.top = Float.parseFloat(innerst.nextToken());
|
||||||
mGestureID = Long.parseLong(token);
|
this.mBBX.right = Float.parseFloat(innerst.nextToken());
|
||||||
}
|
this.mBBX.bottom = Float.parseFloat(innerst.nextToken());
|
||||||
startIndex = endIndex + 1;
|
|
||||||
|
para = st.nextToken();
|
||||||
|
innerst = new StringTokenizer(para, ",");
|
||||||
|
while (innerst.hasMoreTokens()) {
|
||||||
|
String s = innerst.nextToken().trim();
|
||||||
|
if (s.length()==0)
|
||||||
|
break;
|
||||||
|
float x = Float.parseFloat(s);
|
||||||
|
float y = Float.parseFloat(innerst.nextToken());
|
||||||
|
this.mPtsBuffer.add(new PointF(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
para = st.nextToken();
|
||||||
|
this.mColor = Integer.parseInt(para);
|
||||||
|
|
||||||
|
para = st.nextToken();
|
||||||
|
this.mWidth = Float.parseFloat(para);
|
||||||
|
|
||||||
|
para = st.nextToken();
|
||||||
|
this.mLength = Float.parseFloat(para);
|
||||||
|
|
||||||
|
para = st.nextToken();
|
||||||
|
this.mTimestamp = Long.parseLong(para);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the gesture to string
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder str = new StringBuilder();
|
String str = "";
|
||||||
str.append(mGestureID);
|
|
||||||
ArrayList<GestureStroke> strokes = mStrokes;
|
str += "#" + this.mBBX.left + "," + this.mBBX.top + "," +
|
||||||
int count = strokes.size();
|
this.mBBX.right + "," + this.mBBX.bottom;
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureStroke stroke = strokes.get(i);
|
str += "#";
|
||||||
str.append(GestureConstants.STRING_GESTURE_DELIIMITER);
|
Iterator<PointF> it = this.mPtsBuffer.iterator();
|
||||||
str.append(stroke.toString());
|
while (it.hasNext()) {
|
||||||
|
PointF fp = it.next();
|
||||||
|
str += fp.x + "," + fp.y + ",";
|
||||||
}
|
}
|
||||||
|
|
||||||
return str.toString();
|
str += "#";
|
||||||
|
str += this.mColor;
|
||||||
|
|
||||||
|
str += "#";
|
||||||
|
str += this.mWidth;
|
||||||
|
|
||||||
|
str += "#";
|
||||||
|
str += this.mLength;
|
||||||
|
|
||||||
|
str += "#";
|
||||||
|
str += this.mTimestamp;
|
||||||
|
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<Gesture> CREATOR = new Parcelable.Creator<Gesture>() {
|
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
|
||||||
public Gesture createFromParcel(Parcel in) {
|
public Gesture createFromParcel(Parcel in) {
|
||||||
String str = in.readString();
|
String str = in.readString();
|
||||||
Gesture gesture = new Gesture();
|
Gesture stk = new Gesture();
|
||||||
gesture.createFromString(str);
|
stk.createFromString(str);
|
||||||
return gesture;
|
return stk;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Gesture[] newArray(int size) {
|
public Gesture[] newArray(int size) {
|
||||||
@@ -274,32 +339,20 @@ public class Gesture implements Parcelable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
public static Gesture buildFromArray(byte[] bytes) {
|
||||||
* Build a gesture from a byte array
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* @return the gesture
|
|
||||||
*/
|
|
||||||
static Gesture buildFromArray(byte[] bytes) {
|
|
||||||
String str = new String(bytes);
|
String str = new String(bytes);
|
||||||
Gesture gesture = new Gesture();
|
Gesture stk = new Gesture();
|
||||||
gesture.createFromString(str);
|
stk.createFromString(str);
|
||||||
return gesture;
|
return stk;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static byte[] saveToArray(Gesture stk) {
|
||||||
* Save a gesture to a byte array
|
String str = stk.toString();
|
||||||
*
|
|
||||||
* @param stroke
|
|
||||||
* @return the byte array
|
|
||||||
*/
|
|
||||||
static byte[] saveToArray(Gesture stroke) {
|
|
||||||
String str = stroke.toString();
|
|
||||||
return str.getBytes();
|
return str.getBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeToParcel(Parcel out, int flags) {
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
out.writeString(toString());
|
out.writeString(this.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
|
|||||||
@@ -1,22 +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 com.android.gesture;
|
|
||||||
|
|
||||||
|
|
||||||
public interface GestureActionListener {
|
|
||||||
public void onGesturePerformed(GestureOverlay overlay, Gesture gesture);
|
|
||||||
}
|
|
||||||
@@ -1,124 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class GestureAdapter implements GestureListener {
|
|
||||||
|
|
||||||
public static final int SINGLE_STROKE = 0;
|
|
||||||
|
|
||||||
public static final int MULTIPLE_STROKE = 1;
|
|
||||||
|
|
||||||
private static final float STROKE_LENGTH_THRESHOLD = 100;
|
|
||||||
|
|
||||||
private static final float SQUARENESS_THRESHOLD = 0.24f;
|
|
||||||
|
|
||||||
private static final int UNCERTAIN_GESTURE_COLOR = Color.argb(60, 255, 255, 0);
|
|
||||||
|
|
||||||
private boolean mIsGesturing = false;
|
|
||||||
|
|
||||||
private float mTotalLength;
|
|
||||||
|
|
||||||
private float mX, mY;
|
|
||||||
|
|
||||||
private View mModel;
|
|
||||||
|
|
||||||
private int mGestureType = SINGLE_STROKE;
|
|
||||||
|
|
||||||
private ArrayList<GestureActionListener> mActionListeners = new ArrayList<GestureActionListener>();
|
|
||||||
|
|
||||||
public GestureAdapter(View model) {
|
|
||||||
mModel = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGestureType(int type) {
|
|
||||||
mGestureType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStartGesture(GestureOverlay 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.setGestureColor(UNCERTAIN_GESTURE_COLOR);
|
|
||||||
}
|
|
||||||
mModel.dispatchTouchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onGesture(GestureOverlay overlay, MotionEvent event) {
|
|
||||||
if (mIsGesturing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float x = event.getX();
|
|
||||||
float y = event.getY();
|
|
||||||
float dx = x - mX;
|
|
||||||
float dy = y - mY;
|
|
||||||
mTotalLength += (float)Math.sqrt(dx * dx + dy * dy);
|
|
||||||
mX = x;
|
|
||||||
mY = y;
|
|
||||||
|
|
||||||
if (mTotalLength > STROKE_LENGTH_THRESHOLD) {
|
|
||||||
OrientedBoundingBox bbx = GestureUtils.computeOrientedBBX(overlay.getCurrentStroke());
|
|
||||||
if (bbx.squareness > SQUARENESS_THRESHOLD) {
|
|
||||||
mIsGesturing = true;
|
|
||||||
overlay.setGestureColor(GestureOverlay.DEFAULT_GESTURE_COLOR);
|
|
||||||
event = MotionEvent.obtain(event.getDownTime(), System.currentTimeMillis(),
|
|
||||||
MotionEvent.ACTION_UP, x, y, event.getPressure(), event.getSize(), event
|
|
||||||
.getMetaState(), event.getXPrecision(), event.getYPrecision(),
|
|
||||||
event.getDeviceId(), event.getEdgeFlags());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mModel.dispatchTouchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFinishGesture(GestureOverlay overlay, MotionEvent event) {
|
|
||||||
if (mIsGesturing) {
|
|
||||||
overlay.clear(true);
|
|
||||||
ArrayList<GestureActionListener> listeners = mActionListeners;
|
|
||||||
int count = listeners.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureActionListener listener = listeners.get(i);
|
|
||||||
listener.onGesturePerformed(overlay, overlay.getCurrentGesture());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mModel.dispatchTouchEvent(event);
|
|
||||||
overlay.clear(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addGestureActionListener(GestureActionListener listener) {
|
|
||||||
mActionListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeGestureActionListener(GestureActionListener listener) {
|
|
||||||
mActionListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGesturing() {
|
|
||||||
return mIsGesturing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 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 com.android.gesture;
|
|
||||||
|
|
||||||
interface GestureConstants {
|
|
||||||
static final String XML_TAG_LIBRARY = "library";
|
|
||||||
static final String XML_TAG_ENTRY = "entry";
|
|
||||||
static final String XML_TAG_GESTURE = "gesture";
|
|
||||||
static final String XML_TAG_STROKE = "stroke";
|
|
||||||
static final String XML_TAG_ID = "id";
|
|
||||||
static final String XML_TAG_NAME = "name";
|
|
||||||
static final String STRING_GESTURE_DELIIMITER = "#";
|
|
||||||
static final String STRING_STROKE_DELIIMITER = ",";
|
|
||||||
static final int STROKE_STRING_BUFFER_SIZE = 1024;
|
|
||||||
static final int STROKE_POINT_BUFFER_SIZE = 100; // number of points
|
|
||||||
static final int IO_BUFFER_SIZE = 8 * 1024; // 8K
|
|
||||||
}
|
|
||||||
@@ -1,332 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import android.util.Config;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Xml;
|
|
||||||
import android.util.Xml.Encoding;
|
|
||||||
|
|
||||||
import org.xml.sax.Attributes;
|
|
||||||
import org.xml.sax.ContentHandler;
|
|
||||||
import org.xml.sax.Locator;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GestureLibrary maintains gesture examples and makes predictions on a new
|
|
||||||
* gesture
|
|
||||||
*/
|
|
||||||
public class GestureLibrary {
|
|
||||||
|
|
||||||
public static final int SEQUENCE_INVARIANT = 1;
|
|
||||||
|
|
||||||
public static final int SEQUENCE_SENSITIVE = 2;
|
|
||||||
|
|
||||||
private int mSequenceType = SEQUENCE_SENSITIVE;
|
|
||||||
|
|
||||||
public static final int ORIENTATION_INVARIANT = 1;
|
|
||||||
|
|
||||||
public static final int ORIENTATION_SENSITIVE = 2;
|
|
||||||
|
|
||||||
private int mOrientationStyle = ORIENTATION_SENSITIVE;
|
|
||||||
|
|
||||||
private static final String LOGTAG = "GestureLibrary";
|
|
||||||
|
|
||||||
private static final String NAMESPACE = "";
|
|
||||||
|
|
||||||
private final String mGestureFileName;
|
|
||||||
|
|
||||||
private HashMap<String, ArrayList<Gesture>> mEntryName2gestures = new HashMap<String, ArrayList<Gesture>>();
|
|
||||||
|
|
||||||
private Learner mClassifier;
|
|
||||||
|
|
||||||
private boolean mChanged = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param path where gesture data is stored
|
|
||||||
*/
|
|
||||||
public GestureLibrary(String path) {
|
|
||||||
mGestureFileName = path;
|
|
||||||
mClassifier = new InstanceLearner();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify whether the gesture library will handle orientation sensitive
|
|
||||||
* gestures. Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE
|
|
||||||
*
|
|
||||||
* @param style
|
|
||||||
*/
|
|
||||||
public void setOrientationStyle(int style) {
|
|
||||||
mOrientationStyle = style;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOrientationStyle() {
|
|
||||||
return mOrientationStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGestureType(int type) {
|
|
||||||
mSequenceType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGestureType() {
|
|
||||||
return mSequenceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all the gesture entry names in the library
|
|
||||||
*
|
|
||||||
* @return a set of strings
|
|
||||||
*/
|
|
||||||
public Set<String> getGestureEntries() {
|
|
||||||
return mEntryName2gestures.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recognize a gesture
|
|
||||||
*
|
|
||||||
* @param gesture the query
|
|
||||||
* @return a list of predictions of possible entries for a given gesture
|
|
||||||
*/
|
|
||||||
public ArrayList<Prediction> recognize(Gesture gesture) {
|
|
||||||
Instance instance = Instance.createInstance(this, gesture, null);
|
|
||||||
return mClassifier.classify(this, instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a gesture for the entry
|
|
||||||
*
|
|
||||||
* @param entryName entry name
|
|
||||||
* @param gesture
|
|
||||||
*/
|
|
||||||
public void addGesture(String entryName, Gesture gesture) {
|
|
||||||
if (Config.DEBUG) {
|
|
||||||
Log.v(LOGTAG, "Add an example for gesture: " + entryName);
|
|
||||||
}
|
|
||||||
ArrayList<Gesture> gestures = mEntryName2gestures.get(entryName);
|
|
||||||
if (gestures == null) {
|
|
||||||
gestures = new ArrayList<Gesture>();
|
|
||||||
mEntryName2gestures.put(entryName, gestures);
|
|
||||||
}
|
|
||||||
gestures.add(gesture);
|
|
||||||
mClassifier.addInstance(Instance.createInstance(this, gesture, entryName));
|
|
||||||
mChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a gesture from the library. If there are no more gestures for the
|
|
||||||
* given entry, the gesture entry will be removed.
|
|
||||||
*
|
|
||||||
* @param entryName entry name
|
|
||||||
* @param gesture
|
|
||||||
*/
|
|
||||||
public void removeGesture(String entryName, Gesture gesture) {
|
|
||||||
ArrayList<Gesture> gestures = mEntryName2gestures.get(entryName);
|
|
||||||
if (gestures == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gestures.remove(gesture);
|
|
||||||
|
|
||||||
// if there are no more samples, remove the entry automatically
|
|
||||||
if (gestures.isEmpty()) {
|
|
||||||
mEntryName2gestures.remove(entryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
mClassifier.removeInstance(gesture.getID());
|
|
||||||
|
|
||||||
mChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a entry of gestures
|
|
||||||
*
|
|
||||||
* @param entryName the entry name
|
|
||||||
*/
|
|
||||||
public void removeEntireEntry(String entryName) {
|
|
||||||
mEntryName2gestures.remove(entryName);
|
|
||||||
mClassifier.removeInstances(entryName);
|
|
||||||
mChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all the gestures of an entry
|
|
||||||
*
|
|
||||||
* @param entryName
|
|
||||||
* @return the list of gestures that is under this name
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public ArrayList<Gesture> getGestures(String entryName) {
|
|
||||||
ArrayList<Gesture> gestures = mEntryName2gestures.get(entryName);
|
|
||||||
if (gestures != null) {
|
|
||||||
return (ArrayList<Gesture>)gestures.clone();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the gesture library
|
|
||||||
*/
|
|
||||||
public void save() {
|
|
||||||
if (!mChanged)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
File file = new File(mGestureFileName);
|
|
||||||
if (!file.getParentFile().exists()) {
|
|
||||||
file.getParentFile().mkdirs();
|
|
||||||
}
|
|
||||||
if (Config.DEBUG) {
|
|
||||||
Log.v(LOGTAG, "Save to " + mGestureFileName);
|
|
||||||
}
|
|
||||||
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(
|
|
||||||
mGestureFileName), GestureConstants.IO_BUFFER_SIZE);
|
|
||||||
|
|
||||||
PrintWriter writer = new PrintWriter(outputStream);
|
|
||||||
XmlSerializer serializer = Xml.newSerializer();
|
|
||||||
serializer.setOutput(writer);
|
|
||||||
serializer.startDocument(Encoding.ISO_8859_1.name(), null);
|
|
||||||
serializer.startTag(NAMESPACE, GestureConstants.XML_TAG_LIBRARY);
|
|
||||||
HashMap<String, ArrayList<Gesture>> maps = mEntryName2gestures;
|
|
||||||
Iterator<String> it = maps.keySet().iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
String key = it.next();
|
|
||||||
ArrayList<Gesture> examples = maps.get(key);
|
|
||||||
// save an entry
|
|
||||||
serializer.startTag(NAMESPACE, GestureConstants.XML_TAG_ENTRY);
|
|
||||||
serializer.attribute(NAMESPACE, GestureConstants.XML_TAG_NAME, key);
|
|
||||||
int count = examples.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
Gesture gesture = examples.get(i);
|
|
||||||
// save each gesture in the entry
|
|
||||||
gesture.toXML(NAMESPACE, serializer);
|
|
||||||
}
|
|
||||||
serializer.endTag(NAMESPACE, GestureConstants.XML_TAG_ENTRY);
|
|
||||||
}
|
|
||||||
serializer.endTag(NAMESPACE, GestureConstants.XML_TAG_LIBRARY);
|
|
||||||
serializer.endDocument();
|
|
||||||
serializer.flush();
|
|
||||||
writer.close();
|
|
||||||
outputStream.close();
|
|
||||||
mChanged = false;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Log.d(LOGTAG, "Failed to save gestures:", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the gesture library
|
|
||||||
*/
|
|
||||||
public void load() {
|
|
||||||
File file = new File(mGestureFileName);
|
|
||||||
if (file.exists()) {
|
|
||||||
try {
|
|
||||||
if (Config.DEBUG) {
|
|
||||||
Log.v(LOGTAG, "Load from " + mGestureFileName);
|
|
||||||
}
|
|
||||||
BufferedInputStream in = new BufferedInputStream(new FileInputStream(
|
|
||||||
mGestureFileName), GestureConstants.IO_BUFFER_SIZE);
|
|
||||||
Xml.parse(in, Encoding.ISO_8859_1, new CompactInkHandler());
|
|
||||||
in.close();
|
|
||||||
} catch (SAXException ex) {
|
|
||||||
Log.d(LOGTAG, "Failed to load gestures:", ex);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Log.d(LOGTAG, "Failed to load gestures:", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CompactInkHandler implements ContentHandler {
|
|
||||||
Gesture currentGesture = null;
|
|
||||||
|
|
||||||
StringBuilder buffer = new StringBuilder(GestureConstants.STROKE_STRING_BUFFER_SIZE);
|
|
||||||
|
|
||||||
String entryName;
|
|
||||||
|
|
||||||
ArrayList<Gesture> gestures;
|
|
||||||
|
|
||||||
CompactInkHandler() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void characters(char[] ch, int start, int length) {
|
|
||||||
buffer.append(ch, start, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void endDocument() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void endElement(String uri, String localName, String qName) {
|
|
||||||
if (localName.equals(GestureConstants.XML_TAG_ENTRY)) {
|
|
||||||
mEntryName2gestures.put(entryName, gestures);
|
|
||||||
gestures = null;
|
|
||||||
} else if (localName.equals(GestureConstants.XML_TAG_GESTURE)) {
|
|
||||||
gestures.add(currentGesture);
|
|
||||||
mClassifier.addInstance(Instance.createInstance(GestureLibrary.this,
|
|
||||||
currentGesture, entryName));
|
|
||||||
currentGesture = null;
|
|
||||||
} else if (localName.equals(GestureConstants.XML_TAG_STROKE)) {
|
|
||||||
currentGesture.addStroke(GestureStroke.createFromString(buffer.toString()));
|
|
||||||
buffer.setLength(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void endPrefixMapping(String prefix) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ignorableWhitespace(char[] ch, int start, int length) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processingInstruction(String target, String data) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDocumentLocator(Locator locator) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void skippedEntity(String name) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startDocument() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startElement(String uri, String localName, String qName, Attributes attributes) {
|
|
||||||
if (localName.equals(GestureConstants.XML_TAG_ENTRY)) {
|
|
||||||
gestures = new ArrayList<Gesture>();
|
|
||||||
entryName = attributes.getValue(NAMESPACE, GestureConstants.XML_TAG_NAME);
|
|
||||||
} else if (localName.equals(GestureConstants.XML_TAG_GESTURE)) {
|
|
||||||
currentGesture = new Gesture();
|
|
||||||
currentGesture.setID(Long.parseLong(attributes.getValue(NAMESPACE,
|
|
||||||
GestureConstants.XML_TAG_ID)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startPrefixMapping(String prefix, String uri) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,13 +18,8 @@ package com.android.gesture;
|
|||||||
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for processing gesture events
|
|
||||||
*/
|
|
||||||
public interface GestureListener {
|
public interface GestureListener {
|
||||||
public void onStartGesture(GestureOverlay overlay, MotionEvent event);
|
public void onStartGesture(GesturePad pad, MotionEvent event);
|
||||||
|
public void onGesture(GesturePad pad, MotionEvent event);
|
||||||
public void onGesture(GestureOverlay overlay, MotionEvent event);
|
public void onFinishGesture(GesturePad pad, MotionEvent event);
|
||||||
|
|
||||||
public void onFinishGesture(GestureOverlay overlay, MotionEvent event);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,340 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BlurMaskFilter;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Path;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A (transparent) overlay for gesture input that can be placed on top of other
|
|
||||||
* widgets. The view can also be opaque.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class GestureOverlay extends View {
|
|
||||||
|
|
||||||
static final float TOUCH_TOLERANCE = 3;
|
|
||||||
|
|
||||||
private static final int TRANSPARENT_BACKGROUND = Color.argb(0, 0, 0, 0);
|
|
||||||
|
|
||||||
private static final float FADING_ALPHA_CHANGE = 0.03f;
|
|
||||||
|
|
||||||
private static final long FADING_REFRESHING_RATE = 100;
|
|
||||||
|
|
||||||
private static final int GESTURE_STROKE_WIDTH = 12;
|
|
||||||
|
|
||||||
private static final boolean GESTURE_RENDERING_ANTIALIAS = true;
|
|
||||||
|
|
||||||
private static final int BLUR_MASK_RADIUS = 1;
|
|
||||||
|
|
||||||
public static final int DEFAULT_GESTURE_COLOR = Color.argb(255, 255, 255, 0);
|
|
||||||
|
|
||||||
// double buffering
|
|
||||||
private Paint mGesturePaint;
|
|
||||||
|
|
||||||
private Bitmap mBitmap; // with transparent background
|
|
||||||
|
|
||||||
private Canvas mBitmapCanvas;
|
|
||||||
|
|
||||||
// for rendering immediate ink feedback
|
|
||||||
private Path mPath;
|
|
||||||
|
|
||||||
private float mX;
|
|
||||||
|
|
||||||
private float mY;
|
|
||||||
|
|
||||||
// current gesture
|
|
||||||
private Gesture mCurrentGesture = null;
|
|
||||||
|
|
||||||
// gesture event handlers
|
|
||||||
ArrayList<GestureListener> mGestureListeners = new ArrayList<GestureListener>();
|
|
||||||
|
|
||||||
private ArrayList<GesturePoint> mPointBuffer = null;
|
|
||||||
|
|
||||||
// fading out effect
|
|
||||||
private boolean mIsFadingOut = false;
|
|
||||||
|
|
||||||
private float mFadingAlpha = 1;
|
|
||||||
|
|
||||||
private Handler mHandler = new Handler();
|
|
||||||
|
|
||||||
private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);
|
|
||||||
|
|
||||||
private Runnable mFadingOut = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if (mIsFadingOut) {
|
|
||||||
mFadingAlpha -= FADING_ALPHA_CHANGE;
|
|
||||||
if (mFadingAlpha <= 0) {
|
|
||||||
mIsFadingOut = false;
|
|
||||||
mPath = null;
|
|
||||||
mCurrentGesture = null;
|
|
||||||
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
|
|
||||||
} else {
|
|
||||||
mHandler.postDelayed(this, FADING_REFRESHING_RATE);
|
|
||||||
}
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public GestureOverlay(Context context) {
|
|
||||||
super(context);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public GestureOverlay(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<GesturePoint> getCurrentStroke() {
|
|
||||||
return mPointBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Gesture getCurrentGesture() {
|
|
||||||
return mCurrentGesture;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Gesture color
|
|
||||||
*
|
|
||||||
* @param color
|
|
||||||
*/
|
|
||||||
public void setGestureColor(int color) {
|
|
||||||
mGesturePaint.setColor(color);
|
|
||||||
if (mCurrentGesture != null) {
|
|
||||||
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
|
|
||||||
mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the gesture to be shown in the view
|
|
||||||
*
|
|
||||||
* @param gesture
|
|
||||||
*/
|
|
||||||
public void setCurrentGesture(Gesture gesture) {
|
|
||||||
if (mCurrentGesture != null) {
|
|
||||||
clear(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
mCurrentGesture = gesture;
|
|
||||||
|
|
||||||
if (gesture != null) {
|
|
||||||
if (mBitmapCanvas != null) {
|
|
||||||
gesture.draw(mBitmapCanvas, mGesturePaint);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
mGesturePaint = new Paint();
|
|
||||||
mGesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
|
|
||||||
mGesturePaint.setColor(DEFAULT_GESTURE_COLOR);
|
|
||||||
mGesturePaint.setStyle(Paint.Style.STROKE);
|
|
||||||
mGesturePaint.setStrokeJoin(Paint.Join.ROUND);
|
|
||||||
mGesturePaint.setStrokeCap(Paint.Cap.ROUND);
|
|
||||||
mGesturePaint.setStrokeWidth(GESTURE_STROKE_WIDTH);
|
|
||||||
mGesturePaint
|
|
||||||
.setMaskFilter(new BlurMaskFilter(BLUR_MASK_RADIUS, BlurMaskFilter.Blur.NORMAL));
|
|
||||||
|
|
||||||
mPath = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
mBitmap = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
|
|
||||||
mBitmapCanvas = new Canvas(mBitmap);
|
|
||||||
mBitmapCanvas.drawColor(TRANSPARENT_BACKGROUND);
|
|
||||||
if (mCurrentGesture != null) {
|
|
||||||
mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addGestureListener(GestureListener listener) {
|
|
||||||
mGestureListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeGestureListener(GestureListener listener) {
|
|
||||||
mGestureListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
super.onDraw(canvas);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
if (mPath != null) {
|
|
||||||
canvas.drawPath(mPath, mGesturePaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
mFadingAlpha = 1;
|
|
||||||
mIsFadingOut = true;
|
|
||||||
mHandler.removeCallbacks(mFadingOut);
|
|
||||||
mHandler.postDelayed(mFadingOut, FADING_REFRESHING_RATE);
|
|
||||||
} else {
|
|
||||||
mPath = null;
|
|
||||||
mCurrentGesture = null;
|
|
||||||
if (mBitmap != null) {
|
|
||||||
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancelFadingOut() {
|
|
||||||
mIsFadingOut = false;
|
|
||||||
mHandler.removeCallbacks(mFadingOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
|
|
||||||
if (!isEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (event.getAction()) {
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
touch_start(event);
|
|
||||||
invalidate();
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
|
||||||
touch_move(event);
|
|
||||||
invalidate();
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
touch_up(event);
|
|
||||||
invalidate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void touch_start(MotionEvent event) {
|
|
||||||
// pass the event to handlers
|
|
||||||
ArrayList<GestureListener> listeners = mGestureListeners;
|
|
||||||
int count = listeners.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureListener listener = listeners.get(i);
|
|
||||||
listener.onStartGesture(this, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is fading out going on, stop it.
|
|
||||||
if (mIsFadingOut) {
|
|
||||||
mIsFadingOut = false;
|
|
||||||
mHandler.removeCallbacks(mFadingOut);
|
|
||||||
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
|
|
||||||
mCurrentGesture = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
float x = event.getX();
|
|
||||||
float y = event.getY();
|
|
||||||
|
|
||||||
mX = x;
|
|
||||||
mY = y;
|
|
||||||
|
|
||||||
if (mCurrentGesture == null) {
|
|
||||||
mCurrentGesture = new Gesture();
|
|
||||||
}
|
|
||||||
|
|
||||||
mPointBuffer = new ArrayList<GesturePoint>();
|
|
||||||
mPointBuffer.add(new GesturePoint(x, y, event.getEventTime()));
|
|
||||||
|
|
||||||
mPath = new Path();
|
|
||||||
mPath.moveTo(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void touch_move(MotionEvent event) {
|
|
||||||
float x = event.getX();
|
|
||||||
float y = event.getY();
|
|
||||||
|
|
||||||
float dx = Math.abs(x - mX);
|
|
||||||
float dy = Math.abs(y - mY);
|
|
||||||
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
|
|
||||||
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
|
|
||||||
mX = x;
|
|
||||||
mY = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPointBuffer.add(new GesturePoint(x, y, event.getEventTime()));
|
|
||||||
|
|
||||||
// pass the event to handlers
|
|
||||||
ArrayList<GestureListener> listeners = mGestureListeners;
|
|
||||||
int count = listeners.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureListener listener = listeners.get(i);
|
|
||||||
listener.onGesture(this, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void touch_up(MotionEvent event) {
|
|
||||||
// add the stroke to the current gesture
|
|
||||||
mCurrentGesture.addStroke(new GestureStroke(mPointBuffer));
|
|
||||||
mPointBuffer = null;
|
|
||||||
|
|
||||||
// add the stroke to the double buffer
|
|
||||||
mGesturePaint.setDither(true);
|
|
||||||
mBitmapCanvas.drawPath(mPath, mGesturePaint);
|
|
||||||
mGesturePaint.setDither(false);
|
|
||||||
mPath = null;
|
|
||||||
|
|
||||||
// pass the event to handlers
|
|
||||||
ArrayList<GestureListener> listeners = mGestureListeners;
|
|
||||||
int count = listeners.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureListener listener = listeners.get(i);
|
|
||||||
listener.onFinishGesture(this, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2009 Google Inc.
|
* Copyright (C) 2008 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -18,67 +18,64 @@ package com.android.gesture;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BlurMaskFilter;
|
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.PointF;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A (transparent) view for gesture input that can be placed on top of other
|
* A view for rendering and processing gestures
|
||||||
* widgets. The background of the view is customizable.
|
|
||||||
*
|
|
||||||
* @author liyang@google.com (Yang Li)
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class GesturePad extends View {
|
public class GesturePad extends View {
|
||||||
|
|
||||||
private static final float TOUCH_TOLERANCE = 4;
|
public static final float TOUCH_TOLERANCE = 4;
|
||||||
public static final int DEFAULT_GESTURE_COLOR = Color.argb(255, 255, 255, 0);
|
public static final int default_foreground = Color.argb(255, 255, 255, 0);
|
||||||
|
private int background = Color.argb(0, 0, 0, 0);
|
||||||
|
private int foreground = default_foreground;
|
||||||
|
private int uncertain_foreground = Color.argb(55, 255, 255, 0);
|
||||||
|
private Bitmap mBitmap;
|
||||||
|
private Canvas mCanvas;
|
||||||
|
private Path mPath;
|
||||||
|
private Paint mBitmapPaint;
|
||||||
|
private Paint mPaint;
|
||||||
|
private Paint mDebugPaint;
|
||||||
|
private float mX, mY;
|
||||||
|
private boolean mEnableInput = true;
|
||||||
|
private boolean mEnableRendering = true;
|
||||||
|
private boolean mCacheGesture = true;
|
||||||
|
private Gesture mCurrentGesture = null;
|
||||||
|
ArrayList<GestureListener> mGestureListeners = new ArrayList<GestureListener>();
|
||||||
|
|
||||||
// double buffering
|
private boolean mShouldFadingOut = true;
|
||||||
private Paint mGesturePaint;
|
private boolean mIsFadingOut = false;
|
||||||
private Bitmap mBitmap; // with transparent background
|
private float mFadingAlpha = 1;
|
||||||
private Canvas mBitmapCanvas;
|
|
||||||
|
|
||||||
// for rendering immediate ink feedback
|
private boolean reconstruct = false;
|
||||||
private Path mPath;
|
|
||||||
private float mX;
|
|
||||||
private float mY;
|
|
||||||
|
|
||||||
// current gesture
|
private ArrayList<Path> debug = new ArrayList<Path>();
|
||||||
private Gesture mCurrentGesture = null;
|
|
||||||
|
|
||||||
// gesture event handlers
|
|
||||||
ArrayList<GestureListener> mGestureListeners =
|
|
||||||
new ArrayList<GestureListener>();
|
|
||||||
private ArrayList<GesturePoint> mPointBuffer = null;
|
|
||||||
|
|
||||||
// fading out effect
|
|
||||||
private boolean mIsFadingOut = false;
|
|
||||||
private float mFadingAlpha = 1;
|
|
||||||
private Handler mHandler = new Handler();
|
private Handler mHandler = new Handler();
|
||||||
|
|
||||||
private Runnable mFadingOut = new Runnable() {
|
private Runnable mFadingOut = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
mFadingAlpha -= 0.03f;
|
mFadingAlpha -= 0.03f;
|
||||||
if (mFadingAlpha <= 0) {
|
if (mFadingAlpha <= 0) {
|
||||||
mIsFadingOut = false;
|
mIsFadingOut = false;
|
||||||
mPath = null;
|
mPath.reset();
|
||||||
mCurrentGesture = null;
|
} else {
|
||||||
mBitmap.eraseColor(Color.argb(0, 0, 0, 0));
|
mHandler.postDelayed(this, 100);
|
||||||
} else {
|
}
|
||||||
mHandler.postDelayed(this, 100);
|
invalidate();
|
||||||
}
|
}
|
||||||
invalidate();
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public GesturePad(Context context) {
|
public GesturePad(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -90,76 +87,82 @@ public class GesturePad extends View {
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<GesturePoint> getCurrentStroke() {
|
public boolean isEnableRendering() {
|
||||||
return this.mPointBuffer;
|
return this.mEnableRendering;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Gesture getCurrentGesture() {
|
public Gesture getCurrentGesture() {
|
||||||
return mCurrentGesture;
|
return mCurrentGesture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Paint getPaint() {
|
||||||
* Set Gesture color
|
return mPaint;
|
||||||
* @param c
|
|
||||||
*/
|
|
||||||
public void setGestureColor(int c) {
|
|
||||||
this.mGesturePaint.setColor(c);
|
|
||||||
if (mCurrentGesture != null) {
|
|
||||||
mBitmap.eraseColor(Color.argb(0, 0, 0, 0));
|
|
||||||
mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setColor(int c) {
|
||||||
* Set the gesture to be shown in the view
|
this.foreground = c;
|
||||||
* @param gesture
|
}
|
||||||
*/
|
|
||||||
public void setCurrentGesture(Gesture gesture) {
|
|
||||||
if (this.mCurrentGesture != null) {
|
|
||||||
clear(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mCurrentGesture = gesture;
|
public void setFadingAlpha(float f) {
|
||||||
|
mFadingAlpha = f;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.mCurrentGesture != null) {
|
public void setCurrentGesture(Gesture stk) {
|
||||||
if (mBitmapCanvas != null) {
|
this.mCurrentGesture = stk;
|
||||||
this.mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
|
reconstruct = true;
|
||||||
this.invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
mGesturePaint = new Paint();
|
mDebugPaint = new Paint();
|
||||||
mGesturePaint.setAntiAlias(true);
|
mDebugPaint.setColor(Color.WHITE);
|
||||||
mGesturePaint.setDither(true);
|
mDebugPaint.setStrokeWidth(4);
|
||||||
mGesturePaint.setColor(DEFAULT_GESTURE_COLOR);
|
mDebugPaint.setAntiAlias(true);
|
||||||
mGesturePaint.setStyle(Paint.Style.STROKE);
|
mDebugPaint.setStyle(Paint.Style.STROKE);
|
||||||
mGesturePaint.setStrokeJoin(Paint.Join.ROUND);
|
|
||||||
mGesturePaint.setStrokeCap(Paint.Cap.ROUND);
|
|
||||||
mGesturePaint.setStrokeWidth(12);
|
|
||||||
mGesturePaint.setMaskFilter(
|
|
||||||
new BlurMaskFilter(1, BlurMaskFilter.Blur.NORMAL));
|
|
||||||
|
|
||||||
mPath = null;
|
mPaint = new Paint();
|
||||||
|
mPaint.setAntiAlias(true);
|
||||||
|
mPaint.setDither(true);
|
||||||
|
mPaint.setColor(foreground);
|
||||||
|
mPaint.setStyle(Paint.Style.STROKE);
|
||||||
|
mPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||||
|
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
mPaint.setStrokeWidth(12);
|
||||||
|
|
||||||
|
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
|
||||||
|
mPath = new Path();
|
||||||
|
|
||||||
|
reconstruct = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void cacheGesture(boolean b) {
|
||||||
|
mCacheGesture = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableRendering(boolean b) {
|
||||||
|
mEnableRendering = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
super.onSizeChanged(w, h, oldw, oldh);
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
if (w <= 0 || h <= 0) {
|
|
||||||
|
if (w <=0 || h <=0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
int width = w>oldw? w : oldw;
|
||||||
|
int height = h>oldh? h : oldh;
|
||||||
|
Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
mCanvas = new Canvas(newBitmap);
|
||||||
|
|
||||||
|
if (mBitmap != null) {
|
||||||
|
mCanvas.drawColor(background);
|
||||||
|
mCanvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
|
||||||
|
mCanvas.drawPath(mPath, mPaint);
|
||||||
}
|
}
|
||||||
int width = w > oldw? w : oldw;
|
|
||||||
int height = h > oldh? h : oldh;
|
mBitmap = newBitmap;
|
||||||
mBitmap = Bitmap.createBitmap(
|
|
||||||
width, height, Bitmap.Config.ARGB_8888);
|
|
||||||
mBitmapCanvas = new Canvas(mBitmap);
|
|
||||||
mBitmapCanvas.drawColor(Color.argb(0, 0, 0, 0));
|
|
||||||
if (mCurrentGesture != null) {
|
|
||||||
mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addGestureListener(GestureListener l) {
|
public void addGestureListener(GestureListener l) {
|
||||||
@@ -172,50 +175,111 @@ public class GesturePad extends View {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(Canvas canvas) {
|
protected void onDraw(Canvas canvas) {
|
||||||
super.onDraw(canvas);
|
canvas.drawColor(background);
|
||||||
|
|
||||||
|
if (mCacheGesture)
|
||||||
|
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
|
||||||
|
|
||||||
// draw double buffer
|
|
||||||
Paint paint = new Paint(Paint.DITHER_FLAG);
|
|
||||||
if (mIsFadingOut) {
|
if (mIsFadingOut) {
|
||||||
paint.setAlpha((int)(255 * mFadingAlpha));
|
int color = foreground;
|
||||||
canvas.drawBitmap(mBitmap, 0, 0, paint);
|
int alpha = (int)(Color.alpha(color) * mFadingAlpha);
|
||||||
|
mPaint.setColor(Color.argb(alpha,
|
||||||
|
Color.red(color),
|
||||||
|
Color.green(color),
|
||||||
|
Color.blue(color)));
|
||||||
|
} else if (mEnableRendering == false) {
|
||||||
|
mPaint.setColor(uncertain_foreground);
|
||||||
} else {
|
} else {
|
||||||
canvas.drawBitmap(mBitmap, 0, 0, paint);
|
mPaint.setColor(foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw the current stroke
|
if (reconstruct) {
|
||||||
if (mPath != null) {
|
|
||||||
canvas.drawPath(mPath, mGesturePaint);
|
if (this.mCurrentGesture != null) {
|
||||||
|
float xedge = 30;
|
||||||
|
float yedge = 30;
|
||||||
|
float w = this.getWidth() - 2 * xedge;
|
||||||
|
float h = this.getHeight() - 2 * yedge;
|
||||||
|
float sx = w / this.mCurrentGesture.getBBX().width();
|
||||||
|
float sy = h / mCurrentGesture.getBBX().height();
|
||||||
|
float scale = sx>sy?sy:sx;
|
||||||
|
convertFromStroke(mCurrentGesture);
|
||||||
|
Matrix matrix = new Matrix();
|
||||||
|
matrix.preTranslate(-mCurrentGesture.getBBX().centerX(), -mCurrentGesture.getBBX().centerY());
|
||||||
|
matrix.postScale(scale, scale);
|
||||||
|
matrix.postTranslate(this.getWidth()/2, this.getHeight()/2);
|
||||||
|
this.mPath.transform(matrix);
|
||||||
|
} else {
|
||||||
|
mPath.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
reconstruct = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawPath(mPath, mPaint);
|
||||||
|
|
||||||
|
Iterator<Path> it = debug.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Path path = it.next();
|
||||||
|
canvas.drawPath(path, mDebugPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void clearDebugPath() {
|
||||||
* Clear up the gesture pad
|
debug.clear();
|
||||||
* @param fadeOut whether the gesture on the pad should fade out gradually
|
}
|
||||||
* or disappear immediately
|
|
||||||
*/
|
public void addDebugPath(Path path) {
|
||||||
public void clear(boolean fadeOut) {
|
debug.add(path);
|
||||||
if (fadeOut) {
|
}
|
||||||
mFadingAlpha = 1;
|
|
||||||
mIsFadingOut = true;
|
public void addDebugPath(ArrayList<Path> paths) {
|
||||||
mHandler.removeCallbacks(mFadingOut);
|
debug.addAll(paths);
|
||||||
mHandler.postDelayed(mFadingOut, 100);
|
}
|
||||||
} else {
|
|
||||||
mPath = null;
|
public void clear() {
|
||||||
this.mCurrentGesture = null;
|
mPath = new Path();
|
||||||
if (mBitmap != null) {
|
this.mCurrentGesture = null;
|
||||||
mBitmap.eraseColor(Color.argb(0, 0, 0, 0));
|
mCanvas.drawColor(background);
|
||||||
this.invalidate();
|
this.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertFromStroke(Gesture stk) {
|
||||||
|
mPath = null;
|
||||||
|
Iterator it = stk.getPoints().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
PointF p = (PointF) it.next();
|
||||||
|
if (mPath == null) {
|
||||||
|
mPath = new Path();
|
||||||
|
mPath.moveTo(p.x, p.y);
|
||||||
|
mX = p.x;
|
||||||
|
mY = p.y;
|
||||||
|
} else {
|
||||||
|
float dx = Math.abs(p.x - mX);
|
||||||
|
float dy = Math.abs(p.y - mY);
|
||||||
|
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
|
||||||
|
mPath.quadTo(mX, mY, (p.x + mX)/2, (p.y + mY)/2);
|
||||||
|
mX = p.x;
|
||||||
|
mY = p.y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mPath.lineTo(mX, mY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableInput(boolean b) {
|
||||||
|
mEnableInput = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnableInput() {
|
||||||
|
return mEnableInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
|
||||||
if(this.isEnabled() == false) {
|
if(mEnableInput == false)
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
switch (event.getAction()) {
|
switch (event.getAction()) {
|
||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
@@ -231,41 +295,28 @@ public class GesturePad extends View {
|
|||||||
invalidate();
|
invalidate();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void touch_start(MotionEvent event) {
|
private void touch_start(MotionEvent event) {
|
||||||
// if there is fading-out effect, stop it.
|
mIsFadingOut = false;
|
||||||
if (mIsFadingOut) {
|
mHandler.removeCallbacks(mFadingOut);
|
||||||
mIsFadingOut = false;
|
|
||||||
mHandler.removeCallbacks(mFadingOut);
|
|
||||||
mBitmap.eraseColor(Color.argb(0, 0, 0, 0));
|
|
||||||
this.mCurrentGesture = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
float x = event.getX();
|
float x = event.getX();
|
||||||
float y = event.getY();
|
float y = event.getY();
|
||||||
|
|
||||||
|
mCurrentGesture = new Gesture();
|
||||||
|
mCurrentGesture.addPoint(x, y);
|
||||||
|
|
||||||
|
mPath.reset();
|
||||||
|
mPath.moveTo(x, y);
|
||||||
mX = x;
|
mX = x;
|
||||||
mY = y;
|
mY = y;
|
||||||
|
|
||||||
// pass the event to handlers
|
Iterator<GestureListener> it = mGestureListeners.iterator();
|
||||||
int count = mGestureListeners.size();
|
while (it.hasNext()) {
|
||||||
for (int i = 0; i < count; i++) {
|
it.next().onStartGesture(this, event);
|
||||||
GestureListener listener = mGestureListeners.get(i);
|
|
||||||
listener.onStartGesture(this, event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCurrentGesture == null) {
|
|
||||||
mCurrentGesture = new Gesture();
|
|
||||||
}
|
|
||||||
|
|
||||||
mPointBuffer = new ArrayList<GesturePoint>();
|
|
||||||
mPointBuffer.add(new GesturePoint(x, y, event.getEventTime()));
|
|
||||||
|
|
||||||
mPath = new Path();
|
|
||||||
mPath.moveTo(x, y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void touch_move(MotionEvent event) {
|
private void touch_move(MotionEvent event) {
|
||||||
@@ -280,31 +331,40 @@ public class GesturePad extends View {
|
|||||||
mY = y;
|
mY = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
mPointBuffer.add(new GesturePoint(x, y, event.getEventTime()));
|
mCurrentGesture.addPoint(x, y);
|
||||||
|
|
||||||
// pass the event to handlers
|
Iterator<GestureListener> it = mGestureListeners.iterator();
|
||||||
int count = mGestureListeners.size();
|
while (it.hasNext()) {
|
||||||
for (int i = 0; i < count; i++) {
|
it.next().onGesture(this, event);
|
||||||
GestureListener listener = mGestureListeners.get(i);
|
|
||||||
listener.onGesture(this, event);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFadingOut(boolean b) {
|
||||||
|
mShouldFadingOut = b;
|
||||||
|
mIsFadingOut = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldFadingOut() {
|
||||||
|
return mShouldFadingOut;
|
||||||
|
}
|
||||||
|
|
||||||
private void touch_up(MotionEvent event) {
|
private void touch_up(MotionEvent event) {
|
||||||
// add the stroke to the current gesture
|
mPath.lineTo(mX, mY);
|
||||||
mCurrentGesture.addStroke(new GestureStroke(mPointBuffer));
|
|
||||||
mPointBuffer = null;
|
|
||||||
|
|
||||||
// add the stroke to the double buffer
|
if (mCacheGesture)
|
||||||
mBitmapCanvas.drawPath(mPath, mGesturePaint);
|
mCanvas.drawPath(mPath, mPaint);
|
||||||
mPath = null;
|
|
||||||
|
|
||||||
// pass the event to handlers
|
// kill this so we don't double draw
|
||||||
int count = mGestureListeners.size();
|
if (shouldFadingOut()) {
|
||||||
for (int i = 0; i < count; i++) {
|
mFadingAlpha = 1;
|
||||||
GestureListener listener = mGestureListeners.get(i);
|
mIsFadingOut = true;
|
||||||
listener.onFinishGesture(this, event);
|
mHandler.removeCallbacks(mFadingOut);
|
||||||
|
mHandler.postDelayed(mFadingOut, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<GestureListener> it = mGestureListeners.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next().onFinishGesture(this, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +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 com.android.gesture;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A timed point of a gesture stroke
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class GesturePoint {
|
|
||||||
public final float xpos;
|
|
||||||
|
|
||||||
public final float ypos;
|
|
||||||
|
|
||||||
public final long timestamp;
|
|
||||||
|
|
||||||
public GesturePoint(float x, float y, long t) {
|
|
||||||
xpos = x;
|
|
||||||
ypos = y;
|
|
||||||
timestamp = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class GestureProcessor implements GestureListener {
|
|
||||||
|
|
||||||
public static final int SINGLE_STROKE = 0;
|
|
||||||
|
|
||||||
public static final int MULTIPLE_STROKE = 1;
|
|
||||||
|
|
||||||
private static final float STROKE_LENGTH_THRESHOLD = 100;
|
|
||||||
|
|
||||||
private static final float SQUARENESS_THRESHOLD = 0.24f;
|
|
||||||
|
|
||||||
private static final int UNCERTAIN_GESTURE_COLOR = Color.argb(60, 255, 255, 0);
|
|
||||||
|
|
||||||
private boolean mIsGesturing = false;
|
|
||||||
|
|
||||||
private float mTotalLength;
|
|
||||||
|
|
||||||
private float mX, mY;
|
|
||||||
|
|
||||||
private View mModel;
|
|
||||||
|
|
||||||
private int mGestureType = SINGLE_STROKE;
|
|
||||||
|
|
||||||
private ArrayList<GestureActionListener> mActionListeners = new ArrayList<GestureActionListener>();
|
|
||||||
|
|
||||||
public GestureProcessor(View model) {
|
|
||||||
mModel = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param type SINGLE_STROKE or MULTIPLE_STROKE
|
|
||||||
*/
|
|
||||||
public void setGestureType(int type) {
|
|
||||||
mGestureType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStartGesture(GestureOverlay 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.setGestureColor(UNCERTAIN_GESTURE_COLOR);
|
|
||||||
}
|
|
||||||
mModel.dispatchTouchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onGesture(GestureOverlay overlay, MotionEvent event) {
|
|
||||||
if (mIsGesturing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float x = event.getX();
|
|
||||||
float y = event.getY();
|
|
||||||
float dx = x - mX;
|
|
||||||
float dy = y - mY;
|
|
||||||
mTotalLength += (float)Math.sqrt(dx * dx + dy * dy);
|
|
||||||
mX = x;
|
|
||||||
mY = y;
|
|
||||||
|
|
||||||
if (mTotalLength > STROKE_LENGTH_THRESHOLD) {
|
|
||||||
OrientedBoundingBox bbx = GestureUtils.computeOrientedBBX(overlay.getCurrentStroke());
|
|
||||||
if (bbx.squareness > SQUARENESS_THRESHOLD) {
|
|
||||||
mIsGesturing = true;
|
|
||||||
overlay.setGestureColor(GestureOverlay.DEFAULT_GESTURE_COLOR);
|
|
||||||
event = MotionEvent.obtain(event.getDownTime(), System.currentTimeMillis(),
|
|
||||||
MotionEvent.ACTION_UP, x, y, event.getPressure(), event.getSize(), event
|
|
||||||
.getMetaState(), event.getXPrecision(), event.getYPrecision(),
|
|
||||||
event.getDeviceId(), event.getEdgeFlags());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mModel.dispatchTouchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFinishGesture(GestureOverlay overlay, MotionEvent event) {
|
|
||||||
if (mIsGesturing) {
|
|
||||||
overlay.clear(true);
|
|
||||||
ArrayList<GestureActionListener> listeners = mActionListeners;
|
|
||||||
int count = listeners.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GestureActionListener listener = listeners.get(i);
|
|
||||||
listener.onGesturePerformed(overlay, overlay.getCurrentGesture());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mModel.dispatchTouchEvent(event);
|
|
||||||
overlay.clear(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addGestureActionListener(GestureActionListener listener) {
|
|
||||||
mActionListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeGestureActionListener(GestureActionListener listener) {
|
|
||||||
mActionListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGesturing() {
|
|
||||||
return mIsGesturing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,244 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Path;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A gesture stroke started on a touch down and ended on a touch up.
|
|
||||||
*/
|
|
||||||
public class GestureStroke {
|
|
||||||
public final RectF boundingBox;
|
|
||||||
|
|
||||||
public final float length;
|
|
||||||
|
|
||||||
public final float[] xPoints;
|
|
||||||
|
|
||||||
public final float[] yPoints;
|
|
||||||
|
|
||||||
public final long[] timestamps;
|
|
||||||
|
|
||||||
private Path mCachedPath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a gesture stroke from a list of gesture points
|
|
||||||
*
|
|
||||||
* @param pts
|
|
||||||
*/
|
|
||||||
public GestureStroke(ArrayList<GesturePoint> pts) {
|
|
||||||
xPoints = new float[pts.size()];
|
|
||||||
yPoints = new float[pts.size()];
|
|
||||||
timestamps = new long[pts.size()];
|
|
||||||
|
|
||||||
RectF bx = null;
|
|
||||||
float len = 0;
|
|
||||||
int index = 0;
|
|
||||||
int count = pts.size();
|
|
||||||
float[] xpts = xPoints;
|
|
||||||
float[] ypts = yPoints;
|
|
||||||
long[] times = timestamps;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
GesturePoint p = pts.get(i);
|
|
||||||
xpts[index] = p.xpos;
|
|
||||||
ypts[index] = p.ypos;
|
|
||||||
times[index] = p.timestamp;
|
|
||||||
|
|
||||||
if (bx == null) {
|
|
||||||
bx = new RectF();
|
|
||||||
bx.top = p.ypos;
|
|
||||||
bx.left = p.xpos;
|
|
||||||
bx.right = p.xpos;
|
|
||||||
bx.bottom = p.ypos;
|
|
||||||
len = 0;
|
|
||||||
} else {
|
|
||||||
len += Math.sqrt(Math.pow(p.xpos - xpts[index - 1], 2)
|
|
||||||
+ Math.pow(p.ypos - ypts[index - 1], 2));
|
|
||||||
bx.union(p.xpos, p.ypos);
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
boundingBox = bx;
|
|
||||||
length = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw the gesture with a given canvas and paint
|
|
||||||
*
|
|
||||||
* @param canvas
|
|
||||||
*/
|
|
||||||
void draw(Canvas canvas, Paint paint) {
|
|
||||||
if (mCachedPath == null) {
|
|
||||||
float[] xpts = xPoints;
|
|
||||||
float[] ypts = yPoints;
|
|
||||||
int count = xpts.length;
|
|
||||||
Path path = null;
|
|
||||||
float mX = 0, mY = 0;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
float x = xpts[i];
|
|
||||||
float y = ypts[i];
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the stroke to a Path based on the number of points
|
|
||||||
*
|
|
||||||
* @param width the width of the bounding box of the target path
|
|
||||||
* @param height the height of the bounding box of the target path
|
|
||||||
* @param numSample the number of points needed
|
|
||||||
* @return the path
|
|
||||||
*/
|
|
||||||
public Path toPath(float width, float height, int numSample) {
|
|
||||||
float[] pts = GestureUtils.sequentialFeaturize(this, numSample);
|
|
||||||
RectF rect = boundingBox;
|
|
||||||
float scale = height / rect.height();
|
|
||||||
Matrix matrix = new Matrix();
|
|
||||||
matrix.setTranslate(-rect.left, -rect.top);
|
|
||||||
Matrix scaleMatrix = new Matrix();
|
|
||||||
scaleMatrix.setScale(scale, scale);
|
|
||||||
matrix.postConcat(scaleMatrix);
|
|
||||||
Matrix translate = new Matrix();
|
|
||||||
matrix.postConcat(translate);
|
|
||||||
matrix.mapPoints(pts);
|
|
||||||
|
|
||||||
Path path = null;
|
|
||||||
float mX = 0;
|
|
||||||
float mY = 0;
|
|
||||||
int count = pts.length;
|
|
||||||
for (int i = 0; i < count; i += 2) {
|
|
||||||
float x = pts[i];
|
|
||||||
float y = pts[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 >= GestureOverlay.TOUCH_TOLERANCE || dy >= GestureOverlay.TOUCH_TOLERANCE) {
|
|
||||||
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
|
|
||||||
mX = x;
|
|
||||||
mY = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the gesture stroke as XML
|
|
||||||
*
|
|
||||||
* @param namespace
|
|
||||||
* @param serializer
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
void toXML(String namespace, XmlSerializer serializer) throws IOException {
|
|
||||||
serializer.startTag(namespace, GestureConstants.XML_TAG_STROKE);
|
|
||||||
serializer.text(toString());
|
|
||||||
serializer.endTag(namespace, GestureConstants.XML_TAG_STROKE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a gesture stroke from a string
|
|
||||||
*
|
|
||||||
* @param str
|
|
||||||
* @return the gesture stroke
|
|
||||||
*/
|
|
||||||
public static GestureStroke createFromString(String str) {
|
|
||||||
ArrayList<GesturePoint> points = new ArrayList<GesturePoint>(
|
|
||||||
GestureConstants.STROKE_POINT_BUFFER_SIZE);
|
|
||||||
int endIndex;
|
|
||||||
int startIndex = 0;
|
|
||||||
while ((endIndex = str.indexOf(GestureConstants.STRING_STROKE_DELIIMITER, startIndex + 1)) != -1) {
|
|
||||||
|
|
||||||
// parse x
|
|
||||||
String token = str.substring(startIndex, endIndex);
|
|
||||||
float x = Float.parseFloat(token);
|
|
||||||
startIndex = endIndex + 1;
|
|
||||||
|
|
||||||
// parse y
|
|
||||||
endIndex = str.indexOf(GestureConstants.STRING_STROKE_DELIIMITER, startIndex + 1);
|
|
||||||
token = str.substring(startIndex, endIndex);
|
|
||||||
float y = Float.parseFloat(token);
|
|
||||||
startIndex = endIndex + 1;
|
|
||||||
|
|
||||||
// parse t
|
|
||||||
endIndex = str.indexOf(GestureConstants.STRING_STROKE_DELIIMITER, startIndex + 1);
|
|
||||||
token = str.substring(startIndex, endIndex);
|
|
||||||
long time = Long.parseLong(token);
|
|
||||||
startIndex = endIndex + 1;
|
|
||||||
|
|
||||||
points.add(new GesturePoint(x, y, time));
|
|
||||||
}
|
|
||||||
return new GestureStroke(points);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the stroke to string
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder str = new StringBuilder(GestureConstants.STROKE_STRING_BUFFER_SIZE);
|
|
||||||
float[] xpts = xPoints;
|
|
||||||
float[] ypts = yPoints;
|
|
||||||
long[] times = timestamps;
|
|
||||||
int count = xpts.length;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
str.append(xpts[i] + GestureConstants.STRING_STROKE_DELIIMITER + ypts[i]
|
|
||||||
+ GestureConstants.STRING_STROKE_DELIIMITER + times[i]
|
|
||||||
+ GestureConstants.STRING_STROKE_DELIIMITER);
|
|
||||||
}
|
|
||||||
return str.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidate the cached path that is used for rendering the stroke
|
|
||||||
*/
|
|
||||||
public void invalidate() {
|
|
||||||
mCachedPath = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,358 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import android.graphics.RectF;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class GestureUtils {
|
|
||||||
|
|
||||||
private static final int SEQUENCE_SAMPLE_SIZE = 16;
|
|
||||||
|
|
||||||
protected static float[] spatialFeaturize(Gesture gesture, int sampleSize) {
|
|
||||||
float[] sample = new float[sampleSize * sampleSize];
|
|
||||||
Arrays.fill(sample, 0);
|
|
||||||
|
|
||||||
RectF rect = gesture.getBoundingBox();
|
|
||||||
float sx = sampleSize / rect.width();
|
|
||||||
float sy = sampleSize / rect.height();
|
|
||||||
float scale = sx < sy ? sx : sy;
|
|
||||||
android.graphics.Matrix trans = new android.graphics.Matrix();
|
|
||||||
trans.setScale(scale, scale);
|
|
||||||
android.graphics.Matrix translate1 = new android.graphics.Matrix();
|
|
||||||
translate1.setTranslate(-rect.centerX(), -rect.centerY());
|
|
||||||
trans.preConcat(translate1);
|
|
||||||
android.graphics.Matrix translate2 = new android.graphics.Matrix();
|
|
||||||
translate2.setTranslate(sampleSize / 2, sampleSize / 2);
|
|
||||||
trans.postConcat(translate2);
|
|
||||||
|
|
||||||
ArrayList<GestureStroke> strokes = gesture.getStrokes();
|
|
||||||
int count = strokes.size();
|
|
||||||
int size;
|
|
||||||
for (int index = 0; index < count; index++) {
|
|
||||||
GestureStroke stroke = strokes.get(index);
|
|
||||||
float[] pts = sequentialFeaturize(stroke, SEQUENCE_SAMPLE_SIZE);
|
|
||||||
trans.mapPoints(pts);
|
|
||||||
|
|
||||||
size = pts.length;
|
|
||||||
for (int i = 0; i < size; i += 2) {
|
|
||||||
float x = pts[i];
|
|
||||||
int xFloor = (int) Math.floor(x);
|
|
||||||
int xCeiling = (int) Math.ceil(x);
|
|
||||||
float y = pts[i + 1];
|
|
||||||
int yFloor = (int) Math.floor(y);
|
|
||||||
int yCeiling = (int) Math.ceil(y);
|
|
||||||
|
|
||||||
if (yFloor >= 0 && yFloor < sampleSize && xFloor >= 0 && xFloor < sampleSize) {
|
|
||||||
int pos = yFloor * sampleSize + xFloor;
|
|
||||||
float value = (1 - x + xFloor) * (1 - y + yFloor);
|
|
||||||
if (sample[pos] < value) {
|
|
||||||
sample[pos] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (yFloor >= 0 && yFloor < sampleSize && xCeiling >= 0 && xCeiling < sampleSize) {
|
|
||||||
int pos = yFloor * sampleSize + xCeiling;
|
|
||||||
float value = (1 - xCeiling + x) * (1 - y + yFloor);
|
|
||||||
if (sample[pos] < value) {
|
|
||||||
sample[pos] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (yCeiling >= 0 && yCeiling < sampleSize && xFloor >= 0 && xFloor < sampleSize) {
|
|
||||||
int pos = yCeiling * sampleSize + xFloor;
|
|
||||||
float value = (1 - x + xFloor) * (1 - yCeiling + y);
|
|
||||||
if (sample[pos] < value) {
|
|
||||||
sample[pos] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (yCeiling >= 0 && yCeiling < sampleSize && xCeiling >= 0
|
|
||||||
&& xCeiling < sampleSize) {
|
|
||||||
int pos = yCeiling * sampleSize + xCeiling;
|
|
||||||
float value = (1 - xCeiling + x) * (1 - yCeiling + y);
|
|
||||||
if (sample[pos] < value) {
|
|
||||||
sample[pos] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sample;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Featurize a stroke into a vector of a given number of elements
|
|
||||||
*
|
|
||||||
* @param stroke
|
|
||||||
* @param sampleSize
|
|
||||||
* @return a float array
|
|
||||||
*/
|
|
||||||
protected static float[] sequentialFeaturize(GestureStroke stroke, int sampleSize) {
|
|
||||||
final float increment = stroke.length / (sampleSize - 1);
|
|
||||||
int vectorLength = sampleSize * 2;
|
|
||||||
float[] vector = new float[vectorLength];
|
|
||||||
float distanceSoFar = 0;
|
|
||||||
float[] xpts = stroke.xPoints;
|
|
||||||
float[] ypts = stroke.yPoints;
|
|
||||||
float lstPointX = xpts[0];
|
|
||||||
float lstPointY = ypts[0];
|
|
||||||
int index = 0;
|
|
||||||
float currentPointX = Float.MIN_VALUE;
|
|
||||||
float currentPointY = Float.MIN_VALUE;
|
|
||||||
vector[index] = lstPointX;
|
|
||||||
index++;
|
|
||||||
vector[index] = lstPointY;
|
|
||||||
index++;
|
|
||||||
int i = 0;
|
|
||||||
int count = xpts.length;
|
|
||||||
while (i < count) {
|
|
||||||
if (currentPointX == Float.MIN_VALUE) {
|
|
||||||
i++;
|
|
||||||
if (i >= count) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
currentPointX = xpts[i];
|
|
||||||
currentPointY = ypts[i];
|
|
||||||
}
|
|
||||||
float deltaX = currentPointX - lstPointX;
|
|
||||||
float deltaY = currentPointY - lstPointY;
|
|
||||||
float distance = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
||||||
if (distanceSoFar + distance >= increment) {
|
|
||||||
float ratio = (increment - distanceSoFar) / distance;
|
|
||||||
float nx = lstPointX + ratio * deltaX;
|
|
||||||
float ny = lstPointY + ratio * deltaY;
|
|
||||||
vector[index] = nx;
|
|
||||||
index++;
|
|
||||||
vector[index] = ny;
|
|
||||||
index++;
|
|
||||||
lstPointX = nx;
|
|
||||||
lstPointY = ny;
|
|
||||||
distanceSoFar = 0;
|
|
||||||
} else {
|
|
||||||
lstPointX = currentPointX;
|
|
||||||
lstPointY = currentPointY;
|
|
||||||
currentPointX = Float.MIN_VALUE;
|
|
||||||
currentPointY = Float.MIN_VALUE;
|
|
||||||
distanceSoFar += distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = index; i < vectorLength; i += 2) {
|
|
||||||
vector[i] = lstPointX;
|
|
||||||
vector[i + 1] = lstPointY;
|
|
||||||
}
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the centroid
|
|
||||||
*
|
|
||||||
* @param points
|
|
||||||
* @return the centroid
|
|
||||||
*/
|
|
||||||
public static float[] computeCentroid(float[] points) {
|
|
||||||
float centerX = 0;
|
|
||||||
float centerY = 0;
|
|
||||||
int count = points.length;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
centerX += points[i];
|
|
||||||
i++;
|
|
||||||
centerY += points[i];
|
|
||||||
}
|
|
||||||
float[] center = new float[2];
|
|
||||||
center[0] = 2 * centerX / count;
|
|
||||||
center[1] = 2 * centerY / count;
|
|
||||||
|
|
||||||
return center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* calculate the variance-covariance matrix, treat each point as a sample
|
|
||||||
*
|
|
||||||
* @param points
|
|
||||||
* @return the covariance matrix
|
|
||||||
*/
|
|
||||||
protected static double[][] computeCoVariance(float[] points) {
|
|
||||||
double[][] array = new double[2][2];
|
|
||||||
array[0][0] = 0;
|
|
||||||
array[0][1] = 0;
|
|
||||||
array[1][0] = 0;
|
|
||||||
array[1][1] = 0;
|
|
||||||
int count = points.length;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
float x = points[i];
|
|
||||||
i++;
|
|
||||||
float y = points[i];
|
|
||||||
array[0][0] += x * x;
|
|
||||||
array[0][1] += x * y;
|
|
||||||
array[1][0] = array[0][1];
|
|
||||||
array[1][1] += y * y;
|
|
||||||
}
|
|
||||||
array[0][0] /= (count / 2);
|
|
||||||
array[0][1] /= (count / 2);
|
|
||||||
array[1][0] /= (count / 2);
|
|
||||||
array[1][1] /= (count / 2);
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float computeTotalLength(float[] points) {
|
|
||||||
float sum = 0;
|
|
||||||
int count = points.length - 4;
|
|
||||||
for (int i = 0; i < count; i += 2) {
|
|
||||||
float dx = points[i + 2] - points[i];
|
|
||||||
float dy = points[i + 3] - points[i + 1];
|
|
||||||
sum += Math.sqrt(dx * dx + dy * dy);
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double computeStraightness(float[] points) {
|
|
||||||
float totalLen = computeTotalLength(points);
|
|
||||||
float dx = points[2] - points[0];
|
|
||||||
float dy = points[3] - points[1];
|
|
||||||
return Math.sqrt(dx * dx + dy * dy) / totalLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double computeStraightness(float[] points, float totalLen) {
|
|
||||||
float dx = points[2] - points[0];
|
|
||||||
float dy = points[3] - points[1];
|
|
||||||
return Math.sqrt(dx * dx + dy * dy) / totalLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the squared Euclidean distance between two vectors
|
|
||||||
*
|
|
||||||
* @param vector1
|
|
||||||
* @param vector2
|
|
||||||
* @return the distance
|
|
||||||
*/
|
|
||||||
protected static double euclideanDistance(float[] vector1, float[] vector2) {
|
|
||||||
double squaredDistance = 0;
|
|
||||||
int size = vector1.length;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
float difference = vector1[i] - vector2[i];
|
|
||||||
squaredDistance += difference * difference;
|
|
||||||
}
|
|
||||||
return squaredDistance / size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the cosine distance between two instances
|
|
||||||
*
|
|
||||||
* @param in1
|
|
||||||
* @param in2
|
|
||||||
* @return the distance between 0 and Math.PI
|
|
||||||
*/
|
|
||||||
protected static double cosineDistance(Instance in1, Instance in2) {
|
|
||||||
float sum = 0;
|
|
||||||
float[] vector1 = in1.vector;
|
|
||||||
float[] vector2 = in2.vector;
|
|
||||||
int len = vector1.length;
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
sum += vector1[i] * vector2[i];
|
|
||||||
}
|
|
||||||
return Math.acos(sum / (in1.magnitude * in2.magnitude));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OrientedBoundingBox computeOrientedBBX(ArrayList<GesturePoint> pts) {
|
|
||||||
GestureStroke stroke = new GestureStroke(pts);
|
|
||||||
float[] points = sequentialFeaturize(stroke, SEQUENCE_SAMPLE_SIZE);
|
|
||||||
return computeOrientedBBX(points);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OrientedBoundingBox computeOrientedBBX(float[] points) {
|
|
||||||
float[] meanVector = computeCentroid(points);
|
|
||||||
return computeOrientedBBX(points, meanVector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OrientedBoundingBox computeOrientedBBX(float[] points, float[] centroid) {
|
|
||||||
|
|
||||||
android.graphics.Matrix tr = new android.graphics.Matrix();
|
|
||||||
tr.setTranslate(-centroid[0], -centroid[1]);
|
|
||||||
tr.mapPoints(points);
|
|
||||||
|
|
||||||
double[][] array = computeCoVariance(points);
|
|
||||||
double[] targetVector = computeOrientation(array);
|
|
||||||
|
|
||||||
float angle;
|
|
||||||
if (targetVector[0] == 0 && targetVector[1] == 0) {
|
|
||||||
angle = -90;
|
|
||||||
} 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();
|
|
||||||
trans.setRotate(-angle);
|
|
||||||
trans.mapPoints(points);
|
|
||||||
}
|
|
||||||
|
|
||||||
float minx = Float.MAX_VALUE;
|
|
||||||
float miny = Float.MAX_VALUE;
|
|
||||||
float maxx = Float.MIN_VALUE;
|
|
||||||
float maxy = Float.MIN_VALUE;
|
|
||||||
int count = points.length;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
if (points[i] < minx) {
|
|
||||||
minx = points[i];
|
|
||||||
}
|
|
||||||
if (points[i] > maxx) {
|
|
||||||
maxx = points[i];
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
if (points[i] < miny) {
|
|
||||||
miny = points[i];
|
|
||||||
}
|
|
||||||
if (points[i] > maxy) {
|
|
||||||
maxy = points[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OrientedBoundingBox bbx = new OrientedBoundingBox(angle, centroid[0], centroid[1], maxx
|
|
||||||
- minx, maxy - miny);
|
|
||||||
return bbx;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double[] computeOrientation(double[][] covarianceMatrix) {
|
|
||||||
double[] targetVector = new double[2];
|
|
||||||
if (covarianceMatrix[0][1] == 0 || covarianceMatrix[1][0] == 0) {
|
|
||||||
targetVector[0] = 1;
|
|
||||||
targetVector[1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lamda^2 + a * lamda + b = 0
|
|
||||||
double a = -covarianceMatrix[0][0] - covarianceMatrix[1][1];
|
|
||||||
double b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1]
|
|
||||||
* covarianceMatrix[1][0];
|
|
||||||
double value = a / 2;
|
|
||||||
double rightside = Math.sqrt(Math.pow(value, 2) - b);
|
|
||||||
double lambda1 = -value + rightside;
|
|
||||||
double lambda2 = -value - rightside;
|
|
||||||
if (lambda1 == lambda2) {
|
|
||||||
targetVector[0] = 0;
|
|
||||||
targetVector[1] = 0;
|
|
||||||
} else {
|
|
||||||
double lambda = lambda1 > lambda2 ? lambda1 : lambda2;
|
|
||||||
targetVector[0] = 1;
|
|
||||||
targetVector[1] = (lambda - covarianceMatrix[0][0]) / covarianceMatrix[0][1];
|
|
||||||
}
|
|
||||||
return targetVector;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +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 com.android.gesture;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An instance represents a sample if the label is available or a query if the
|
|
||||||
* label is null.
|
|
||||||
*/
|
|
||||||
public class Instance {
|
|
||||||
|
|
||||||
private static final int SEQUENCE_SAMPLE_SIZE = 16;
|
|
||||||
|
|
||||||
private static final int PATCH_SAMPLE_SIZE = 8;
|
|
||||||
|
|
||||||
private final static float[] ORIENTATIONS = {
|
|
||||||
0, 45, 90, 135, 180, -0, -45, -90, -135, -180
|
|
||||||
};
|
|
||||||
|
|
||||||
// the feature vector
|
|
||||||
public final float[] vector;
|
|
||||||
|
|
||||||
// the label can be null
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
// the length of the vector
|
|
||||||
public final float magnitude;
|
|
||||||
|
|
||||||
// the id of the instance
|
|
||||||
public final long instanceID;
|
|
||||||
|
|
||||||
private Instance(long id, float[] sample, String sampleName) {
|
|
||||||
instanceID = id;
|
|
||||||
vector = sample;
|
|
||||||
label = sampleName;
|
|
||||||
float sum = 0;
|
|
||||||
int size = sample.length;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
sum += sample[i] * sample[i];
|
|
||||||
}
|
|
||||||
magnitude = (float) Math.sqrt(sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a learning instance for a single stroke gesture
|
|
||||||
*
|
|
||||||
* @param gesture
|
|
||||||
* @param label
|
|
||||||
* @return the instance
|
|
||||||
*/
|
|
||||||
public static Instance createInstance(GestureLibrary gesturelib, Gesture gesture, String label) {
|
|
||||||
float[] pts;
|
|
||||||
if (gesturelib.getGestureType() == GestureLibrary.SEQUENCE_SENSITIVE) {
|
|
||||||
pts = sequenceSampler(gesturelib, gesture);
|
|
||||||
} else {
|
|
||||||
pts = spatialSampler(gesture);
|
|
||||||
}
|
|
||||||
return new Instance(gesture.getID(), pts, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float[] spatialSampler(Gesture gesture) {
|
|
||||||
float[] pts = GestureUtils.spatialFeaturize(gesture, PATCH_SAMPLE_SIZE);
|
|
||||||
return pts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float[] sequenceSampler(GestureLibrary gesturelib, Gesture gesture) {
|
|
||||||
float[] pts = GestureUtils.sequentialFeaturize(gesture.getStrokes().get(0),
|
|
||||||
SEQUENCE_SAMPLE_SIZE);
|
|
||||||
float[] center = GestureUtils.computeCentroid(pts);
|
|
||||||
float orientation = (float) Math.atan2(pts[1] - center[1], pts[0] - center[0]);
|
|
||||||
orientation *= 180 / Math.PI;
|
|
||||||
|
|
||||||
float adjustment = -orientation;
|
|
||||||
if (gesturelib.getOrientationStyle() == GestureLibrary.ORIENTATION_SENSITIVE) {
|
|
||||||
int count = ORIENTATIONS.length;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
float delta = ORIENTATIONS[i] - orientation;
|
|
||||||
if (Math.abs(delta) < Math.abs(adjustment)) {
|
|
||||||
adjustment = delta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
android.graphics.Matrix m = new android.graphics.Matrix();
|
|
||||||
m.setTranslate(-center[0], -center[1]);
|
|
||||||
android.graphics.Matrix rotation = new android.graphics.Matrix();
|
|
||||||
rotation.setRotate(adjustment);
|
|
||||||
m.postConcat(rotation);
|
|
||||||
m.mapPoints(pts);
|
|
||||||
return pts;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,105 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import android.util.Config;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An implementation of an instance-based learner
|
|
||||||
*/
|
|
||||||
|
|
||||||
class InstanceLearner extends Learner {
|
|
||||||
|
|
||||||
private static final String LOGTAG = "InstanceLearner";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ArrayList<Prediction> classify(GestureLibrary lib, Instance instance) {
|
|
||||||
ArrayList<Prediction> predictions = new ArrayList<Prediction>();
|
|
||||||
ArrayList<Instance> instances = getInstances();
|
|
||||||
int count = instances.size();
|
|
||||||
TreeMap<String, Double> label2score = new TreeMap<String, Double>();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
Instance sample = instances.get(i);
|
|
||||||
if (sample.vector.length != instance.vector.length) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
double distance;
|
|
||||||
if (lib.getGestureType() == GestureLibrary.SEQUENCE_SENSITIVE) {
|
|
||||||
distance = GestureUtils.cosineDistance(sample, instance);
|
|
||||||
} else {
|
|
||||||
distance = GestureUtils.euclideanDistance(sample.vector, instance.vector);
|
|
||||||
}
|
|
||||||
double weight;
|
|
||||||
if (distance == 0) {
|
|
||||||
weight = Double.MAX_VALUE;
|
|
||||||
} else {
|
|
||||||
weight = 1 / distance;
|
|
||||||
}
|
|
||||||
Double score = label2score.get(sample.label);
|
|
||||||
if (score == null || weight > score) {
|
|
||||||
label2score.put(sample.label, weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double sum = 0;
|
|
||||||
Iterator<String> lableIterator = label2score.keySet().iterator();
|
|
||||||
while (lableIterator.hasNext()) {
|
|
||||||
String name = lableIterator.next();
|
|
||||||
double score = label2score.get(name);
|
|
||||||
sum += score;
|
|
||||||
predictions.add(new Prediction(name, score));
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize
|
|
||||||
Iterator<Prediction> predictionIterator = predictions.iterator();
|
|
||||||
while (predictionIterator.hasNext()) {
|
|
||||||
Prediction name = predictionIterator.next();
|
|
||||||
name.predictionScore /= sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(predictions, new Comparator<Prediction>() {
|
|
||||||
public int compare(Prediction object1, Prediction object2) {
|
|
||||||
double score1 = object1.predictionScore;
|
|
||||||
double score2 = object2.predictionScore;
|
|
||||||
if (score1 > score2) {
|
|
||||||
return -1;
|
|
||||||
} else if (score1 < score2) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Config.DEBUG) {
|
|
||||||
predictionIterator = predictions.iterator();
|
|
||||||
while (predictionIterator.hasNext()) {
|
|
||||||
Prediction name = predictionIterator.next();
|
|
||||||
Log.v(LOGTAG, "prediction [" + name.gestureName + " = " + name.predictionScore + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return predictions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The abstract class of a gesture learner
|
|
||||||
*/
|
|
||||||
abstract class Learner {
|
|
||||||
|
|
||||||
private final ArrayList<Instance> mInstances = new ArrayList<Instance>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an instance to the learner
|
|
||||||
*
|
|
||||||
* @param instance
|
|
||||||
*/
|
|
||||||
void addInstance(Instance instance) {
|
|
||||||
mInstances.add(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all the instances
|
|
||||||
*
|
|
||||||
* @return instances
|
|
||||||
*/
|
|
||||||
ArrayList<Instance> getInstances() {
|
|
||||||
return mInstances;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove an instance based on its id
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
void removeInstance(long id) {
|
|
||||||
ArrayList<Instance> instances = mInstances;
|
|
||||||
int count = instances.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
Instance instance = instances.get(i);
|
|
||||||
if (id == instance.instanceID) {
|
|
||||||
instances.remove(instance);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all the instances of a category
|
|
||||||
*
|
|
||||||
* @param name the category name
|
|
||||||
*/
|
|
||||||
void removeInstances(String name) {
|
|
||||||
ArrayList<Instance> toDelete = new ArrayList<Instance>();
|
|
||||||
ArrayList<Instance> instances = mInstances;
|
|
||||||
int count = instances.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
Instance instance = instances.get(i);
|
|
||||||
if (instance.label.equals(name)) {
|
|
||||||
toDelete.add(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mInstances.removeAll(toDelete);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract ArrayList<Prediction> classify(GestureLibrary library, Instance instance);
|
|
||||||
}
|
|
||||||
@@ -1,81 +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 com.android.gesture;
|
|
||||||
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.graphics.Path;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An oriented bounding box
|
|
||||||
*/
|
|
||||||
public class OrientedBoundingBox {
|
|
||||||
|
|
||||||
public final float squareness;
|
|
||||||
|
|
||||||
public final float width;
|
|
||||||
public final float height;
|
|
||||||
|
|
||||||
public final float orientation; // -PI<alpha<PI
|
|
||||||
|
|
||||||
public final float centerX;
|
|
||||||
public final float centerY;
|
|
||||||
|
|
||||||
OrientedBoundingBox(float angle, float cx, float cy, float w, float h) {
|
|
||||||
orientation = angle;
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
centerX = cx;
|
|
||||||
centerY = cy;
|
|
||||||
float ratio = w / h;
|
|
||||||
if (ratio > 1) {
|
|
||||||
squareness = 1 / ratio;
|
|
||||||
} else {
|
|
||||||
squareness = ratio;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path toPath() {
|
|
||||||
Path path = new Path();
|
|
||||||
float[] point = new float[2];
|
|
||||||
point[0] = -width / 2;
|
|
||||||
point[1] = height / 2;
|
|
||||||
Matrix matrix = new Matrix();
|
|
||||||
matrix.setRotate(orientation);
|
|
||||||
matrix.postTranslate(centerX, centerY);
|
|
||||||
matrix.mapPoints(point);
|
|
||||||
path.moveTo(point[0], point[1]);
|
|
||||||
|
|
||||||
point[0] = -width / 2;
|
|
||||||
point[1] = -height / 2;
|
|
||||||
matrix.mapPoints(point);
|
|
||||||
path.lineTo(point[0], point[1]);
|
|
||||||
|
|
||||||
point[0] = width / 2;
|
|
||||||
point[1] = -height / 2;
|
|
||||||
matrix.mapPoints(point);
|
|
||||||
path.lineTo(point[0], point[1]);
|
|
||||||
|
|
||||||
point[0] = width / 2;
|
|
||||||
point[1] = height / 2;
|
|
||||||
matrix.mapPoints(point);
|
|
||||||
path.lineTo(point[0], point[1]);
|
|
||||||
|
|
||||||
path.close();
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +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 com.android.gesture;
|
|
||||||
|
|
||||||
public class Prediction {
|
|
||||||
public final String gestureName;
|
|
||||||
|
|
||||||
public double predictionScore;
|
|
||||||
|
|
||||||
Prediction(String label, double score) {
|
|
||||||
gestureName = label;
|
|
||||||
predictionScore = score;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return gestureName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +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 com.android.gesture.example;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
class ContactAdapter extends ArrayAdapter<ContactItem> {
|
|
||||||
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
|
|
||||||
public ContactAdapter(Activity activity, ArrayList<ContactItem> contacts) {
|
|
||||||
super(activity, 0, contacts);
|
|
||||||
mInflater = activity.getLayoutInflater();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContactItem getItem(int position) {
|
|
||||||
return super.getItem(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return getItem(position).itemID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
final ContactItem info = getItem(position);
|
|
||||||
|
|
||||||
View view = convertView;
|
|
||||||
if (view == null) {
|
|
||||||
view = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
|
|
||||||
view.setTag(view.findViewById(android.R.id.text1));
|
|
||||||
}
|
|
||||||
|
|
||||||
final TextView textView = (TextView)view.getTag();
|
|
||||||
textView.setText(info.toString());
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int search(String query) {
|
|
||||||
if (query != null && query.length() > 0) {
|
|
||||||
int start = 0;
|
|
||||||
int end = getCount() - 1;
|
|
||||||
int index = binarySearch(query, start, end);
|
|
||||||
for (index = index - 1; index >= 0; index--) {
|
|
||||||
String str = getItem(index).toString().toLowerCase();
|
|
||||||
if (!str.startsWith(query)) {
|
|
||||||
return index + 1;
|
|
||||||
}
|
|
||||||
if (index == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int binarySearch(String prefix, int start, int end) {
|
|
||||||
if (start > end) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int mid = (start + end) / 2;
|
|
||||||
String str = getItem(mid).toString().toLowerCase();
|
|
||||||
if (prefix.compareTo(str) <= 0) {
|
|
||||||
if (str.startsWith(prefix)) {
|
|
||||||
return mid;
|
|
||||||
} else {
|
|
||||||
return binarySearch(prefix, start, mid - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return binarySearch(prefix, mid + 1, end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,34 +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 com.android.gesture.example;
|
|
||||||
|
|
||||||
|
|
||||||
class ContactItem {
|
|
||||||
final String itemName;
|
|
||||||
|
|
||||||
final long itemID;
|
|
||||||
|
|
||||||
public ContactItem(long id, String name) {
|
|
||||||
itemID = id;
|
|
||||||
itemName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return itemName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +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 com.android.gesture.example;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.Contacts.People;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.Window;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import com.android.gesture.Gesture;
|
|
||||||
import com.android.gesture.GestureActionListener;
|
|
||||||
import com.android.gesture.GestureProcessor;
|
|
||||||
import com.android.gesture.GestureLibrary;
|
|
||||||
import com.android.gesture.GestureOverlay;
|
|
||||||
import com.android.gesture.Prediction;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class ContactListGestureOverlay extends Activity {
|
|
||||||
|
|
||||||
static final String GESTURE_FILE_NAME = Environment.getExternalStorageDirectory().getAbsolutePath()
|
|
||||||
+ File.separator + "gestureOverlay.xml";
|
|
||||||
|
|
||||||
private static final String SORT_ORDER = People.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
|
|
||||||
|
|
||||||
private static final String[] CONTACTS_PROJECTION = new String[] {
|
|
||||||
People._ID, // 0
|
|
||||||
People.DISPLAY_NAME, // 1
|
|
||||||
};
|
|
||||||
|
|
||||||
private GestureOverlay mOverlay;
|
|
||||||
|
|
||||||
private ContactAdapter mContactAdapter;
|
|
||||||
|
|
||||||
private GestureProcessor mGestureProcessor;
|
|
||||||
|
|
||||||
private GestureLibrary mLibrary;
|
|
||||||
|
|
||||||
private ListView mContactList;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
|
||||||
setContentView(R.layout.overlaydemo);
|
|
||||||
|
|
||||||
setProgressBarIndeterminateVisibility(true);
|
|
||||||
|
|
||||||
// load the gesture library
|
|
||||||
mLibrary = new GestureLibrary(GESTURE_FILE_NAME);
|
|
||||||
mLibrary.load();
|
|
||||||
|
|
||||||
// load the contact list
|
|
||||||
mContactList = (ListView)this.findViewById(R.id.list);
|
|
||||||
registerForContextMenu(mContactList);
|
|
||||||
mContactList.setTextFilterEnabled(true);
|
|
||||||
mContactList.setVerticalScrollBarEnabled(true);
|
|
||||||
mContactList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
|
||||||
if (!mGestureProcessor.isGesturing()) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, ContentUris.withAppendedId(
|
|
||||||
People.CONTENT_URI, id));
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ContentResolver resolver = getContentResolver();
|
|
||||||
Cursor cursor = resolver.query(People.CONTENT_URI, CONTACTS_PROJECTION, null, null,
|
|
||||||
SORT_ORDER);
|
|
||||||
ArrayList<ContactItem> list = new ArrayList<ContactItem>();
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
list.add(new ContactItem(cursor.getLong(0), cursor.getString(1)));
|
|
||||||
}
|
|
||||||
mContactAdapter = new ContactAdapter(this, list);
|
|
||||||
mContactList.setAdapter(mContactAdapter);
|
|
||||||
|
|
||||||
setProgressBarIndeterminateVisibility(false);
|
|
||||||
|
|
||||||
// add a gesture overlay on top of the ListView
|
|
||||||
mOverlay = new GestureOverlay(this);
|
|
||||||
mGestureProcessor = new GestureProcessor(mContactList);
|
|
||||||
mGestureProcessor.addGestureActionListener(new GestureActionListener() {
|
|
||||||
public void onGesturePerformed(GestureOverlay overlay, Gesture gesture) {
|
|
||||||
ArrayList<Prediction> predictions = mLibrary.recognize(gesture);
|
|
||||||
if (!predictions.isEmpty()) {
|
|
||||||
int index = mContactAdapter.search(predictions.get(0).gestureName);
|
|
||||||
if (index != -1) {
|
|
||||||
mContactList.setSelection(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mOverlay.addGestureListener(mGestureProcessor);
|
|
||||||
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT);
|
|
||||||
this.addContentView(mOverlay, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,209 +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 com.android.gesture.example;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
|
||||||
|
|
||||||
import com.android.gesture.Gesture;
|
|
||||||
import com.android.gesture.GestureLibrary;
|
|
||||||
import com.android.gesture.GestureListener;
|
|
||||||
import com.android.gesture.GestureOverlay;
|
|
||||||
import com.android.gesture.Prediction;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class GestureEntry extends Activity {
|
|
||||||
|
|
||||||
private static final String PARCEL_KEY = "gesture";
|
|
||||||
|
|
||||||
static final String GESTURE_FILE_NAME = Environment.getExternalStorageDirectory().getAbsolutePath()
|
|
||||||
+ File.separator + "gestureEntry.xml";
|
|
||||||
|
|
||||||
private static final int DIALOG_NEW_ENTRY = 1;
|
|
||||||
|
|
||||||
private static final int NEW_ID = Menu.FIRST;
|
|
||||||
|
|
||||||
private static final int VIEW_ID = Menu.FIRST + 1;
|
|
||||||
|
|
||||||
private GestureOverlay mGesturePad;
|
|
||||||
|
|
||||||
private Spinner mRecognitionResult;
|
|
||||||
|
|
||||||
private GestureLibrary mGestureLibrary;
|
|
||||||
|
|
||||||
private boolean mChangedByRecognizer = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.demo);
|
|
||||||
|
|
||||||
// init the gesture library
|
|
||||||
mGestureLibrary = new GestureLibrary(GESTURE_FILE_NAME);
|
|
||||||
mGestureLibrary.load();
|
|
||||||
|
|
||||||
// create the spinner for showing the recognition results
|
|
||||||
// the spinner also allows a user to correct a prediction
|
|
||||||
mRecognitionResult = (Spinner) findViewById(R.id.spinner);
|
|
||||||
mRecognitionResult.setOnItemSelectedListener(new OnItemSelectedListener() {
|
|
||||||
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
// correct the recognition result by adding the new example
|
|
||||||
if (!mChangedByRecognizer) {
|
|
||||||
mGestureLibrary.addGesture(parent.getSelectedItem().toString(), mGesturePad
|
|
||||||
.getCurrentGesture());
|
|
||||||
} else {
|
|
||||||
mChangedByRecognizer = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// create the area for drawing a gesture
|
|
||||||
mGesturePad = (GestureOverlay) findViewById(R.id.drawingpad);
|
|
||||||
mGesturePad.setBackgroundColor(Color.BLACK);
|
|
||||||
mGesturePad.addGestureListener(new GestureListener() {
|
|
||||||
public void onFinishGesture(GestureOverlay pad, MotionEvent event) {
|
|
||||||
recognize(pad.getCurrentGesture());
|
|
||||||
pad.clear(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onGesture(GestureOverlay pad, MotionEvent event) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStartGesture(GestureOverlay pad, MotionEvent event) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
Gesture g = (Gesture) savedInstanceState.getParcelable(PARCEL_KEY);
|
|
||||||
if (g != null) {
|
|
||||||
mGesturePad.setCurrentGesture(g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
|
||||||
LayoutInflater factory = LayoutInflater.from(this);
|
|
||||||
final View textEntryView = factory.inflate(R.layout.newgesture_dialog, null);
|
|
||||||
return new AlertDialog.Builder(GestureEntry.this).setTitle(
|
|
||||||
R.string.newgesture_text_entry).setView(textEntryView).setPositiveButton(
|
|
||||||
R.string.newgesture_dialog_ok, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int whichButton) {
|
|
||||||
EditText edittext = (EditText) ((AlertDialog) dialog)
|
|
||||||
.findViewById(R.id.gesturename_edit);
|
|
||||||
String text = edittext.getText().toString().trim();
|
|
||||||
if (text.length() > 0) {
|
|
||||||
mGestureLibrary.addGesture(text, mGesturePad.getCurrentGesture());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).setNegativeButton(R.string.newgesture_dialog_cancel,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int whichButton) {
|
|
||||||
}
|
|
||||||
}).create();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
super.onCreateOptionsMenu(menu);
|
|
||||||
menu.add(0, NEW_ID, 0, R.string.newgesture).setShortcut('0', 'n').setIcon(
|
|
||||||
android.R.drawable.ic_menu_add);
|
|
||||||
menu.add(0, VIEW_ID, 0, R.string.viewgesture).setShortcut('1', 'v').setIcon(
|
|
||||||
android.R.drawable.ic_menu_view);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case NEW_ID:
|
|
||||||
if (mGesturePad.getCurrentGesture() != null) {
|
|
||||||
showDialog(DIALOG_NEW_ENTRY);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIEW_ID:
|
|
||||||
startActivityForResult(new Intent(this, GestureLibViewer.class), VIEW_ID);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
mGestureLibrary.load();
|
|
||||||
mGesturePad.clear(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
mGestureLibrary.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPrepareDialog(int id, Dialog dialog) {
|
|
||||||
super.onPrepareDialog(id, dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
Gesture gesture = mGesturePad.getCurrentGesture();
|
|
||||||
if (gesture != null) {
|
|
||||||
outState.putParcelable(PARCEL_KEY, gesture);
|
|
||||||
}
|
|
||||||
mGestureLibrary.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recognize(Gesture ink) {
|
|
||||||
mChangedByRecognizer = true;
|
|
||||||
ArrayList<Prediction> predictions = mGestureLibrary.recognize(ink);
|
|
||||||
ArrayAdapter<Prediction> adapter = new ArrayAdapter<Prediction>(this,
|
|
||||||
android.R.layout.simple_spinner_item, predictions);
|
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
mRecognitionResult.setAdapter(adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
103
tests/sketch/src/com/android/gesture/example/GestureEntryDemo.java
Normal file → Executable file
103
tests/sketch/src/com/android/gesture/example/GestureEntryDemo.java
Normal file → Executable file
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009 Google Inc.
|
* Copyright (C) 2008 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -21,34 +21,29 @@ import android.app.AlertDialog;
|
|||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
|
|
||||||
import com.android.gesture.Gesture;
|
import com.android.gesture.Gesture;
|
||||||
import com.android.gesture.GestureLibrary;
|
import com.android.gesture.GestureLib;
|
||||||
import com.android.gesture.GestureListener;
|
import com.android.gesture.GestureListener;
|
||||||
import com.android.gesture.GesturePad;
|
import com.android.gesture.GesturePad;
|
||||||
import com.android.gesture.Prediction;
|
import com.android.gesture.R;
|
||||||
|
import com.android.gesture.recognizer.Prediction;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The demo shows how to construct a gesture-based user interface on Android.
|
* The demo shows how to construct a gesture-based user interface on Android.
|
||||||
*
|
|
||||||
* @author liyang@google.com (Yang Li)
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class GestureEntryDemo extends Activity {
|
public class GestureEntryDemo extends Activity {
|
||||||
@@ -57,35 +52,31 @@ public class GestureEntryDemo extends Activity {
|
|||||||
private static final int NEW_ID = Menu.FIRST;
|
private static final int NEW_ID = Menu.FIRST;
|
||||||
private static final int VIEW_ID = Menu.FIRST + 1;
|
private static final int VIEW_ID = Menu.FIRST + 1;
|
||||||
|
|
||||||
private GesturePad mGesturePad;
|
GesturePad mView;
|
||||||
private Spinner mRecognitionResult;
|
Spinner mResult;
|
||||||
private GestureLibrary mGestureLibrary;
|
GestureLib mRecognizer;
|
||||||
private boolean mChangedByRecognizer = false;
|
boolean mChangedByRecognizer = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.demo);
|
setContentView(R.layout.demo);
|
||||||
|
|
||||||
// init the gesture library
|
// init the recognizer
|
||||||
mGestureLibrary = new GestureLibrary(
|
mRecognizer = new GestureLib("/sdcard/gestureentry");
|
||||||
"/sdcard/gestureentry/gestures.xml");
|
mRecognizer.load();
|
||||||
mGestureLibrary.load();
|
|
||||||
|
|
||||||
// create the spinner for showing the recognition results
|
// create the spinner for showing the recognition results
|
||||||
// the spinner also allows a user to correct a prediction
|
// the spinner also allows a user to correct a prediction
|
||||||
mRecognitionResult = (Spinner) findViewById(R.id.spinner);
|
mResult = (Spinner) findViewById(R.id.spinner);
|
||||||
mRecognitionResult.setOnItemSelectedListener(
|
mResult.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
new OnItemSelectedListener() {
|
|
||||||
|
|
||||||
public void onItemSelected(
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
// correct the recognition result by adding the new example
|
// correct the recognition result by adding the new example
|
||||||
if (mChangedByRecognizer == false) {
|
if (mChangedByRecognizer == false) {
|
||||||
mGestureLibrary.addGesture(
|
mRecognizer.addGesture(parent.getSelectedItem().toString(),
|
||||||
parent.getSelectedItem().toString(),
|
mView.getCurrentGesture());
|
||||||
mGesturePad.getCurrentGesture());
|
|
||||||
} else {
|
} else {
|
||||||
mChangedByRecognizer = false;
|
mChangedByRecognizer = false;
|
||||||
}
|
}
|
||||||
@@ -99,35 +90,28 @@ public class GestureEntryDemo extends Activity {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// create the area for drawing a gesture
|
// create the area for drawing a gesture
|
||||||
mGesturePad = (GesturePad)this.findViewById(R.id.drawingpad);
|
mView = (GesturePad)this.findViewById(R.id.drawingpad);
|
||||||
mGesturePad.setBackgroundColor(Color.BLACK);
|
mView.cacheGesture(false);
|
||||||
mGesturePad.addGestureListener(new GestureListener() {
|
mView.setFadingOut(false);
|
||||||
public void onFinishGesture(GesturePad pad, MotionEvent event) {
|
mView.addGestureListener(new GestureListener() {
|
||||||
|
public void onFinishGesture(GesturePad patch, MotionEvent event) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
recognize(pad.getCurrentGesture());
|
recognize(patch.getCurrentGesture());
|
||||||
}
|
}
|
||||||
public void onGesture(GesturePad pad, MotionEvent event) {
|
public void onGesture(GesturePad patch, MotionEvent event) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
}
|
}
|
||||||
public void onStartGesture(GesturePad pad, MotionEvent event) {
|
public void onStartGesture(GesturePad patch, MotionEvent event) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Button clear = (Button)this.findViewById(R.id.clear);
|
|
||||||
clear.setOnClickListener(new OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
mGesturePad.clear(false);
|
|
||||||
mGesturePad.invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
Gesture g = (Gesture)savedInstanceState.getParcelable("gesture");
|
mView.setCurrentGesture(
|
||||||
if (g != null) {
|
(Gesture)savedInstanceState.getParcelable("gesture"));
|
||||||
mGesturePad.setCurrentGesture(g);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,12 +129,10 @@ public class GestureEntryDemo extends Activity {
|
|||||||
public void onClick(DialogInterface dialog, int whichButton) {
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
/* User clicked OK so do some stuff */
|
/* User clicked OK so do some stuff */
|
||||||
EditText edittext =
|
EditText edittext =
|
||||||
(EditText)((AlertDialog)dialog).findViewById(
|
(EditText)((AlertDialog)dialog).findViewById(R.id.gesturename_edit);
|
||||||
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(
|
mRecognizer.addGesture(text, mView.getCurrentGesture());
|
||||||
text, mGesturePad.getCurrentGesture());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -182,7 +164,7 @@ public class GestureEntryDemo extends Activity {
|
|||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case NEW_ID:
|
case NEW_ID:
|
||||||
// if there has been a gesture on the canvas
|
// if there has been a gesture on the canvas
|
||||||
if (mGesturePad.getCurrentGesture() != null) {
|
if (mView.getCurrentGesture() != null) {
|
||||||
showDialog(DIALOG_NEW_ENTRY);
|
showDialog(DIALOG_NEW_ENTRY);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -198,17 +180,16 @@ public class GestureEntryDemo extends Activity {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
int requestCode, int resultCode, Intent data) {
|
mRecognizer.load();
|
||||||
mGestureLibrary.load();
|
mView.clear();
|
||||||
mGesturePad.clear(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
super.onPause();
|
super.onPause();
|
||||||
mGestureLibrary.save();
|
mRecognizer.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -222,20 +203,18 @@ public class GestureEntryDemo extends Activity {
|
|||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
Gesture gesture = mGesturePad.getCurrentGesture();
|
outState.putParcelable("gesture", mView.getCurrentGesture());
|
||||||
if (gesture != null)
|
mRecognizer.save();
|
||||||
outState.putParcelable("gesture", gesture);
|
|
||||||
mGestureLibrary.save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recognize(Gesture ink) {
|
public void recognize(Gesture ink) {
|
||||||
mChangedByRecognizer = true;
|
mChangedByRecognizer = true;
|
||||||
ArrayList<Prediction> predictions = mGestureLibrary.recognize(ink);
|
ArrayList<Prediction> predictions = mRecognizer.recognize(ink);
|
||||||
ArrayAdapter<Prediction> adapter = new ArrayAdapter<Prediction>(this,
|
ArrayAdapter adapter = new ArrayAdapter(this,
|
||||||
android.R.layout.simple_spinner_item, predictions);
|
android.R.layout.simple_spinner_item, predictions);
|
||||||
adapter.setDropDownViewResource(
|
adapter.setDropDownViewResource(
|
||||||
android.R.layout.simple_spinner_dropdown_item);
|
android.R.layout.simple_spinner_dropdown_item);
|
||||||
mRecognitionResult.setAdapter(adapter);
|
mResult.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
260
tests/sketch/src/com/android/gesture/example/GestureLibViewer.java
Normal file → Executable file
260
tests/sketch/src/com/android/gesture/example/GestureLibViewer.java
Normal file → Executable file
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2009 The Android Open Source Project
|
* Copyright (C) 2008 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
package com.android.gesture.example;
|
package com.android.gesture.example;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.graphics.Path;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -28,156 +30,228 @@ import android.widget.Spinner;
|
|||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
|
|
||||||
import com.android.gesture.Gesture;
|
import com.android.gesture.Gesture;
|
||||||
import com.android.gesture.GestureLibrary;
|
import com.android.gesture.GestureLib;
|
||||||
import com.android.gesture.GestureOverlay;
|
import com.android.gesture.GesturePad;
|
||||||
|
import com.android.gesture.R;
|
||||||
|
import com.android.gesture.recognizer.Instance;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GestureLibViewer gives an example on how to browse existing gestures and
|
* GestureLibViewer is for viewing existing gestures and
|
||||||
* removing unwanted gestures.
|
* removing unwanted gestures.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class GestureLibViewer extends Activity {
|
public class GestureLibViewer extends Activity {
|
||||||
|
|
||||||
private GestureOverlay mGesturePad;
|
GesturePad mView;
|
||||||
|
Spinner mResult;
|
||||||
private Spinner mGestureCategory;
|
GestureLib mRecognizer;
|
||||||
|
ArrayList<Gesture> mSamples;
|
||||||
private GestureLibrary mGesureLibrary;
|
int mCurrentGestureIndex;
|
||||||
|
|
||||||
private ArrayList<Gesture> mGestures;
|
|
||||||
|
|
||||||
private int mCurrentGestureIndex;
|
|
||||||
|
|
||||||
private class RemoveGestureListener implements OnClickListener {
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (mGestures.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = (String) mGestureCategory.getSelectedItem();
|
|
||||||
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
|
||||||
mGesureLibrary.removeGesture(name, gesture);
|
|
||||||
|
|
||||||
mGestures = mGesureLibrary.getGestures(name);
|
|
||||||
|
|
||||||
if (mGestures == null) {
|
|
||||||
// delete the entire entry
|
|
||||||
mCurrentGestureIndex = 0;
|
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
|
||||||
list.addAll(mGesureLibrary.getGestureEntries());
|
|
||||||
Collections.sort(list);
|
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(GestureLibViewer.this,
|
|
||||||
android.R.layout.simple_spinner_item, list);
|
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
mGestureCategory.setAdapter(adapter);
|
|
||||||
} else {
|
|
||||||
if (mCurrentGestureIndex > mGestures.size() - 1) {
|
|
||||||
mCurrentGestureIndex--;
|
|
||||||
}
|
|
||||||
gesture = mGestures.get(mCurrentGestureIndex);
|
|
||||||
mGesturePad.setCurrentGesture(gesture);
|
|
||||||
mGesturePad.invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.gestureviewer);
|
setContentView(R.layout.gestureviewer);
|
||||||
|
|
||||||
// create the area for drawing a gesture
|
// create the area for drawing a glyph
|
||||||
mGesturePad = (GestureOverlay) findViewById(R.id.drawingpad);
|
mView = (GesturePad)this.findViewById(R.id.drawingpad);
|
||||||
mGesturePad.setEnabled(false);
|
mView.cacheGesture(false);
|
||||||
|
mView.setFadingOut(false);
|
||||||
|
mView.setEnableInput(false);
|
||||||
|
|
||||||
// init the gesture library
|
// init the recognizer
|
||||||
mGesureLibrary = new GestureLibrary(GestureEntry.GESTURE_FILE_NAME);
|
mRecognizer = new GestureLib("/sdcard/gestureentry");
|
||||||
mGesureLibrary.load();
|
mRecognizer.load();
|
||||||
|
|
||||||
mGestureCategory = (Spinner) findViewById(R.id.spinner);
|
mResult = (Spinner) findViewById(R.id.spinner);
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
if (!mGesureLibrary.getGestureEntries().isEmpty()) {
|
list.addAll(mRecognizer.getLabels());
|
||||||
list.addAll(mGesureLibrary.getGestureEntries());
|
Collections.sort(list);
|
||||||
Collections.sort(list);
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
|
android.R.layout.simple_spinner_item,
|
||||||
android.R.layout.simple_spinner_item, list);
|
list);
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
adapter.setDropDownViewResource(
|
||||||
mGestureCategory.setAdapter(adapter);
|
android.R.layout.simple_spinner_dropdown_item);
|
||||||
mGestures = mGesureLibrary.getGestures(list.get(0));
|
mResult.setAdapter(adapter);
|
||||||
|
mSamples = mRecognizer.getGestures(list.get(0));
|
||||||
|
if (mSamples.isEmpty() == false) {
|
||||||
mCurrentGestureIndex = 0;
|
mCurrentGestureIndex = 0;
|
||||||
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
Gesture gesture = mSamples.get(mCurrentGestureIndex);
|
||||||
mGesturePad.setCurrentGesture(gesture);
|
mView.setCurrentGesture(gesture);
|
||||||
|
mView.clearDebugPath();
|
||||||
|
mView.addDebugPath(
|
||||||
|
toPath(mRecognizer.getClassifier().getInstance(gesture.getID())));
|
||||||
}
|
}
|
||||||
|
|
||||||
mGestureCategory.setOnItemSelectedListener(new OnItemSelectedListener() {
|
mResult.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
mGestures = mGesureLibrary.getGestures((String) mGestureCategory.getSelectedItem());
|
// TODO Auto-generated method stub
|
||||||
if (!mGestures.isEmpty()) {
|
mSamples = mRecognizer.getGestures(
|
||||||
|
(String)mResult.getSelectedItem());
|
||||||
|
if (mSamples.isEmpty() == false) {
|
||||||
mCurrentGestureIndex = 0;
|
mCurrentGestureIndex = 0;
|
||||||
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
Gesture gesture = mSamples.get(mCurrentGestureIndex);
|
||||||
mGesturePad.setCurrentGesture(gesture);
|
mView.setCurrentGesture(gesture);
|
||||||
|
mView.clearDebugPath();
|
||||||
|
mView.addDebugPath(
|
||||||
|
toPath(mRecognizer.getClassifier().getInstance(gesture.getID())));
|
||||||
}
|
}
|
||||||
mGesturePad.invalidate();
|
mView.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Button remove = (Button) findViewById(R.id.remove);
|
Button remove = (Button)this.findViewById(R.id.remove);
|
||||||
remove.setOnClickListener(new RemoveGestureListener());
|
remove.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
if (mSamples.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
Button next = (Button) findViewById(R.id.next);
|
String name = (String)mResult.getSelectedItem();
|
||||||
|
Gesture gesture = mSamples.get(mCurrentGestureIndex);
|
||||||
|
mRecognizer.removeGesture(name, gesture);
|
||||||
|
|
||||||
|
mSamples = mRecognizer.getGestures(name);
|
||||||
|
|
||||||
|
if (mSamples == null) {
|
||||||
|
// delete the entire entry
|
||||||
|
mCurrentGestureIndex = 0;
|
||||||
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
|
list.addAll(mRecognizer.getLabels());
|
||||||
|
Collections.sort(list);
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
|
||||||
|
GestureLibViewer.this,
|
||||||
|
android.R.layout.simple_spinner_item,
|
||||||
|
list);
|
||||||
|
adapter.setDropDownViewResource(
|
||||||
|
android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
mResult.setAdapter(adapter);
|
||||||
|
} else {
|
||||||
|
if (mCurrentGestureIndex > mSamples.size()-1) {
|
||||||
|
mCurrentGestureIndex--;
|
||||||
|
}
|
||||||
|
gesture = mSamples.get(mCurrentGestureIndex);
|
||||||
|
mView.setCurrentGesture(gesture);
|
||||||
|
mView.clearDebugPath();
|
||||||
|
mView.addDebugPath(
|
||||||
|
toPath(mRecognizer.getClassifier().getInstance(gesture.getID())));
|
||||||
|
mView.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button next = (Button)this.findViewById(R.id.next);
|
||||||
next.setOnClickListener(new OnClickListener() {
|
next.setOnClickListener(new OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (mCurrentGestureIndex >= mGestures.size() - 1) {
|
// TODO Auto-generated method stub
|
||||||
|
if (mCurrentGestureIndex >= mSamples.size()-1)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
mCurrentGestureIndex++;
|
mCurrentGestureIndex++;
|
||||||
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
Gesture gesture = mSamples.get(mCurrentGestureIndex);
|
||||||
mGesturePad.setCurrentGesture(gesture);
|
mView.setCurrentGesture(gesture);
|
||||||
mGesturePad.invalidate();
|
mView.clearDebugPath();
|
||||||
|
mView.addDebugPath(
|
||||||
|
toPath(mRecognizer.getClassifier().getInstance(gesture.getID())));
|
||||||
|
mView.invalidate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Button previous = (Button) findViewById(R.id.previous);
|
Button previous = (Button)this.findViewById(R.id.previous);
|
||||||
previous.setOnClickListener(new OnClickListener() {
|
previous.setOnClickListener(new OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (mCurrentGestureIndex >= 1 && !mGestures.isEmpty()) {
|
// TODO Auto-generated method stub
|
||||||
|
if (mCurrentGestureIndex >= 1 &&
|
||||||
|
mSamples.isEmpty() == false) {
|
||||||
mCurrentGestureIndex--;
|
mCurrentGestureIndex--;
|
||||||
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
Gesture gesture = mSamples.get(mCurrentGestureIndex);
|
||||||
mGesturePad.setCurrentGesture(gesture);
|
mView.setCurrentGesture(gesture);
|
||||||
mGesturePad.invalidate();
|
mView.clearDebugPath();
|
||||||
|
mView.addDebugPath(
|
||||||
|
toPath(mRecognizer.getClassifier().getInstance(gesture.getID())));
|
||||||
|
mView.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ArrayList<Path> toPath(Instance instance) {
|
||||||
|
ArrayList<Path> paths = new ArrayList();
|
||||||
|
Path path = null;
|
||||||
|
float minx = 0, miny = 0;
|
||||||
|
float mX = 0, mY = 0;
|
||||||
|
for (int i=0; i<instance.vector.length; i+=2) {
|
||||||
|
float x = instance.vector[i];
|
||||||
|
float y = instance.vector[i+1];
|
||||||
|
if (x < minx)
|
||||||
|
minx = x;
|
||||||
|
if (y < miny)
|
||||||
|
miny = y;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Matrix matrix = new Matrix();
|
||||||
|
matrix.setTranslate(-minx + 10, -miny + 10);
|
||||||
|
path.transform(matrix);
|
||||||
|
paths.add(path);
|
||||||
|
|
||||||
|
path = new Path();
|
||||||
|
path.moveTo(instance.vector[0]-5, instance.vector[1]-5);
|
||||||
|
path.lineTo(instance.vector[0]-5, instance.vector[1]+5);
|
||||||
|
path.lineTo(instance.vector[0]+5, instance.vector[1]+5);
|
||||||
|
path.lineTo(instance.vector[0]+5, instance.vector[1]-5);
|
||||||
|
path.close();
|
||||||
|
path.transform(matrix);
|
||||||
|
paths.add(path);
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
// TODO Auto-generated method stub
|
||||||
mGesureLibrary.save();
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
setResult(RESULT_OK);
|
mRecognizer.save();
|
||||||
finish();
|
this.setResult(RESULT_OK);
|
||||||
return true;
|
finish();
|
||||||
} else {
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
super.onPause();
|
super.onPause();
|
||||||
mGesureLibrary.save();
|
mRecognizer.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
mGesureLibrary.save();
|
mRecognizer.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user