/*
 * 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 android.gesture;

import android.util.Log;
import android.os.SystemClock;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.DataOutputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.Map;

import static android.gesture.GestureConstants.LOG_TAG;

GestureLibrary maintains gesture examples and makes predictions on a new gesture
/** * GestureLibrary maintains gesture examples and makes predictions on a new * gesture */
// // File format for GestureStore: // // Nb. bytes Java type Description // ----------------------------------- // Header // 2 bytes short File format version number // 4 bytes int Number of entries // Entry // X bytes UTF String Entry name // 4 bytes int Number of gestures // Gesture // 8 bytes long Gesture ID // 4 bytes int Number of strokes // Stroke // 4 bytes int Number of points // Point // 4 bytes float X coordinate of the point // 4 bytes float Y coordinate of the point // 8 bytes long Time stamp // public class GestureStore { public static final int SEQUENCE_INVARIANT = 1; // when SEQUENCE_SENSITIVE is used, only single stroke gestures are currently allowed public static final int SEQUENCE_SENSITIVE = 2; // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures public static final int ORIENTATION_INVARIANT = 1; // at most 2 directions can be recognized public static final int ORIENTATION_SENSITIVE = 2; // at most 4 directions can be recognized static final int ORIENTATION_SENSITIVE_4 = 4; // at most 8 directions can be recognized static final int ORIENTATION_SENSITIVE_8 = 8; private static final short FILE_FORMAT_VERSION = 1; private static final boolean PROFILE_LOADING_SAVING = false; private int mSequenceType = SEQUENCE_SENSITIVE; private int mOrientationStyle = ORIENTATION_SENSITIVE; private final HashMap<String, ArrayList<Gesture>> mNamedGestures = new HashMap<String, ArrayList<Gesture>>(); private Learner mClassifier; private boolean mChanged = false; public GestureStore() { mClassifier = new InstanceLearner(); }
Specify how the gesture library will handle orientation. Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE
Params:
  • style –
/** * Specify how the gesture library will handle orientation. * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE * * @param style */
public void setOrientationStyle(int style) { mOrientationStyle = style; } public int getOrientationStyle() { return mOrientationStyle; }
Params:
  • type – SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
/** * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE */
public void setSequenceType(int type) { mSequenceType = type; }
Returns:SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
/** * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE */
public int getSequenceType() { return mSequenceType; }
Get all the gesture entry names in the library
Returns:a set of strings
/** * Get all the gesture entry names in the library * * @return a set of strings */
public Set<String> getGestureEntries() { return mNamedGestures.keySet(); }
Recognize a gesture
Params:
  • gesture – the query
Returns:a list of predictions of possible entries for a given gesture
/** * 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(mSequenceType, mOrientationStyle, gesture, null); return mClassifier.classify(mSequenceType, mOrientationStyle, instance.vector); }
Add a gesture for the entry
Params:
  • entryName – entry name
  • gesture –
/** * Add a gesture for the entry * * @param entryName entry name * @param gesture */
public void addGesture(String entryName, Gesture gesture) { if (entryName == null || entryName.length() == 0) { return; } ArrayList<Gesture> gestures = mNamedGestures.get(entryName); if (gestures == null) { gestures = new ArrayList<Gesture>(); mNamedGestures.put(entryName, gestures); } gestures.add(gesture); mClassifier.addInstance( Instance.createInstance(mSequenceType, mOrientationStyle, 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.
Params:
  • entryName – entry name
  • gesture –
/** * 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 = mNamedGestures.get(entryName); if (gestures == null) { return; } gestures.remove(gesture); // if there are no more samples, remove the entry automatically if (gestures.isEmpty()) { mNamedGestures.remove(entryName); } mClassifier.removeInstance(gesture.getID()); mChanged = true; }
Remove a entry of gestures
Params:
  • entryName – the entry name
/** * Remove a entry of gestures * * @param entryName the entry name */
public void removeEntry(String entryName) { mNamedGestures.remove(entryName); mClassifier.removeInstances(entryName); mChanged = true; }
Get all the gestures of an entry
Params:
  • entryName –
Returns:the list of gestures that is under this name
/** * Get all the gestures of an entry * * @param entryName * @return the list of gestures that is under this name */
public ArrayList<Gesture> getGestures(String entryName) { ArrayList<Gesture> gestures = mNamedGestures.get(entryName); if (gestures != null) { return new ArrayList<Gesture>(gestures); } else { return null; } } public boolean hasChanged() { return mChanged; }
Save the gesture library
/** * Save the gesture library */
public void save(OutputStream stream) throws IOException { save(stream, false); } public void save(OutputStream stream, boolean closeStream) throws IOException { DataOutputStream out = null; try { long start; if (PROFILE_LOADING_SAVING) { start = SystemClock.elapsedRealtime(); } final HashMap<String, ArrayList<Gesture>> maps = mNamedGestures; out = new DataOutputStream((stream instanceof BufferedOutputStream) ? stream : new BufferedOutputStream(stream, GestureConstants.IO_BUFFER_SIZE)); // Write version number out.writeShort(FILE_FORMAT_VERSION); // Write number of entries out.writeInt(maps.size()); for (Map.Entry<String, ArrayList<Gesture>> entry : maps.entrySet()) { final String key = entry.getKey(); final ArrayList<Gesture> examples = entry.getValue(); final int count = examples.size(); // Write entry name out.writeUTF(key); // Write number of examples for this entry out.writeInt(count); for (int i = 0; i < count; i++) { examples.get(i).serialize(out); } } out.flush(); if (PROFILE_LOADING_SAVING) { long end = SystemClock.elapsedRealtime(); Log.d(LOG_TAG, "Saving gestures library = " + (end - start) + " ms"); } mChanged = false; } finally { if (closeStream) GestureUtils.closeStream(out); } }
Load the gesture library
/** * Load the gesture library */
public void load(InputStream stream) throws IOException { load(stream, false); } public void load(InputStream stream, boolean closeStream) throws IOException { DataInputStream in = null; try { in = new DataInputStream((stream instanceof BufferedInputStream) ? stream : new BufferedInputStream(stream, GestureConstants.IO_BUFFER_SIZE)); long start; if (PROFILE_LOADING_SAVING) { start = SystemClock.elapsedRealtime(); } // Read file format version number final short versionNumber = in.readShort(); switch (versionNumber) { case 1: readFormatV1(in); break; } if (PROFILE_LOADING_SAVING) { long end = SystemClock.elapsedRealtime(); Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms"); } } finally { if (closeStream) GestureUtils.closeStream(in); } } private void readFormatV1(DataInputStream in) throws IOException { final Learner classifier = mClassifier; final HashMap<String, ArrayList<Gesture>> namedGestures = mNamedGestures; namedGestures.clear(); // Number of entries in the library final int entriesCount = in.readInt(); for (int i = 0; i < entriesCount; i++) { // Entry name final String name = in.readUTF(); // Number of gestures final int gestureCount = in.readInt(); final ArrayList<Gesture> gestures = new ArrayList<Gesture>(gestureCount); for (int j = 0; j < gestureCount; j++) { final Gesture gesture = Gesture.deserialize(in); gestures.add(gesture); classifier.addInstance( Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name)); } namedGestures.put(name, gestures); } } Learner getLearner() { return mClassifier; } }