blob: 29c07adbc18a7de92495b3ac86d8d87bc52dbae3 [file] [log] [blame]
/*
* 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.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.gesture.recognizer.RecognitionUtil;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;
/**
* A single stroke gesture.
*/
public class Gesture implements Parcelable {
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 long systemStartupTime = System.currentTimeMillis();
private static int instanceCount = 0;
public Gesture() {
mID = systemStartupTime + instanceCount++;
}
public void setColor(int c) {
mColor = c;
}
public void setStrokeWidth(float w) {
mWidth = w;
}
public int getColor() {
return mColor;
}
public float getStrokeWidth() {
return mWidth;
}
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();
}
/**
* @return the length of the gesture
*/
public float getLength() {
return this.mLength;
}
public RectF getBBX() {
return mBBX;
}
public void setID(long id) {
mID = id;
}
public long getID() {
return mID;
}
public long getTimeStamp() {
return mTimestamp;
}
public void setTimestamp(long t) {
this.mTimestamp = t;
}
/**
* draw the gesture
* @param canvas
*/
public void draw(Canvas canvas) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(mColor);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(mWidth);
Path path = null;
float mX = 0, mY = 0;
Iterator<PointF> it = mPtsBuffer.iterator();
while (it.hasNext()) {
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;
}
}
}
canvas.drawPath(path, paint);
}
/**
* convert the gesture to a Path
* @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 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 numSample
* @param foreground
* @return
*/
public Bitmap toBitmap(int w, int h,
int edge, int numSample) {
RectF bbx = this.getBBX();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
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.setAntiAlias(true);
paint.setDither(true);
paint.setColor(mColor);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(2);
c.drawPath(path, paint);
return bitmap;
}
/**
* save the gesture as XML
* @param namespace
* @param serializer
* @throws IOException
*/
public void toXML(String namespace, XmlSerializer serializer) throws IOException {
serializer.startTag(namespace, "stroke");
serializer.attribute(namespace, "timestamp", Long.toString(mTimestamp));
serializer.attribute(namespace, "id", Long.toString(mID));
serializer.attribute(namespace, "color", Integer.toString(mColor));
serializer.attribute(namespace, "width", Float.toString(mWidth));
Iterator it = this.mPtsBuffer.iterator();
String pts = "";
while (it.hasNext()) {
PointF fp = (PointF)it.next();
if (pts.length() > 0)
pts += ",";
pts += fp.x + "," + fp.y;
}
serializer.text(pts);
serializer.endTag(namespace, "stroke");
}
public void createFromString(String str) {
StringTokenizer st = new StringTokenizer(str, "#");
String para = st.nextToken();
StringTokenizer innerst = new StringTokenizer(para, ",");
this.mBBX = new RectF();
this.mBBX.left = Float.parseFloat(innerst.nextToken());
this.mBBX.top = Float.parseFloat(innerst.nextToken());
this.mBBX.right = Float.parseFloat(innerst.nextToken());
this.mBBX.bottom = Float.parseFloat(innerst.nextToken());
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);
}
@Override
public String toString() {
String str = "";
str += "#" + this.mBBX.left + "," + this.mBBX.top + "," +
this.mBBX.right + "," + this.mBBX.bottom;
str += "#";
Iterator<PointF> it = this.mPtsBuffer.iterator();
while (it.hasNext()) {
PointF fp = it.next();
str += fp.x + "," + fp.y + ",";
}
str += "#";
str += this.mColor;
str += "#";
str += this.mWidth;
str += "#";
str += this.mLength;
str += "#";
str += this.mTimestamp;
return str;
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Gesture createFromParcel(Parcel in) {
String str = in.readString();
Gesture stk = new Gesture();
stk.createFromString(str);
return stk;
}
public Gesture[] newArray(int size) {
return new Gesture[size];
}
};
public static Gesture buildFromArray(byte[] bytes) {
String str = new String(bytes);
Gesture stk = new Gesture();
stk.createFromString(str);
return stk;
}
public static byte[] saveToArray(Gesture stk) {
String str = stk.toString();
return str.getBytes();
}
public void writeToParcel(Parcel out, int flags) {
out.writeString(this.toString());
}
public int describeContents() {
return CONTENTS_FILE_DESCRIPTOR;
}
}