Home | History | Annotate | Download | only in gesture
      1 /*
      2  * Copyright (C) 2008-2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.gesture;
     18 
     19 import android.util.Log;
     20 import android.os.SystemClock;
     21 
     22 import java.io.BufferedInputStream;
     23 import java.io.BufferedOutputStream;
     24 import java.io.IOException;
     25 import java.io.DataOutputStream;
     26 import java.io.DataInputStream;
     27 import java.io.InputStream;
     28 import java.io.OutputStream;
     29 import java.util.ArrayList;
     30 import java.util.HashMap;
     31 import java.util.Set;
     32 import java.util.Map;
     33 
     34 import static android.gesture.GestureConstants.LOG_TAG;
     35 
     36 /**
     37  * GestureLibrary maintains gesture examples and makes predictions on a new
     38  * gesture
     39  */
     40 //
     41 //    File format for GestureStore:
     42 //
     43 //                Nb. bytes   Java type   Description
     44 //                -----------------------------------
     45 //    Header
     46 //                2 bytes     short       File format version number
     47 //                4 bytes     int         Number of entries
     48 //    Entry
     49 //                X bytes     UTF String  Entry name
     50 //                4 bytes     int         Number of gestures
     51 //    Gesture
     52 //                8 bytes     long        Gesture ID
     53 //                4 bytes     int         Number of strokes
     54 //    Stroke
     55 //                4 bytes     int         Number of points
     56 //    Point
     57 //                4 bytes     float       X coordinate of the point
     58 //                4 bytes     float       Y coordinate of the point
     59 //                8 bytes     long        Time stamp
     60 //
     61 public class GestureStore {
     62     public static final int SEQUENCE_INVARIANT = 1;
     63     // when SEQUENCE_SENSITIVE is used, only single stroke gestures are currently allowed
     64     public static final int SEQUENCE_SENSITIVE = 2;
     65 
     66     // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures
     67     public static final int ORIENTATION_INVARIANT = 1;
     68     // at most 2 directions can be recognized
     69     public static final int ORIENTATION_SENSITIVE = 2;
     70     // at most 4 directions can be recognized
     71     static final int ORIENTATION_SENSITIVE_4 = 4;
     72     // at most 8 directions can be recognized
     73     static final int ORIENTATION_SENSITIVE_8 = 8;
     74 
     75     private static final short FILE_FORMAT_VERSION = 1;
     76 
     77     private static final boolean PROFILE_LOADING_SAVING = false;
     78 
     79     private int mSequenceType = SEQUENCE_SENSITIVE;
     80     private int mOrientationStyle = ORIENTATION_SENSITIVE;
     81 
     82     private final HashMap<String, ArrayList<Gesture>> mNamedGestures =
     83             new HashMap<String, ArrayList<Gesture>>();
     84 
     85     private Learner mClassifier;
     86 
     87     private boolean mChanged = false;
     88 
     89     public GestureStore() {
     90         mClassifier = new InstanceLearner();
     91     }
     92 
     93     /**
     94      * Specify how the gesture library will handle orientation.
     95      * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE
     96      *
     97      * @param style
     98      */
     99     public void setOrientationStyle(int style) {
    100         mOrientationStyle = style;
    101     }
    102 
    103     public int getOrientationStyle() {
    104         return mOrientationStyle;
    105     }
    106 
    107     /**
    108      * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
    109      */
    110     public void setSequenceType(int type) {
    111         mSequenceType = type;
    112     }
    113 
    114     /**
    115      * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
    116      */
    117     public int getSequenceType() {
    118         return mSequenceType;
    119     }
    120 
    121     /**
    122      * Get all the gesture entry names in the library
    123      *
    124      * @return a set of strings
    125      */
    126     public Set<String> getGestureEntries() {
    127         return mNamedGestures.keySet();
    128     }
    129 
    130     /**
    131      * Recognize a gesture
    132      *
    133      * @param gesture the query
    134      * @return a list of predictions of possible entries for a given gesture
    135      */
    136     public ArrayList<Prediction> recognize(Gesture gesture) {
    137         Instance instance = Instance.createInstance(mSequenceType,
    138                 mOrientationStyle, gesture, null);
    139         return mClassifier.classify(mSequenceType, mOrientationStyle, instance.vector);
    140     }
    141 
    142     /**
    143      * Add a gesture for the entry
    144      *
    145      * @param entryName entry name
    146      * @param gesture
    147      */
    148     public void addGesture(String entryName, Gesture gesture) {
    149         if (entryName == null || entryName.length() == 0) {
    150             return;
    151         }
    152         ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
    153         if (gestures == null) {
    154             gestures = new ArrayList<Gesture>();
    155             mNamedGestures.put(entryName, gestures);
    156         }
    157         gestures.add(gesture);
    158         mClassifier.addInstance(
    159                 Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName));
    160         mChanged = true;
    161     }
    162 
    163     /**
    164      * Remove a gesture from the library. If there are no more gestures for the
    165      * given entry, the gesture entry will be removed.
    166      *
    167      * @param entryName entry name
    168      * @param gesture
    169      */
    170     public void removeGesture(String entryName, Gesture gesture) {
    171         ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
    172         if (gestures == null) {
    173             return;
    174         }
    175 
    176         gestures.remove(gesture);
    177 
    178         // if there are no more samples, remove the entry automatically
    179         if (gestures.isEmpty()) {
    180             mNamedGestures.remove(entryName);
    181         }
    182 
    183         mClassifier.removeInstance(gesture.getID());
    184 
    185         mChanged = true;
    186     }
    187 
    188     /**
    189      * Remove a entry of gestures
    190      *
    191      * @param entryName the entry name
    192      */
    193     public void removeEntry(String entryName) {
    194         mNamedGestures.remove(entryName);
    195         mClassifier.removeInstances(entryName);
    196         mChanged = true;
    197     }
    198 
    199     /**
    200      * Get all the gestures of an entry
    201      *
    202      * @param entryName
    203      * @return the list of gestures that is under this name
    204      */
    205     public ArrayList<Gesture> getGestures(String entryName) {
    206         ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
    207         if (gestures != null) {
    208             return new ArrayList<Gesture>(gestures);
    209         } else {
    210             return null;
    211         }
    212     }
    213 
    214     public boolean hasChanged() {
    215         return mChanged;
    216     }
    217 
    218     /**
    219      * Save the gesture library
    220      */
    221     public void save(OutputStream stream) throws IOException {
    222         save(stream, false);
    223     }
    224 
    225     public void save(OutputStream stream, boolean closeStream) throws IOException {
    226         DataOutputStream out = null;
    227 
    228         try {
    229             long start;
    230             if (PROFILE_LOADING_SAVING) {
    231                 start = SystemClock.elapsedRealtime();
    232             }
    233 
    234             final HashMap<String, ArrayList<Gesture>> maps = mNamedGestures;
    235 
    236             out = new DataOutputStream((stream instanceof BufferedOutputStream) ? stream :
    237                     new BufferedOutputStream(stream, GestureConstants.IO_BUFFER_SIZE));
    238             // Write version number
    239             out.writeShort(FILE_FORMAT_VERSION);
    240             // Write number of entries
    241             out.writeInt(maps.size());
    242 
    243             for (Map.Entry<String, ArrayList<Gesture>> entry : maps.entrySet()) {
    244                 final String key = entry.getKey();
    245                 final ArrayList<Gesture> examples = entry.getValue();
    246                 final int count = examples.size();
    247 
    248                 // Write entry name
    249                 out.writeUTF(key);
    250                 // Write number of examples for this entry
    251                 out.writeInt(count);
    252 
    253                 for (int i = 0; i < count; i++) {
    254                     examples.get(i).serialize(out);
    255                 }
    256             }
    257 
    258             out.flush();
    259 
    260             if (PROFILE_LOADING_SAVING) {
    261                 long end = SystemClock.elapsedRealtime();
    262                 Log.d(LOG_TAG, "Saving gestures library = " + (end - start) + " ms");
    263             }
    264 
    265             mChanged = false;
    266         } finally {
    267             if (closeStream) GestureUtils.closeStream(out);
    268         }
    269     }
    270 
    271     /**
    272      * Load the gesture library
    273      */
    274     public void load(InputStream stream) throws IOException {
    275         load(stream, false);
    276     }
    277 
    278     public void load(InputStream stream, boolean closeStream) throws IOException {
    279         DataInputStream in = null;
    280         try {
    281             in = new DataInputStream((stream instanceof BufferedInputStream) ? stream :
    282                     new BufferedInputStream(stream, GestureConstants.IO_BUFFER_SIZE));
    283 
    284             long start;
    285             if (PROFILE_LOADING_SAVING) {
    286                 start = SystemClock.elapsedRealtime();
    287             }
    288 
    289             // Read file format version number
    290             final short versionNumber = in.readShort();
    291             switch (versionNumber) {
    292                 case 1:
    293                     readFormatV1(in);
    294                     break;
    295             }
    296 
    297             if (PROFILE_LOADING_SAVING) {
    298                 long end = SystemClock.elapsedRealtime();
    299                 Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms");
    300             }
    301         } finally {
    302             if (closeStream) GestureUtils.closeStream(in);
    303         }
    304     }
    305 
    306     private void readFormatV1(DataInputStream in) throws IOException {
    307         final Learner classifier = mClassifier;
    308         final HashMap<String, ArrayList<Gesture>> namedGestures = mNamedGestures;
    309         namedGestures.clear();
    310 
    311         // Number of entries in the library
    312         final int entriesCount = in.readInt();
    313 
    314         for (int i = 0; i < entriesCount; i++) {
    315             // Entry name
    316             final String name = in.readUTF();
    317             // Number of gestures
    318             final int gestureCount = in.readInt();
    319 
    320             final ArrayList<Gesture> gestures = new ArrayList<Gesture>(gestureCount);
    321             for (int j = 0; j < gestureCount; j++) {
    322                 final Gesture gesture = Gesture.deserialize(in);
    323                 gestures.add(gesture);
    324                 classifier.addInstance(
    325                         Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name));
    326             }
    327 
    328             namedGestures.put(name, gestures);
    329         }
    330     }
    331 
    332     Learner getLearner() {
    333         return mClassifier;
    334     }
    335 }
    336