Modify the base gestures API to use streams instead of files. Adds new wrappers to easily load/save gestures from files, resources, etc. Do the same for the letters recognizer.
diff --git a/core/java/android/gesture/GestureLibraries.java b/core/java/android/gesture/GestureLibraries.java
new file mode 100644
index 0000000..2ce7a8e
--- /dev/null
+++ b/core/java/android/gesture/GestureLibraries.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 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 static android.gesture.GestureConstants.*;
+import android.content.Context;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+
+public final class GestureLibraries {
+ private GestureLibraries() {
+ }
+
+ public static GestureLibrary fromFile(String path) {
+ return fromFile(new File(path));
+ }
+
+ public static GestureLibrary fromFile(File path) {
+ return new FileGestureLibrary(path);
+ }
+
+ public static GestureLibrary fromPrivateFile(Context context, String name) {
+ return fromFile(context.getFileStreamPath(name));
+ }
+
+ public static GestureLibrary fromRawResource(Context context, int resourceId) {
+ return new ResourceGestureLibrary(context, resourceId);
+ }
+
+ private static class FileGestureLibrary extends GestureLibrary {
+ private final File mPath;
+
+ public FileGestureLibrary(File path) {
+ mPath = path;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return !mPath.canWrite();
+ }
+
+ public boolean save() {
+ final File file = mPath;
+ if (!file.canWrite()) return false;
+
+ if (!file.getParentFile().exists()) {
+ if (!file.getParentFile().mkdirs()) {
+ return false;
+ }
+ }
+
+ boolean result = false;
+ try {
+ mStore.save(new FileOutputStream(file), true);
+ result = true;
+ } catch (FileNotFoundException e) {
+ Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
+ }
+
+ return result;
+ }
+
+ public boolean load() {
+ boolean result = false;
+ final File file = mPath;
+ if (file.exists() && file.canRead()) {
+ try {
+ mStore.load(new FileInputStream(file), true);
+ result = true;
+ } catch (FileNotFoundException e) {
+ Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
+ }
+ }
+
+ return result;
+ }
+ }
+
+ private static class ResourceGestureLibrary extends GestureLibrary {
+ private final WeakReference<Context> mContext;
+ private final int mResourceId;
+
+ public ResourceGestureLibrary(Context context, int resourceId) {
+ mContext = new WeakReference<Context>(context);
+ mResourceId = resourceId;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return true;
+ }
+
+ public boolean save() {
+ return false;
+ }
+
+ public boolean load() {
+ boolean result = false;
+ final Context context = mContext.get();
+ if (context != null) {
+ final InputStream in = context.getResources().openRawResource(mResourceId);
+ try {
+ mStore.load(in, true);
+ result = true;
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Could not load the gesture library from raw resource " +
+ context.getResources().getResourceName(mResourceId), e);
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/core/java/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java
index 9020d2b..a29c2c8 100644
--- a/core/java/android/gesture/GestureLibrary.java
+++ b/core/java/android/gesture/GestureLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,337 +14,68 @@
* 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.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.DataOutputStream;
-import java.io.DataInputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Set;
-import java.util.Map;
+import java.util.ArrayList;
-import static android.gesture.GestureConstants.LOG_TAG;
+public abstract class GestureLibrary {
+ protected final GestureStore mStore;
-/**
- * GestureLibrary maintains gesture examples and makes predictions on a new
- * gesture
- */
-//
-// File format for GestureLibrary:
-//
-// 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 GestureLibrary {
- 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;
- public static final int ORIENTATION_SENSITIVE = 2;
-
- 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 String mGestureFileName;
-
- private final HashMap<String, ArrayList<Gesture>> mNamedGestures =
- new HashMap<String, ArrayList<Gesture>>();
-
- private Learner mClassifier;
-
- private boolean mChanged = false;
-
- /**
- * @param path where gesture data is stored
- */
- public GestureLibrary(String path) {
- mGestureFileName = path;
- mClassifier = new InstanceLearner();
+ protected GestureLibrary() {
+ mStore = new GestureStore();
}
- /**
- * Specify how the gesture library will handle orientation.
- * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE
- *
- * @param style
- */
+ public abstract boolean save();
+
+ public abstract boolean load();
+
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ public Learner getLearner() {
+ return mStore.getLearner();
+ }
+
public void setOrientationStyle(int style) {
- mOrientationStyle = style;
+ mStore.setOrientationStyle(style);
}
public int getOrientationStyle() {
- return mOrientationStyle;
+ return mStore.getOrientationStyle();
}
- /**
- * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
- */
public void setSequenceType(int type) {
- mSequenceType = type;
+ mStore.setSequenceType(type);
}
- /**
- * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
- */
public int getSequenceType() {
- return mSequenceType;
+ return mStore.getSequenceType();
}
- /**
- * Get all the gesture entry names in the library
- *
- * @return a set of strings
- */
public Set<String> getGestureEntries() {
- return mNamedGestures.keySet();
+ return mStore.getGestureEntries();
}
- /**
- * 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, instance.vector);
+ return mStore.recognize(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;
+ mStore.addGesture(entryName, 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;
+ mStore.removeGesture(entryName, gesture);
}
- /**
- * Remove a entry of gestures
- *
- * @param entryName the entry name
- */
public void removeEntry(String entryName) {
- mNamedGestures.remove(entryName);
- mClassifier.removeInstances(entryName);
- mChanged = true;
+ mStore.removeEntry(entryName);
}
- /**
- * 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;
- }
- }
-
- /**
- * Save the gesture library
- */
- public boolean save() {
- if (!mChanged) {
- return true;
- }
-
- boolean result = false;
- DataOutputStream out = null;
-
- try {
- File file = new File(mGestureFileName);
- if (!file.getParentFile().exists()) {
- if (!file.getParentFile().mkdirs()) {
- return false;
- }
- }
-
- long start;
- if (PROFILE_LOADING_SAVING) {
- start = SystemClock.elapsedRealtime();
- }
-
- final HashMap<String, ArrayList<Gesture>> maps = mNamedGestures;
-
- out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file),
- 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;
- result = true;
- } catch (IOException ex) {
- Log.d(LOG_TAG, "Failed to save gestures:", ex);
- } finally {
- GestureUtilities.closeStream(out);
- }
-
- return result;
- }
-
- /**
- * Load the gesture library
- */
- public boolean load() {
- boolean result = false;
-
- final File file = new File(mGestureFileName);
- if (file.exists()) {
- DataInputStream in = null;
- try {
- in = new DataInputStream(new BufferedInputStream(
- new FileInputStream(mGestureFileName), 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");
- }
-
- result = true;
- } catch (IOException ex) {
- Log.d(LOG_TAG, "Failed to load gestures:", ex);
- } finally {
- GestureUtilities.closeStream(in);
- }
- }
-
- return result;
- }
-
- 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;
+ return mStore.getGestures(entryName);
}
}
diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java
new file mode 100644
index 0000000..ddf1c83
--- /dev/null
+++ b/core/java/android/gesture/GestureStore.java
@@ -0,0 +1,330 @@
+/*
+ * 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
+ */
+//
+// 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;
+ public static final int ORIENTATION_SENSITIVE = 2;
+
+ 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
+ *
+ * @param style
+ */
+ public void setOrientationStyle(int style) {
+ mOrientationStyle = style;
+ }
+
+ public int getOrientationStyle() {
+ return mOrientationStyle;
+ }
+
+ /**
+ * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
+ */
+ public void setSequenceType(int type) {
+ mSequenceType = type;
+ }
+
+ /**
+ * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
+ */
+ public int getSequenceType() {
+ return mSequenceType;
+ }
+
+ /**
+ * Get all the gesture entry names in the library
+ *
+ * @return a set of strings
+ */
+ public Set<String> getGestureEntries() {
+ return mNamedGestures.keySet();
+ }
+
+ /**
+ * 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, instance.vector);
+ }
+
+ /**
+ * 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.
+ *
+ * @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
+ *
+ * @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
+ *
+ * @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;
+ }
+ }
+
+ /**
+ * Save the gesture library
+ */
+ public void save(OutputStream stream) throws IOException {
+ save(stream, false);
+ }
+
+ public void save(OutputStream stream, boolean closeStream) throws IOException {
+ if (!mChanged) {
+ return;
+ }
+
+ 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) ? out :
+ new BufferedOutputStream(out, 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) GestureUtilities.closeStream(out);
+ }
+ }
+
+ /**
+ * 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) GestureUtilities.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;
+ }
+}
diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java
index 9ac0a96..ef208ac 100755
--- a/core/java/android/gesture/Instance.java
+++ b/core/java/android/gesture/Instance.java
@@ -72,7 +72,7 @@
static Instance createInstance(int sequenceType, int orientationType, Gesture gesture, String label) {
float[] pts;
Instance instance;
- if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) {
+ if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) {
pts = temporalSampler(orientationType, gesture);
instance = new Instance(gesture.getID(), pts, label);
instance.normalize();
@@ -94,7 +94,7 @@
float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]);
float adjustment = -orientation;
- if (orientationType == GestureLibrary.ORIENTATION_SENSITIVE) {
+ if (orientationType == GestureStore.ORIENTATION_SENSITIVE) {
int count = ORIENTATIONS.length;
for (int i = 0; i < count; i++) {
float delta = ORIENTATIONS[i] - orientation;
diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java
index b6feb64..00cdadc 100644
--- a/core/java/android/gesture/InstanceLearner.java
+++ b/core/java/android/gesture/InstanceLearner.java
@@ -38,7 +38,7 @@
continue;
}
double distance;
- if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) {
+ if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) {
distance = GestureUtilities.cosineDistance(sample.vector, vector);
} else {
distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);
diff --git a/core/java/android/gesture/LetterRecognizer.java b/core/java/android/gesture/LetterRecognizer.java
index 9e801ed..580fc26 100644
--- a/core/java/android/gesture/LetterRecognizer.java
+++ b/core/java/android/gesture/LetterRecognizer.java
@@ -23,6 +23,7 @@
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -31,10 +32,9 @@
import static android.gesture.GestureConstants.LOG_TAG;
public class LetterRecognizer {
- public final static int RECOGNIZER_LATIN_LOWERCASE = 0;
static final String GESTURE_FILE_NAME = "letters.gestures";
- private final static int ADJUST_RANGE = 3;
+ private final static int ADJUST_RANGE = 3;
private SigmoidUnit[] mHiddenLayer;
private SigmoidUnit[] mOutputLayer;
@@ -42,8 +42,8 @@
private final String[] mClasses;
private final int mPatchSize;
-
- private GestureLibrary mGestureLibrary;
+
+ private GestureLibrary mGestureStore;
private final Comparator<Prediction> mComparator = new PredictionComparator();
@@ -69,15 +69,6 @@
}
}
- public static LetterRecognizer getLetterRecognizer(Context context, int type) {
- switch (type) {
- case RECOGNIZER_LATIN_LOWERCASE: {
- return createFromResource(context, com.android.internal.R.raw.latin_lowercase);
- }
- }
- return null;
- }
-
private LetterRecognizer(int numOfInput, int numOfHidden, String[] classes) {
mPatchSize = (int) Math.sqrt(numOfInput);
mHiddenLayer = new SigmoidUnit[numOfHidden];
@@ -137,14 +128,18 @@
return output;
}
- private static LetterRecognizer createFromResource(Context context, int resourceID) {
+ static LetterRecognizer createFromResource(Context context, int resourceID) {
final Resources resources = context.getResources();
+ final InputStream stream = resources.openRawResource(resourceID);
+ return createFromStream(stream);
+ }
+ static LetterRecognizer createFromStream(InputStream stream) {
DataInputStream in = null;
LetterRecognizer classifier = null;
try {
- in = new DataInputStream(new BufferedInputStream(resources.openRawResource(resourceID),
+ in = new DataInputStream(new BufferedInputStream(stream,
GestureConstants.IO_BUFFER_SIZE));
final int version = in.readShort();
@@ -206,49 +201,49 @@
}
/**
- * TODO: Publish this API once we figure out where we should save the personzlied
+ * TODO: Publish this API once we figure out where we should save the personalized
* gestures, and how to do so across all apps
*
* @hide
*/
public boolean save() {
- if (mGestureLibrary != null) {
- return mGestureLibrary.save();
+ if (mGestureStore != null) {
+ return mGestureStore.save();
}
return false;
}
/**
- * TODO: Publish this API once we figure out where we should save the personzlied
+ * TODO: Publish this API once we figure out where we should save the personalized
* gestures, and how to do so across all apps
*
* @hide
*/
public void setPersonalizationEnabled(boolean enabled) {
if (enabled) {
- mGestureLibrary = new GestureLibrary(GESTURE_FILE_NAME);
- mGestureLibrary.setSequenceType(GestureLibrary.SEQUENCE_INVARIANT);
- mGestureLibrary.load();
+ mGestureStore = GestureLibraries.fromFile(GESTURE_FILE_NAME);
+ mGestureStore.setSequenceType(GestureStore.SEQUENCE_INVARIANT);
+ mGestureStore.load();
} else {
- mGestureLibrary = null;
+ mGestureStore = null;
}
}
/**
- * TODO: Publish this API once we figure out where we should save the personzlied
+ * TODO: Publish this API once we figure out where we should save the personalized
* gestures, and how to do so across all apps
*
* @hide
*/
public void addExample(String letter, Gesture example) {
- if (mGestureLibrary != null) {
- mGestureLibrary.addGesture(letter, example);
+ if (mGestureStore != null) {
+ mGestureStore.addGesture(letter, example);
}
}
-
+
private void adjustPrediction(Gesture query, ArrayList<Prediction> predictions) {
- if (mGestureLibrary != null) {
- final ArrayList<Prediction> results = mGestureLibrary.recognize(query);
+ if (mGestureStore != null) {
+ final ArrayList<Prediction> results = mGestureStore.recognize(query);
final HashMap<String, Prediction> topNList = new HashMap<String, Prediction>();
for (int j = 0; j < ADJUST_RANGE; j++) {
diff --git a/core/java/android/gesture/LetterRecognizers.java b/core/java/android/gesture/LetterRecognizers.java
new file mode 100644
index 0000000..e3f45a0
--- /dev/null
+++ b/core/java/android/gesture/LetterRecognizers.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 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.content.Context;
+import android.util.Log;
+import static android.gesture.GestureConstants.LOG_TAG;
+import static android.gesture.LetterRecognizer.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+public final class LetterRecognizers {
+ public final static int RECOGNIZER_LATIN_LOWERCASE = 0;
+
+ private LetterRecognizers() {
+ }
+
+ public static LetterRecognizer fromType(Context context, int type) {
+ switch (type) {
+ case RECOGNIZER_LATIN_LOWERCASE: {
+ return createFromResource(context, com.android.internal.R.raw.latin_lowercase);
+ }
+ }
+ return null;
+ }
+
+ public static LetterRecognizer fromResource(Context context, int resourceId) {
+ return createFromResource(context, resourceId);
+ }
+
+ public static LetterRecognizer fromFile(String path) {
+ return fromFile(new File(path));
+ }
+
+ public static LetterRecognizer fromFile(File file) {
+ try {
+ return createFromStream(new FileInputStream(file));
+ } catch (FileNotFoundException e) {
+ Log.d(LOG_TAG, "Failed to load handwriting data from file " + file, e);
+ }
+ return null;
+ }
+
+ public static LetterRecognizer fromStream(InputStream stream) {
+ return createFromStream(stream);
+ }
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f60aba3..ec02143 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -52,6 +52,7 @@
import android.gesture.Gesture;
import android.gesture.LetterRecognizer;
import android.gesture.Prediction;
+import android.gesture.LetterRecognizers;
import com.android.internal.R;
@@ -94,7 +95,7 @@
public static final int TRANSCRIPT_MODE_NORMAL = 1;
/**
* The list will automatically scroll to the bottom, no matter what items
- * are currently visible.
+ * are currently visible.
*
* @see #setTranscriptMode(int)
*/
@@ -156,7 +157,7 @@
* Indicates the view is in the process of being flung
*/
static final int TOUCH_MODE_FLING = 4;
-
+
/**
* Indicates that the user is currently dragging the fast scroll thumb
*/
@@ -349,7 +350,7 @@
* bitmap cache after scrolling.
*/
boolean mScrollingCacheEnabled;
-
+
/**
* Whether or not to enable the fast scroll feature on this list
*/
@@ -422,7 +423,7 @@
* The last CheckForTap runnable we posted, if any
*/
private Runnable mPendingCheckForTap;
-
+
/**
* The last CheckForKeyLongPress runnable we posted, if any
*/
@@ -576,7 +577,7 @@
int color = a.getColor(R.styleable.AbsListView_cacheColorHint, 0);
setCacheColorHint(color);
-
+
boolean enableFastScroll = a.getBoolean(R.styleable.AbsListView_fastScrollEnabled, false);
setFastScrollEnabled(enableFastScroll);
@@ -605,7 +606,7 @@
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
mDensityScale = getContext().getResources().getDisplayMetrics().density;
}
-
+
/**
* <p>Sets the type of gestures to use with this list. When gestures are enabled,
* that is if the <code>gestures</code> parameter is not {@link #GESTURES_NONE},
@@ -663,7 +664,7 @@
* @see #GESTURES_NONE
* @see #GESTURES_JUMP
* @see #GESTURES_FILTER
- * @see #setGestures(int)
+ * @see #setGestures(int)
*/
@ViewDebug.ExportedProperty(mapping = {
@ViewDebug.IntToString(from = GESTURES_NONE, to = "NONE"),
@@ -737,7 +738,7 @@
mGesturesOverlay.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);
mGesturesOverlay.addOnGesturePerformedListener(new GesturesProcessor());
- mPreviousGesturing = false;
+ mPreviousGesturing = false;
}
}
@@ -778,10 +779,10 @@
}
/**
- * Enables fast scrolling by letting the user quickly scroll through lists by
- * dragging the fast scroll thumb. The adapter attached to the list may want
+ * Enables fast scrolling by letting the user quickly scroll through lists by
+ * dragging the fast scroll thumb. The adapter attached to the list may want
* to implement {@link SectionIndexer} if it wishes to display alphabet preview and
- * jump between sections of the list.
+ * jump between sections of the list.
* @see SectionIndexer
* @see #isFastScrollEnabled()
* @param enabled whether or not to enable fast scrolling
@@ -799,7 +800,7 @@
}
}
}
-
+
/**
* Returns the current state of the fast scroll feature.
* @see #setFastScrollEnabled(boolean)
@@ -809,10 +810,10 @@
public boolean isFastScrollEnabled() {
return mFastScrollEnabled;
}
-
+
/**
* If fast scroll is visible, then don't draw the vertical scrollbar.
- * @hide
+ * @hide
*/
@Override
protected boolean isVerticalScrollBarHidden() {
@@ -830,11 +831,11 @@
* When smooth scrollbar is disabled, the position and size of the scrollbar thumb
* is based solely on the number of items in the adapter and the position of the
* visible items inside the adapter. This provides a stable scrollbar as the user
- * navigates through a list of items with varying heights.
+ * navigates through a list of items with varying heights.
*
* @param enabled Whether or not to enable smooth scrollbar.
*
- * @see #setSmoothScrollbarEnabled(boolean)
+ * @see #setSmoothScrollbarEnabled(boolean)
* @attr ref android.R.styleable#AbsListView_smoothScrollbar
*/
public void setSmoothScrollbarEnabled(boolean enabled) {
@@ -1142,7 +1143,7 @@
/**
* Sets the initial value for the text filter.
* @param filterText The text to use for the filter.
- *
+ *
* @see #setTextFilterEnabled
*/
public void setFilterText(String filterText) {
@@ -1168,7 +1169,7 @@
}
/**
- * Returns the list's text filter, if available.
+ * Returns the list's text filter, if available.
* @return the list's text filter or null if filtering isn't enabled
*/
public CharSequence getTextFilter() {
@@ -1177,7 +1178,7 @@
}
return null;
}
-
+
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
@@ -1740,7 +1741,7 @@
System.arraycopy(state, enabledPos + 1, state, enabledPos,
state.length - enabledPos - 1);
}
-
+
return state;
}
@@ -1851,16 +1852,16 @@
*/
private class WindowRunnnable {
private int mOriginalAttachCount;
-
+
public void rememberWindowAttachCount() {
mOriginalAttachCount = getWindowAttachCount();
}
-
+
public boolean sameWindow() {
return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount;
}
}
-
+
private class PerformClick extends WindowRunnnable implements Runnable {
View mChild;
int mClickMotionPosition;
@@ -1887,7 +1888,7 @@
final long longPressId = mAdapter.getItemId(mMotionPosition);
boolean handled = false;
- if (sameWindow() && !mDataChanged) {
+ if (sameWindow() && !mDataChanged) {
handled = performLongPress(child, longPressPosition, longPressId);
}
if (handled) {
@@ -1901,7 +1902,7 @@
}
}
}
-
+
private class CheckForKeyLongPress extends WindowRunnnable implements Runnable {
public void run() {
if (isPressed() && mSelectedPosition >= 0) {
@@ -1930,7 +1931,7 @@
boolean handled = false;
dismissGesturesPopup();
-
+
if (mOnItemLongClickListener != null) {
handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, child,
longPressPosition, longPressId);
@@ -2079,7 +2080,7 @@
mTouchMode = TOUCH_MODE_DONE_WAITING;
}
} else {
- mTouchMode = TOUCH_MODE_DONE_WAITING;
+ mTouchMode = TOUCH_MODE_DONE_WAITING;
}
}
}
@@ -2138,7 +2139,7 @@
boolean intercepted = mFastScroller.onTouchEvent(ev);
if (intercepted) {
return true;
- }
+ }
}
final int action = ev.getAction();
@@ -2326,10 +2327,10 @@
}
setPressed(false);
-
+
// Need to redraw since we probably aren't drawing the selector anymore
invalidate();
-
+
final Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mPendingCheckForLongPress);
@@ -2373,7 +2374,7 @@
return true;
}
-
+
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
@@ -2388,14 +2389,14 @@
int x = (int) ev.getX();
int y = (int) ev.getY();
View v;
-
+
if (mFastScroller != null) {
boolean intercepted = mFastScroller.onInterceptTouchEvent(ev);
if (intercepted) {
return true;
}
}
-
+
switch (action) {
case MotionEvent.ACTION_DOWN: {
int motionPosition = findMotionRow(y);
@@ -3245,7 +3246,7 @@
}
return null;
}
-
+
/**
* For filtering we proxy an input connection to an internal text editor,
* and this allows the proxying to happen.
@@ -3254,7 +3255,7 @@
public boolean checkInputConnectionProxy(View view) {
return view == mTextFilter;
}
-
+
/**
* Creates the window for the text filter and populates it with an EditText field;
*
@@ -3647,7 +3648,7 @@
mCurrentScrap = scrapViews[0];
mScrapViews = scrapViews;
}
-
+
public boolean shouldRecycleViewType(int viewType) {
return viewType >= 0;
}
@@ -3862,8 +3863,8 @@
private final char[] mHolder;
GesturesProcessor() {
- mRecognizer = LetterRecognizer.getLetterRecognizer(getContext(),
- LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE);
+ mRecognizer = LetterRecognizers.fromType(getContext(),
+ LetterRecognizers.RECOGNIZER_LATIN_LOWERCASE);
if (mRecognizer == null) {
setGestures(GESTURES_NONE);
}