Recovered the code of the gesture library
diff --git a/tests/sketch/src/com/android/gesture/GestureLibrary.java b/tests/sketch/src/com/android/gesture/GestureLibrary.java
new file mode 100644
index 0000000..c89aa16
--- /dev/null
+++ b/tests/sketch/src/com/android/gesture/GestureLibrary.java
@@ -0,0 +1,337 @@
+/*
+ * 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;
+
+    // when SEQUENCE_SENSITIVE is used, only single stroke gestures are allowed
+    public static final int SEQUENCE_SENSITIVE = 2;
+
+    private int mSequenceType = SEQUENCE_SENSITIVE;
+
+    public static final int ORIENTATION_INVARIANT = 1;
+
+    // ORIENTATION_SENSITIVE is only available for single stroke gestures
+    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);
+        }
+        if (entryName == null || entryName.length() == 0) {
+            return;
+        }
+        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) {
+        }
+    }
+}