Merge change 2746 into donut
* changes: 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.
This commit is contained in:
139
core/java/android/gesture/GestureLibraries.java
Normal file
139
core/java/android/gesture/GestureLibraries.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,337 +14,68 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package android.gesture;
|
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.Set;
|
||||||
import java.util.Map;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import static android.gesture.GestureConstants.LOG_TAG;
|
public abstract class GestureLibrary {
|
||||||
|
protected final GestureStore mStore;
|
||||||
|
|
||||||
/**
|
protected GestureLibrary() {
|
||||||
* GestureLibrary maintains gesture examples and makes predictions on a new
|
mStore = new GestureStore();
|
||||||
* gesture
|
}
|
||||||
*/
|
|
||||||
//
|
public abstract boolean save();
|
||||||
// File format for GestureLibrary:
|
|
||||||
//
|
public abstract boolean load();
|
||||||
// Nb. bytes Java type Description
|
|
||||||
// -----------------------------------
|
public boolean isReadOnly() {
|
||||||
// Header
|
return false;
|
||||||
// 2 bytes short File format version number
|
}
|
||||||
// 4 bytes int Number of entries
|
|
||||||
// Entry
|
public Learner getLearner() {
|
||||||
// X bytes UTF String Entry name
|
return mStore.getLearner();
|
||||||
// 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify how the gesture library will handle orientation.
|
|
||||||
* Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE
|
|
||||||
*
|
|
||||||
* @param style
|
|
||||||
*/
|
|
||||||
public void setOrientationStyle(int style) {
|
public void setOrientationStyle(int style) {
|
||||||
mOrientationStyle = style;
|
mStore.setOrientationStyle(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOrientationStyle() {
|
public int getOrientationStyle() {
|
||||||
return mOrientationStyle;
|
return mStore.getOrientationStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
|
|
||||||
*/
|
|
||||||
public void setSequenceType(int type) {
|
public void setSequenceType(int type) {
|
||||||
mSequenceType = type;
|
mStore.setSequenceType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE
|
|
||||||
*/
|
|
||||||
public int getSequenceType() {
|
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() {
|
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) {
|
public ArrayList<Prediction> recognize(Gesture gesture) {
|
||||||
Instance instance = Instance.createInstance(mSequenceType, mOrientationStyle, gesture, null);
|
return mStore.recognize(gesture);
|
||||||
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) {
|
public void addGesture(String entryName, Gesture gesture) {
|
||||||
if (entryName == null || entryName.length() == 0) {
|
mStore.addGesture(entryName, gesture);
|
||||||
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) {
|
public void removeGesture(String entryName, Gesture gesture) {
|
||||||
ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
|
mStore.removeGesture(entryName, gesture);
|
||||||
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) {
|
public void removeEntry(String entryName) {
|
||||||
mNamedGestures.remove(entryName);
|
mStore.removeEntry(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) {
|
public ArrayList<Gesture> getGestures(String entryName) {
|
||||||
ArrayList<Gesture> gestures = mNamedGestures.get(entryName);
|
return mStore.getGestures(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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
330
core/java/android/gesture/GestureStore.java
Normal file
330
core/java/android/gesture/GestureStore.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -72,7 +72,7 @@ class Instance {
|
|||||||
static Instance createInstance(int sequenceType, int orientationType, Gesture gesture, String label) {
|
static Instance createInstance(int sequenceType, int orientationType, Gesture gesture, String label) {
|
||||||
float[] pts;
|
float[] pts;
|
||||||
Instance instance;
|
Instance instance;
|
||||||
if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) {
|
if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) {
|
||||||
pts = temporalSampler(orientationType, gesture);
|
pts = temporalSampler(orientationType, gesture);
|
||||||
instance = new Instance(gesture.getID(), pts, label);
|
instance = new Instance(gesture.getID(), pts, label);
|
||||||
instance.normalize();
|
instance.normalize();
|
||||||
@@ -94,7 +94,7 @@ class Instance {
|
|||||||
float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]);
|
float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]);
|
||||||
|
|
||||||
float adjustment = -orientation;
|
float adjustment = -orientation;
|
||||||
if (orientationType == GestureLibrary.ORIENTATION_SENSITIVE) {
|
if (orientationType == GestureStore.ORIENTATION_SENSITIVE) {
|
||||||
int count = ORIENTATIONS.length;
|
int count = ORIENTATIONS.length;
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
float delta = ORIENTATIONS[i] - orientation;
|
float delta = ORIENTATIONS[i] - orientation;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class InstanceLearner extends Learner {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
double distance;
|
double distance;
|
||||||
if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) {
|
if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) {
|
||||||
distance = GestureUtilities.cosineDistance(sample.vector, vector);
|
distance = GestureUtilities.cosineDistance(sample.vector, vector);
|
||||||
} else {
|
} else {
|
||||||
distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);
|
distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.util.Log;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -31,7 +32,6 @@ import java.util.HashMap;
|
|||||||
import static android.gesture.GestureConstants.LOG_TAG;
|
import static android.gesture.GestureConstants.LOG_TAG;
|
||||||
|
|
||||||
public class LetterRecognizer {
|
public class LetterRecognizer {
|
||||||
public final static int RECOGNIZER_LATIN_LOWERCASE = 0;
|
|
||||||
static final String GESTURE_FILE_NAME = "letters.gestures";
|
static final String GESTURE_FILE_NAME = "letters.gestures";
|
||||||
|
|
||||||
private final static int ADJUST_RANGE = 3;
|
private final static int ADJUST_RANGE = 3;
|
||||||
@@ -43,7 +43,7 @@ public class LetterRecognizer {
|
|||||||
|
|
||||||
private final int mPatchSize;
|
private final int mPatchSize;
|
||||||
|
|
||||||
private GestureLibrary mGestureLibrary;
|
private GestureLibrary mGestureStore;
|
||||||
|
|
||||||
private final Comparator<Prediction> mComparator = new PredictionComparator();
|
private final Comparator<Prediction> mComparator = new PredictionComparator();
|
||||||
|
|
||||||
@@ -69,15 +69,6 @@ public class LetterRecognizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
private LetterRecognizer(int numOfInput, int numOfHidden, String[] classes) {
|
||||||
mPatchSize = (int) Math.sqrt(numOfInput);
|
mPatchSize = (int) Math.sqrt(numOfInput);
|
||||||
mHiddenLayer = new SigmoidUnit[numOfHidden];
|
mHiddenLayer = new SigmoidUnit[numOfHidden];
|
||||||
@@ -137,14 +128,18 @@ public class LetterRecognizer {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LetterRecognizer createFromResource(Context context, int resourceID) {
|
static LetterRecognizer createFromResource(Context context, int resourceID) {
|
||||||
final Resources resources = context.getResources();
|
final Resources resources = context.getResources();
|
||||||
|
final InputStream stream = resources.openRawResource(resourceID);
|
||||||
|
return createFromStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LetterRecognizer createFromStream(InputStream stream) {
|
||||||
DataInputStream in = null;
|
DataInputStream in = null;
|
||||||
LetterRecognizer classifier = null;
|
LetterRecognizer classifier = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
in = new DataInputStream(new BufferedInputStream(resources.openRawResource(resourceID),
|
in = new DataInputStream(new BufferedInputStream(stream,
|
||||||
GestureConstants.IO_BUFFER_SIZE));
|
GestureConstants.IO_BUFFER_SIZE));
|
||||||
|
|
||||||
final int version = in.readShort();
|
final int version = in.readShort();
|
||||||
@@ -206,49 +201,49 @@ public class LetterRecognizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* gestures, and how to do so across all apps
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public boolean save() {
|
public boolean save() {
|
||||||
if (mGestureLibrary != null) {
|
if (mGestureStore != null) {
|
||||||
return mGestureLibrary.save();
|
return mGestureStore.save();
|
||||||
}
|
}
|
||||||
return false;
|
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
|
* gestures, and how to do so across all apps
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public void setPersonalizationEnabled(boolean enabled) {
|
public void setPersonalizationEnabled(boolean enabled) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
mGestureLibrary = new GestureLibrary(GESTURE_FILE_NAME);
|
mGestureStore = GestureLibraries.fromFile(GESTURE_FILE_NAME);
|
||||||
mGestureLibrary.setSequenceType(GestureLibrary.SEQUENCE_INVARIANT);
|
mGestureStore.setSequenceType(GestureStore.SEQUENCE_INVARIANT);
|
||||||
mGestureLibrary.load();
|
mGestureStore.load();
|
||||||
} else {
|
} 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
|
* gestures, and how to do so across all apps
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public void addExample(String letter, Gesture example) {
|
public void addExample(String letter, Gesture example) {
|
||||||
if (mGestureLibrary != null) {
|
if (mGestureStore != null) {
|
||||||
mGestureLibrary.addGesture(letter, example);
|
mGestureStore.addGesture(letter, example);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustPrediction(Gesture query, ArrayList<Prediction> predictions) {
|
private void adjustPrediction(Gesture query, ArrayList<Prediction> predictions) {
|
||||||
if (mGestureLibrary != null) {
|
if (mGestureStore != null) {
|
||||||
final ArrayList<Prediction> results = mGestureLibrary.recognize(query);
|
final ArrayList<Prediction> results = mGestureStore.recognize(query);
|
||||||
final HashMap<String, Prediction> topNList = new HashMap<String, Prediction>();
|
final HashMap<String, Prediction> topNList = new HashMap<String, Prediction>();
|
||||||
|
|
||||||
for (int j = 0; j < ADJUST_RANGE; j++) {
|
for (int j = 0; j < ADJUST_RANGE; j++) {
|
||||||
|
|||||||
65
core/java/android/gesture/LetterRecognizers.java
Normal file
65
core/java/android/gesture/LetterRecognizers.java
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ import android.gesture.GestureOverlayView;
|
|||||||
import android.gesture.Gesture;
|
import android.gesture.Gesture;
|
||||||
import android.gesture.LetterRecognizer;
|
import android.gesture.LetterRecognizer;
|
||||||
import android.gesture.Prediction;
|
import android.gesture.Prediction;
|
||||||
|
import android.gesture.LetterRecognizers;
|
||||||
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
|
|
||||||
@@ -3862,8 +3863,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
|||||||
private final char[] mHolder;
|
private final char[] mHolder;
|
||||||
|
|
||||||
GesturesProcessor() {
|
GesturesProcessor() {
|
||||||
mRecognizer = LetterRecognizer.getLetterRecognizer(getContext(),
|
mRecognizer = LetterRecognizers.fromType(getContext(),
|
||||||
LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE);
|
LetterRecognizers.RECOGNIZER_LATIN_LOWERCASE);
|
||||||
if (mRecognizer == null) {
|
if (mRecognizer == null) {
|
||||||
setGestures(GESTURES_NONE);
|
setGestures(GESTURES_NONE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import android.gesture.Gesture;
|
|||||||
import android.gesture.GestureOverlayView;
|
import android.gesture.GestureOverlayView;
|
||||||
import android.gesture.LetterRecognizer;
|
import android.gesture.LetterRecognizer;
|
||||||
import android.gesture.Prediction;
|
import android.gesture.Prediction;
|
||||||
|
import android.gesture.LetterRecognizers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -57,8 +58,8 @@ public class ContactListGestureOverlay extends Activity {
|
|||||||
setContentView(R.layout.overlaydemo);
|
setContentView(R.layout.overlaydemo);
|
||||||
|
|
||||||
// create a letter recognizer
|
// create a letter recognizer
|
||||||
mRecognizer = LetterRecognizer.getLetterRecognizer(this,
|
mRecognizer = LetterRecognizers.fromType(this,
|
||||||
LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE);
|
LetterRecognizers.RECOGNIZER_LATIN_LOWERCASE);
|
||||||
mOverlay = (GestureOverlayView) findViewById(R.id.overlay);
|
mOverlay = (GestureOverlayView) findViewById(R.id.overlay);
|
||||||
|
|
||||||
// load the contact list
|
// load the contact list
|
||||||
|
|||||||
@@ -36,9 +36,10 @@ import android.widget.Spinner;
|
|||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
import android.gesture.Gesture;
|
import android.gesture.Gesture;
|
||||||
|
|
||||||
import android.gesture.GestureLibrary;
|
|
||||||
import android.gesture.GestureOverlayView;
|
import android.gesture.GestureOverlayView;
|
||||||
import android.gesture.Prediction;
|
import android.gesture.Prediction;
|
||||||
|
import android.gesture.GestureLibraries;
|
||||||
|
import android.gesture.GestureLibrary;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -61,7 +62,7 @@ public class GestureEntry extends Activity {
|
|||||||
|
|
||||||
private Spinner mRecognitionResult;
|
private Spinner mRecognitionResult;
|
||||||
|
|
||||||
private GestureLibrary mGestureLibrary;
|
private GestureLibrary mGestureStore;
|
||||||
|
|
||||||
private boolean mChangedByRecognizer = false;
|
private boolean mChangedByRecognizer = false;
|
||||||
|
|
||||||
@@ -71,8 +72,8 @@ public class GestureEntry extends Activity {
|
|||||||
setContentView(R.layout.demo);
|
setContentView(R.layout.demo);
|
||||||
|
|
||||||
// init the gesture library
|
// init the gesture library
|
||||||
mGestureLibrary = new GestureLibrary(GESTURE_FILE_NAME);
|
mGestureStore = GestureLibraries.fromFile(GESTURE_FILE_NAME);
|
||||||
mGestureLibrary.load();
|
mGestureStore.load();
|
||||||
|
|
||||||
// create the spinner for showing the recognition results
|
// create the spinner for showing the recognition results
|
||||||
// the spinner also allows a user to correct a prediction
|
// the spinner also allows a user to correct a prediction
|
||||||
@@ -82,7 +83,7 @@ public class GestureEntry extends Activity {
|
|||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
// correct the recognition result by adding the new example
|
// correct the recognition result by adding the new example
|
||||||
if (!mChangedByRecognizer) {
|
if (!mChangedByRecognizer) {
|
||||||
mGestureLibrary.addGesture(parent.getSelectedItem().toString(), mGesturePad
|
mGestureStore.addGesture(parent.getSelectedItem().toString(), mGesturePad
|
||||||
.getGesture());
|
.getGesture());
|
||||||
} else {
|
} else {
|
||||||
mChangedByRecognizer = false;
|
mChangedByRecognizer = false;
|
||||||
@@ -134,7 +135,7 @@ public class GestureEntry extends Activity {
|
|||||||
.findViewById(R.id.gesturename_edit);
|
.findViewById(R.id.gesturename_edit);
|
||||||
String text = edittext.getText().toString().trim();
|
String text = edittext.getText().toString().trim();
|
||||||
if (text.length() > 0) {
|
if (text.length() > 0) {
|
||||||
mGestureLibrary.addGesture(text, mGesturePad.getGesture());
|
mGestureStore.addGesture(text, mGesturePad.getGesture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).setNegativeButton(R.string.newgesture_dialog_cancel,
|
}).setNegativeButton(R.string.newgesture_dialog_cancel,
|
||||||
@@ -173,14 +174,14 @@ public class GestureEntry extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
mGestureLibrary.load();
|
mGestureStore.load();
|
||||||
mGesturePad.clear(false);
|
mGesturePad.clear(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
mGestureLibrary.save();
|
mGestureStore.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -195,12 +196,12 @@ public class GestureEntry extends Activity {
|
|||||||
if (gesture != null) {
|
if (gesture != null) {
|
||||||
outState.putParcelable(PARCEL_KEY, gesture);
|
outState.putParcelable(PARCEL_KEY, gesture);
|
||||||
}
|
}
|
||||||
mGestureLibrary.save();
|
mGestureStore.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recognize(Gesture gesture) {
|
private void recognize(Gesture gesture) {
|
||||||
mChangedByRecognizer = true;
|
mChangedByRecognizer = true;
|
||||||
ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture);
|
ArrayList<Prediction> predictions = mGestureStore.recognize(gesture);
|
||||||
ArrayAdapter<Prediction> adapter = new ArrayAdapter<Prediction>(this,
|
ArrayAdapter<Prediction> adapter = new ArrayAdapter<Prediction>(this,
|
||||||
android.R.layout.simple_spinner_item, predictions);
|
android.R.layout.simple_spinner_item, predictions);
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ import android.widget.Spinner;
|
|||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
import android.gesture.Gesture;
|
import android.gesture.Gesture;
|
||||||
|
|
||||||
import android.gesture.GestureLibrary;
|
|
||||||
import android.gesture.GestureOverlayView;
|
import android.gesture.GestureOverlayView;
|
||||||
|
import android.gesture.GestureLibraries;
|
||||||
|
import android.gesture.GestureLibrary;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -45,7 +46,7 @@ public class GestureLibViewer extends Activity {
|
|||||||
|
|
||||||
private Spinner mGestureCategory;
|
private Spinner mGestureCategory;
|
||||||
|
|
||||||
private GestureLibrary mGesureLibrary;
|
private GestureLibrary mGesureStore;
|
||||||
|
|
||||||
private ArrayList<Gesture> mGestures;
|
private ArrayList<Gesture> mGestures;
|
||||||
|
|
||||||
@@ -59,15 +60,15 @@ public class GestureLibViewer extends Activity {
|
|||||||
|
|
||||||
String name = (String) mGestureCategory.getSelectedItem();
|
String name = (String) mGestureCategory.getSelectedItem();
|
||||||
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
||||||
mGesureLibrary.removeGesture(name, gesture);
|
mGesureStore.removeGesture(name, gesture);
|
||||||
|
|
||||||
mGestures = mGesureLibrary.getGestures(name);
|
mGestures = mGesureStore.getGestures(name);
|
||||||
|
|
||||||
if (mGestures == null) {
|
if (mGestures == null) {
|
||||||
// delete the entire entry
|
// delete the entire entry
|
||||||
mCurrentGestureIndex = 0;
|
mCurrentGestureIndex = 0;
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
list.addAll(mGesureLibrary.getGestureEntries());
|
list.addAll(mGesureStore.getGestureEntries());
|
||||||
Collections.sort(list);
|
Collections.sort(list);
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(GestureLibViewer.this,
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(GestureLibViewer.this,
|
||||||
android.R.layout.simple_spinner_item, list);
|
android.R.layout.simple_spinner_item, list);
|
||||||
@@ -94,19 +95,19 @@ public class GestureLibViewer extends Activity {
|
|||||||
mGesturePad.setEnabled(false);
|
mGesturePad.setEnabled(false);
|
||||||
|
|
||||||
// init the gesture library
|
// init the gesture library
|
||||||
mGesureLibrary = new GestureLibrary(GestureEntry.GESTURE_FILE_NAME);
|
mGesureStore = GestureLibraries.fromFile(GestureEntry.GESTURE_FILE_NAME);
|
||||||
mGesureLibrary.load();
|
mGesureStore.load();
|
||||||
|
|
||||||
mGestureCategory = (Spinner) findViewById(R.id.spinner);
|
mGestureCategory = (Spinner) findViewById(R.id.spinner);
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
if (!mGesureLibrary.getGestureEntries().isEmpty()) {
|
if (!mGesureStore.getGestureEntries().isEmpty()) {
|
||||||
list.addAll(mGesureLibrary.getGestureEntries());
|
list.addAll(mGesureStore.getGestureEntries());
|
||||||
Collections.sort(list);
|
Collections.sort(list);
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
|
||||||
android.R.layout.simple_spinner_item, list);
|
android.R.layout.simple_spinner_item, list);
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
mGestureCategory.setAdapter(adapter);
|
mGestureCategory.setAdapter(adapter);
|
||||||
mGestures = mGesureLibrary.getGestures(list.get(0));
|
mGestures = mGesureStore.getGestures(list.get(0));
|
||||||
mCurrentGestureIndex = 0;
|
mCurrentGestureIndex = 0;
|
||||||
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
||||||
mGesturePad.setGesture(gesture);
|
mGesturePad.setGesture(gesture);
|
||||||
@@ -114,7 +115,7 @@ public class GestureLibViewer extends Activity {
|
|||||||
|
|
||||||
mGestureCategory.setOnItemSelectedListener(new OnItemSelectedListener() {
|
mGestureCategory.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
mGestures = mGesureLibrary.getGestures((String) mGestureCategory.getSelectedItem());
|
mGestures = mGesureStore.getGestures((String) mGestureCategory.getSelectedItem());
|
||||||
if (!mGestures.isEmpty()) {
|
if (!mGestures.isEmpty()) {
|
||||||
mCurrentGestureIndex = 0;
|
mCurrentGestureIndex = 0;
|
||||||
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
Gesture gesture = mGestures.get(mCurrentGestureIndex);
|
||||||
@@ -160,7 +161,7 @@ public class GestureLibViewer extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
mGesureLibrary.save();
|
mGesureStore.save();
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
finish();
|
finish();
|
||||||
return true;
|
return true;
|
||||||
@@ -172,12 +173,12 @@ public class GestureLibViewer extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
mGesureLibrary.save();
|
mGesureStore.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
mGesureLibrary.save();
|
mGesureStore.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user