10a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy/*
20a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * Copyright (C) 2008-2009 The Android Open Source Project
30a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy *
40a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * Licensed under the Apache License, Version 2.0 (the "License");
50a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * you may not use this file except in compliance with the License.
60a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * You may obtain a copy of the License at
70a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy *
80a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy *      http://www.apache.org/licenses/LICENSE-2.0
90a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy *
100a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * Unless required by applicable law or agreed to in writing, software
110a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * distributed under the License is distributed on an "AS IS" BASIS,
120a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * See the License for the specific language governing permissions and
140a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * limitations under the License.
150a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy */
160a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
170a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guypackage android.gesture;
180a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
190a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport android.util.Log;
200a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport android.os.SystemClock;
210a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
220a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.io.BufferedInputStream;
230a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.io.BufferedOutputStream;
240a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.io.IOException;
250a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.io.DataOutputStream;
260a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.io.DataInputStream;
270a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.io.InputStream;
280a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.io.OutputStream;
290a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.util.ArrayList;
300a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.util.HashMap;
310a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.util.Set;
320a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport java.util.Map;
330a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
340a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guyimport static android.gesture.GestureConstants.LOG_TAG;
350a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
360a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy/**
370a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * GestureLibrary maintains gesture examples and makes predictions on a new
380a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy * gesture
390a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy */
400a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//
410a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//    File format for GestureStore:
420a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//
430a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                Nb. bytes   Java type   Description
440a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                -----------------------------------
450a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//    Header
460a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                2 bytes     short       File format version number
470a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                4 bytes     int         Number of entries
480a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//    Entry
490a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                X bytes     UTF String  Entry name
500a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                4 bytes     int         Number of gestures
510a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//    Gesture
520a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                8 bytes     long        Gesture ID
530a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                4 bytes     int         Number of strokes
540a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//    Stroke
550a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                4 bytes     int         Number of points
560a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//    Point
570a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                4 bytes     float       X coordinate of the point
580a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                4 bytes     float       Y coordinate of the point
590a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//                8 bytes     long        Time stamp
600a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy//
610a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guypublic class GestureStore {
620a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public static final int SEQUENCE_INVARIANT = 1;
630a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    // when SEQUENCE_SENSITIVE is used, only single stroke gestures are currently allowed
640a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public static final int SEQUENCE_SENSITIVE = 2;
650a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
660a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures
670a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public static final int ORIENTATION_INVARIANT = 1;
684758f1216bd16763c72500bc3c2f0fb43c08d613Yang Li    // at most 2 directions can be recognized
690a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public static final int ORIENTATION_SENSITIVE = 2;
704758f1216bd16763c72500bc3c2f0fb43c08d613Yang Li    // at most 4 directions can be recognized
714758f1216bd16763c72500bc3c2f0fb43c08d613Yang Li    static final int ORIENTATION_SENSITIVE_4 = 4;
724758f1216bd16763c72500bc3c2f0fb43c08d613Yang Li    // at most 8 directions can be recognized
734758f1216bd16763c72500bc3c2f0fb43c08d613Yang Li    static final int ORIENTATION_SENSITIVE_8 = 8;
740a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
750a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    private static final short FILE_FORMAT_VERSION = 1;
760a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
770a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    private static final boolean PROFILE_LOADING_SAVING = false;
780a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
790a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    private int mSequenceType = SEQUENCE_SENSITIVE;
800a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    private int mOrientationStyle = ORIENTATION_SENSITIVE;
810a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
820a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    private final HashMap<String, ArrayList<Gesture>> mNamedGestures =
830a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            new HashMap<String, ArrayList<Gesture>>();
840a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
850a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    private Learner mClassifier;
860a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
870a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    private boolean mChanged = false;
880a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
890a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public GestureStore() {
900a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mClassifier = new InstanceLearner();
910a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
920a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
930a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
940a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Specify how the gesture library will handle orientation.
950a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE
960a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     *
970a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param style
980a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
990a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void setOrientationStyle(int style) {
1000a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mOrientationStyle = style;
1010a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1020a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1030a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public int getOrientationStyle() {
1040a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        return mOrientationStyle;
1050a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1060a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1070a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
1080a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
1090a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
1100a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void setSequenceType(int type) {
1110a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mSequenceType = type;
1120a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1130a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1140a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
1150a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
1160a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
1170a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public int getSequenceType() {
1180a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        return mSequenceType;
1190a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1200a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1210a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
1220a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Get all the gesture entry names in the library
1230a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     *
1240a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @return a set of strings
1250a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
1260a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public Set<String> getGestureEntries() {
1270a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        return mNamedGestures.keySet();
1280a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1290a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1300a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
1310a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Recognize a gesture
1320a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     *
1330a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param gesture the query
1340a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @return a list of predictions of possible entries for a given gesture
1350a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
1360a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public ArrayList<Prediction> recognize(Gesture gesture) {
1370a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        Instance instance = Instance.createInstance(mSequenceType,
1380a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                mOrientationStyle, gesture, null);
1394758f1216bd16763c72500bc3c2f0fb43c08d613Yang Li        return mClassifier.classify(mSequenceType, mOrientationStyle, instance.vector);
1400a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1410a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1420a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
1430a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Add a gesture for the entry
1440a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     *
1450a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param entryName entry name
1460a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param gesture
1470a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
1480a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void addGesture(String entryName, Gesture gesture) {
1490a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        if (entryName == null || entryName.length() == 0) {
1500a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            return;
1510a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        }
1520a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
1530a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        if (gestures == null) {
1540a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            gestures = new ArrayList<Gesture>();
1550a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            mNamedGestures.put(entryName, gestures);
1560a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        }
1570a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        gestures.add(gesture);
1580a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mClassifier.addInstance(
1590a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName));
1600a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mChanged = true;
1610a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1620a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1630a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
1640a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Remove a gesture from the library. If there are no more gestures for the
1650a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * given entry, the gesture entry will be removed.
1660a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     *
1670a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param entryName entry name
1680a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param gesture
1690a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
1700a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void removeGesture(String entryName, Gesture gesture) {
1710a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
1720a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        if (gestures == null) {
1730a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            return;
1740a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        }
1750a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1760a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        gestures.remove(gesture);
1770a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1780a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        // if there are no more samples, remove the entry automatically
1790a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        if (gestures.isEmpty()) {
1800a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            mNamedGestures.remove(entryName);
1810a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        }
1820a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1830a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mClassifier.removeInstance(gesture.getID());
1840a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1850a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mChanged = true;
1860a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1870a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1880a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
1890a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Remove a entry of gestures
1900a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     *
1910a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param entryName the entry name
1920a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
1930a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void removeEntry(String entryName) {
1940a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mNamedGestures.remove(entryName);
1950a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mClassifier.removeInstances(entryName);
1960a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        mChanged = true;
1970a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
1980a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
1990a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
2000a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Get all the gestures of an entry
2010a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     *
2020a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @param entryName
2030a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * @return the list of gestures that is under this name
2040a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
2050a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public ArrayList<Gesture> getGestures(String entryName) {
2060a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
2070a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        if (gestures != null) {
2080a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            return new ArrayList<Gesture>(gestures);
2090a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        } else {
2100a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            return null;
2110a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        }
2120a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
2130a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
21403f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy    public boolean hasChanged() {
21503f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy        return mChanged;
21603f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy    }
21703f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy
2180a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
2190a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Save the gesture library
2200a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
2210a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void save(OutputStream stream) throws IOException {
2220a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        save(stream, false);
2230a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
2240a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2250a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void save(OutputStream stream, boolean closeStream) throws IOException {
2260a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        DataOutputStream out = null;
2270a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2280a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        try {
2290a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            long start;
2300a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            if (PROFILE_LOADING_SAVING) {
2310a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                start = SystemClock.elapsedRealtime();
2320a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            }
2330a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2340a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            final HashMap<String, ArrayList<Gesture>> maps = mNamedGestures;
2350a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2367fe416e9436a7b2a00e27e73ceb725de4e763f30Romain Guy            out = new DataOutputStream((stream instanceof BufferedOutputStream) ? stream :
2377fe416e9436a7b2a00e27e73ceb725de4e763f30Romain Guy                    new BufferedOutputStream(stream, GestureConstants.IO_BUFFER_SIZE));
2380a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            // Write version number
2390a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            out.writeShort(FILE_FORMAT_VERSION);
2400a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            // Write number of entries
2410a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            out.writeInt(maps.size());
2420a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2430a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            for (Map.Entry<String, ArrayList<Gesture>> entry : maps.entrySet()) {
2440a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                final String key = entry.getKey();
2450a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                final ArrayList<Gesture> examples = entry.getValue();
2460a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                final int count = examples.size();
2470a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2480a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                // Write entry name
2490a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                out.writeUTF(key);
2500a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                // Write number of examples for this entry
2510a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                out.writeInt(count);
2520a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2530a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                for (int i = 0; i < count; i++) {
2540a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                    examples.get(i).serialize(out);
2550a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                }
2560a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            }
2570a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2580a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            out.flush();
2590a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2600a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            if (PROFILE_LOADING_SAVING) {
2610a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                long end = SystemClock.elapsedRealtime();
2620a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                Log.d(LOG_TAG, "Saving gestures library = " + (end - start) + " ms");
2630a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            }
2640a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2650a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            mChanged = false;
2660a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        } finally {
26746c53129c6f27c9193ab195a69cb50591b8c1fa2Romain Guy            if (closeStream) GestureUtils.closeStream(out);
2680a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        }
2690a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
2700a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2710a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    /**
2720a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     * Load the gesture library
2730a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy     */
2740a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void load(InputStream stream) throws IOException {
2750a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        load(stream, false);
2760a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
2770a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2780a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    public void load(InputStream stream, boolean closeStream) throws IOException {
2790a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        DataInputStream in = null;
2800a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        try {
2810a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            in = new DataInputStream((stream instanceof BufferedInputStream) ? stream :
2820a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                    new BufferedInputStream(stream, GestureConstants.IO_BUFFER_SIZE));
2830a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2840a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            long start;
2850a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            if (PROFILE_LOADING_SAVING) {
2860a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                start = SystemClock.elapsedRealtime();
2870a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            }
2880a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2890a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            // Read file format version number
2900a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            final short versionNumber = in.readShort();
2910a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            switch (versionNumber) {
2920a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                case 1:
2930a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                    readFormatV1(in);
2940a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                    break;
2950a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            }
2960a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
2970a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            if (PROFILE_LOADING_SAVING) {
2980a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                long end = SystemClock.elapsedRealtime();
2990a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms");
3000a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            }
3010a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        } finally {
30246c53129c6f27c9193ab195a69cb50591b8c1fa2Romain Guy            if (closeStream) GestureUtils.closeStream(in);
3030a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        }
3040a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
3050a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
3060a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    private void readFormatV1(DataInputStream in) throws IOException {
3070a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        final Learner classifier = mClassifier;
3080a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        final HashMap<String, ArrayList<Gesture>> namedGestures = mNamedGestures;
3090a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        namedGestures.clear();
3100a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
3110a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        // Number of entries in the library
3120a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        final int entriesCount = in.readInt();
3130a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
3140a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        for (int i = 0; i < entriesCount; i++) {
3150a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            // Entry name
3160a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            final String name = in.readUTF();
3170a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            // Number of gestures
3180a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            final int gestureCount = in.readInt();
3190a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
3200a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            final ArrayList<Gesture> gestures = new ArrayList<Gesture>(gestureCount);
3210a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            for (int j = 0; j < gestureCount; j++) {
3220a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                final Gesture gesture = Gesture.deserialize(in);
3230a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                gestures.add(gesture);
3240a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                classifier.addInstance(
3250a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy                        Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name));
3260a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            }
3270a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
3280a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy            namedGestures.put(name, gestures);
3290a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        }
3300a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
3310a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy
3320a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    Learner getLearner() {
3330a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy        return mClassifier;
3340a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy    }
3350a63716ed0e44f7cd32b81a444429318d42d8f08Romain Guy}
336