GestureStore.java revision 0a63716ed0e44f7cd32b81a444429318d42d8f08
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 17package android.gesture; 18 19import android.util.Log; 20import android.os.SystemClock; 21 22import java.io.BufferedInputStream; 23import java.io.BufferedOutputStream; 24import java.io.IOException; 25import java.io.DataOutputStream; 26import java.io.DataInputStream; 27import java.io.InputStream; 28import java.io.OutputStream; 29import java.util.ArrayList; 30import java.util.HashMap; 31import java.util.Set; 32import java.util.Map; 33 34import 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// 61public 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 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 */ 94 public void setOrientationStyle(int style) { 95 mOrientationStyle = style; 96 } 97 98 public int getOrientationStyle() { 99 return mOrientationStyle; 100 } 101 102 /** 103 * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE 104 */ 105 public void setSequenceType(int type) { 106 mSequenceType = type; 107 } 108 109 /** 110 * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE 111 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 209 /** 210 * Save the gesture library 211 */ 212 public void save(OutputStream stream) throws IOException { 213 save(stream, false); 214 } 215 216 public void save(OutputStream stream, boolean closeStream) throws IOException { 217 if (!mChanged) { 218 return; 219 } 220 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) ? out : 232 new BufferedOutputStream(out, 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 */ 269 public void load(InputStream stream) throws IOException { 270 load(stream, false); 271 } 272 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 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 327 Learner getLearner() { 328 return mClassifier; 329 } 330} 331