blob: d0a25f22058d2c84c07e79247123b424344b65c6 [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.util.Log;
import android.util.Xml;
import android.util.Xml.Encoding;
import com.android.gesture.recognizer.Classifier;
import com.android.gesture.recognizer.Instance;
import com.android.gesture.recognizer.NearestNeighbor;
import com.android.gesture.recognizer.Prediction;
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;
import java.util.StringTokenizer;
public class GestureLib {
private static final String LOGTAG = "GestureLib";
private static String namespace = "ink";
private final String datapath;
private HashMap<String, ArrayList<Gesture>> name2gestures =
new HashMap<String, ArrayList<Gesture>>();
private Classifier mClassifier;
public GestureLib(String path) {
datapath = path;
mClassifier = new NearestNeighbor();
}
public Classifier getClassifier() {
return mClassifier;
}
/**
* get all the labels in the library
* @return a set of strings
*/
public Set<String> getLabels() {
return name2gestures.keySet();
}
public ArrayList<Prediction> recognize(Gesture gesture) {
Instance instance = Instance.createInstance(gesture, null);
return mClassifier.classify(instance);
}
public void addGesture(String name, Gesture gesture) {
Log.v(LOGTAG, "add an example for gesture: " + name);
ArrayList<Gesture> gestures = name2gestures.get(name);
if (gestures == null) {
gestures = new ArrayList<Gesture>();
name2gestures.put(name, gestures);
}
gestures.add(gesture);
mClassifier.addInstance(
Instance.createInstance(gesture, name));
}
public void removeGesture(String name, Gesture gesture) {
ArrayList<Gesture> gestures = name2gestures.get(name);
if (gestures == null) {
return;
}
gestures.remove(gesture);
// if there are no more samples, remove the entry automatically
if (gestures.isEmpty()) {
name2gestures.remove(name);
}
mClassifier.removeInstance(gesture.getID());
}
public ArrayList<Gesture> getGestures(String label) {
ArrayList<Gesture> gestures = name2gestures.get(label);
if (gestures != null)
return (ArrayList<Gesture>)gestures.clone();
else
return null;
}
public void load() {
String filename = datapath
+ File.separator + "gestures.xml";
File f = new File(filename);
if (f.exists()) {
try {
loadInk(filename, null);
}
catch (SAXException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public void save() {
try {
compactSave();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void compactSave() throws IOException {
File f = new File(datapath);
if (f.exists() == false) {
f.mkdirs();
}
String filename = datapath + File.separator + "gestures.xml";
Log.v(LOGTAG, "save to " + filename);
BufferedOutputStream fos = new BufferedOutputStream(
new FileOutputStream(filename));
PrintWriter writer = new PrintWriter(fos);
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(writer);
serializer.startDocument(Encoding.ISO_8859_1.name(), null);
serializer.startTag(namespace, "gestures");
Iterator<String> it = name2gestures.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
ArrayList<Gesture> gestures = name2gestures.get(key);
saveGestures(serializer, key, gestures);
}
serializer.endTag(namespace, "gestures");
serializer.endDocument();
serializer.flush();
writer.close();
fos.close();
}
private static void saveGestures(XmlSerializer serializer,
String name, ArrayList<Gesture> strokes) throws IOException {
serializer.startTag(namespace, "gesture");
serializer.startTag(namespace, "name");
serializer.text(name);
serializer.endTag(namespace, "name");
Iterator<Gesture> it = strokes.iterator();
while (it.hasNext()) {
Gesture stk = it.next();
stk.toXML(namespace, serializer);
}
serializer.endTag(namespace, "gesture");
}
private void loadInk(String filename, String label) throws SAXException, IOException {
Log.v(LOGTAG, "load from " + filename);
BufferedInputStream in = new BufferedInputStream(
new FileInputStream(filename));
Xml.parse(in, Encoding.ISO_8859_1, new CompactInkHandler());
in.close();
}
class CompactInkHandler implements ContentHandler {
Gesture currentGesture = null;
StringBuffer buffer = null;
String gestureName;
ArrayList<Gesture> gestures;
CompactInkHandler() {
}
// Receive notification of character data.
public void characters(char[] ch, int start, int length) {
buffer.append(ch, start, length);
}
//Receive notification of the end of a document.
public void endDocument() {
}
// Receive notification of the end of an element.
public void endElement(String uri, String localName, String qName) {
if (localName.equals("gesture")) {
name2gestures.put(gestureName, gestures);
gestures = null;
} else if (localName.equals("name")) {
gestureName = buffer.toString();
} else if (localName.equals("stroke")) {
StringTokenizer tokenizer = new StringTokenizer(buffer.toString(), ",");
while (tokenizer.hasMoreTokens()) {
String str = tokenizer.nextToken();
float x = Float.parseFloat(str);
str = tokenizer.nextToken();
float y = Float.parseFloat(str);
try
{
currentGesture.addPoint(x, y);
}
catch(Exception ex) {
ex.printStackTrace();
}
}
gestures.add(currentGesture);
mClassifier.addInstance(
Instance.createInstance(currentGesture, gestureName));
currentGesture = null;
}
}
// End the scope of a prefix-URI mapping.
public void endPrefixMapping(String prefix) {
}
//Receive notification of ignorable whitespace in element content.
public void ignorableWhitespace(char[] ch, int start, int length) {
}
//Receive notification of a processing instruction.
public void processingInstruction(String target, String data) {
}
// Receive an object for locating the origin of SAX document events.
public void setDocumentLocator(Locator locator) {
}
// Receive notification of a skipped entity.
public void skippedEntity(String name) {
}
// Receive notification of the beginning of a document.
public void startDocument() {
}
// Receive notification of the beginning of an element.
public void startElement(String uri, String localName, String qName, Attributes atts) {
if (localName.equals("gesture")) {
gestures = new ArrayList<Gesture>();
} else if (localName.equals("name")) {
buffer = new StringBuffer();
} else if (localName.equals("stroke")) {
currentGesture = new Gesture();
currentGesture.setTimestamp(Long.parseLong(atts.getValue(namespace, "timestamp")));
currentGesture.setColor(Integer.parseInt(atts.getValue(namespace, "color")));
currentGesture.setStrokeWidth(Float.parseFloat(atts.getValue(namespace, "width")));
buffer = new StringBuffer();
}
}
public void startPrefixMapping(String prefix, String uri) {
}
}
}