• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     public static final int ORIENTATION_SENSITIVE = 2;
69 
70     private static final short FILE_FORMAT_VERSION = 1;
71 
72     private static final boolean PROFILE_LOADING_SAVING = false;
73 
74     private int mSequenceType = SEQUENCE_SENSITIVE;
75     private int mOrientationStyle = ORIENTATION_SENSITIVE;
76 
77     private final HashMap<String, ArrayList<Gesture>> mNamedGestures =
78             new HashMap<String, ArrayList<Gesture>>();
79 
80     private Learner mClassifier;
81 
82     private boolean mChanged = false;
83 
GestureStore()84     public GestureStore() {
85         mClassifier = new InstanceLearner();
86     }
87 
88     /**
89      * Specify how the gesture library will handle orientation.
90      * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE
91      *
92      * @param style
93      */
setOrientationStyle(int style)94     public void setOrientationStyle(int style) {
95         mOrientationStyle = style;
96     }
97 
getOrientationStyle()98     public int getOrientationStyle() {
99         return mOrientationStyle;
100     }
101 
102     /**
103      * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
104      */
setSequenceType(int type)105     public void setSequenceType(int type) {
106         mSequenceType = type;
107     }
108 
109     /**
110      * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
111      */
getSequenceType()112     public int getSequenceType() {
113         return mSequenceType;
114     }
115 
116     /**
117      * Get all the gesture entry names in the library
118      *
119      * @return a set of strings
120      */
getGestureEntries()121     public Set<String> getGestureEntries() {
122         return mNamedGestures.keySet();
123     }
124 
125     /**
126      * Recognize a gesture
127      *
128      * @param gesture the query
129      * @return a list of predictions of possible entries for a given gesture
130      */
recognize(Gesture gesture)131     public ArrayList<Prediction> recognize(Gesture gesture) {
132         Instance instance = Instance.createInstance(mSequenceType,
133                 mOrientationStyle, gesture, null);
134         return mClassifier.classify(mSequenceType, instance.vector);
135     }
136 
137     /**
138      * Add a gesture for the entry
139      *
140      * @param entryName entry name
141      * @param gesture
142      */
addGesture(String entryName, Gesture gesture)143     public void addGesture(String entryName, Gesture gesture) {
144         if (entryName == null || entryName.length() == 0) {
145             return;
146         }
147         ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
148         if (gestures == null) {
149             gestures = new ArrayList<Gesture>();
150             mNamedGestures.put(entryName, gestures);
151         }
152         gestures.add(gesture);
153         mClassifier.addInstance(
154                 Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName));
155         mChanged = true;
156     }
157 
158     /**
159      * Remove a gesture from the library. If there are no more gestures for the
160      * given entry, the gesture entry will be removed.
161      *
162      * @param entryName entry name
163      * @param gesture
164      */
removeGesture(String entryName, Gesture gesture)165     public void removeGesture(String entryName, Gesture gesture) {
166         ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
167         if (gestures == null) {
168             return;
169         }
170 
171         gestures.remove(gesture);
172 
173         // if there are no more samples, remove the entry automatically
174         if (gestures.isEmpty()) {
175             mNamedGestures.remove(entryName);
176         }
177 
178         mClassifier.removeInstance(gesture.getID());
179 
180         mChanged = true;
181     }
182 
183     /**
184      * Remove a entry of gestures
185      *
186      * @param entryName the entry name
187      */
removeEntry(String entryName)188     public void removeEntry(String entryName) {
189         mNamedGestures.remove(entryName);
190         mClassifier.removeInstances(entryName);
191         mChanged = true;
192     }
193 
194     /**
195      * Get all the gestures of an entry
196      *
197      * @param entryName
198      * @return the list of gestures that is under this name
199      */
getGestures(String entryName)200     public ArrayList<Gesture> getGestures(String entryName) {
201         ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
202         if (gestures != null) {
203             return new ArrayList<Gesture>(gestures);
204         } else {
205             return null;
206         }
207     }
208 
hasChanged()209     public boolean hasChanged() {
210         return mChanged;
211     }
212 
213     /**
214      * Save the gesture library
215      */
save(OutputStream stream)216     public void save(OutputStream stream) throws IOException {
217         save(stream, false);
218     }
219 
save(OutputStream stream, boolean closeStream)220     public void save(OutputStream stream, boolean closeStream) throws IOException {
221         DataOutputStream out = null;
222 
223         try {
224             long start;
225             if (PROFILE_LOADING_SAVING) {
226                 start = SystemClock.elapsedRealtime();
227             }
228 
229             final HashMap<String, ArrayList<Gesture>> maps = mNamedGestures;
230 
231             out = new DataOutputStream((stream instanceof BufferedOutputStream) ? stream :
232                     new BufferedOutputStream(stream, GestureConstants.IO_BUFFER_SIZE));
233             // Write version number
234             out.writeShort(FILE_FORMAT_VERSION);
235             // Write number of entries
236             out.writeInt(maps.size());
237 
238             for (Map.Entry<String, ArrayList<Gesture>> entry : maps.entrySet()) {
239                 final String key = entry.getKey();
240                 final ArrayList<Gesture> examples = entry.getValue();
241                 final int count = examples.size();
242 
243                 // Write entry name
244                 out.writeUTF(key);
245                 // Write number of examples for this entry
246                 out.writeInt(count);
247 
248                 for (int i = 0; i < count; i++) {
249                     examples.get(i).serialize(out);
250                 }
251             }
252 
253             out.flush();
254 
255             if (PROFILE_LOADING_SAVING) {
256                 long end = SystemClock.elapsedRealtime();
257                 Log.d(LOG_TAG, "Saving gestures library = " + (end - start) + " ms");
258             }
259 
260             mChanged = false;
261         } finally {
262             if (closeStream) GestureUtilities.closeStream(out);
263         }
264     }
265 
266     /**
267      * Load the gesture library
268      */
load(InputStream stream)269     public void load(InputStream stream) throws IOException {
270         load(stream, false);
271     }
272 
load(InputStream stream, boolean closeStream)273     public void load(InputStream stream, boolean closeStream) throws IOException {
274         DataInputStream in = null;
275         try {
276             in = new DataInputStream((stream instanceof BufferedInputStream) ? stream :
277                     new BufferedInputStream(stream, GestureConstants.IO_BUFFER_SIZE));
278 
279             long start;
280             if (PROFILE_LOADING_SAVING) {
281                 start = SystemClock.elapsedRealtime();
282             }
283 
284             // Read file format version number
285             final short versionNumber = in.readShort();
286             switch (versionNumber) {
287                 case 1:
288                     readFormatV1(in);
289                     break;
290             }
291 
292             if (PROFILE_LOADING_SAVING) {
293                 long end = SystemClock.elapsedRealtime();
294                 Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms");
295             }
296         } finally {
297             if (closeStream) GestureUtilities.closeStream(in);
298         }
299     }
300 
readFormatV1(DataInputStream in)301     private void readFormatV1(DataInputStream in) throws IOException {
302         final Learner classifier = mClassifier;
303         final HashMap<String, ArrayList<Gesture>> namedGestures = mNamedGestures;
304         namedGestures.clear();
305 
306         // Number of entries in the library
307         final int entriesCount = in.readInt();
308 
309         for (int i = 0; i < entriesCount; i++) {
310             // Entry name
311             final String name = in.readUTF();
312             // Number of gestures
313             final int gestureCount = in.readInt();
314 
315             final ArrayList<Gesture> gestures = new ArrayList<Gesture>(gestureCount);
316             for (int j = 0; j < gestureCount; j++) {
317                 final Gesture gesture = Gesture.deserialize(in);
318                 gestures.add(gesture);
319                 classifier.addInstance(
320                         Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name));
321             }
322 
323             namedGestures.put(name, gestures);
324         }
325     }
326 
getLearner()327     Learner getLearner() {
328         return mClassifier;
329     }
330 }
331