From db567c390bd56c05614eaa83c02dbb99f97ad9cc Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Thu, 21 May 2009 16:23:21 -0700 Subject: [PATCH] Move the Gestures API to the framework in android.gesture. --- api/current.xml | 1091 +++++++++++++++++ .../java}/android/gesture/Gesture.java | 28 +- .../android/gesture/GestureConstants.java | 5 +- .../java}/android/gesture/GestureLibrary.java | 6 +- .../android/gesture/GestureOverlayView.java | 65 +- .../java}/android/gesture/GesturePoint.java | 2 +- .../java}/android/gesture/GestureStroke.java | 34 +- .../android/gesture/GestureUtilities.java | 4 +- .../java}/android/gesture/Instance.java | 2 +- .../android/gesture/InstanceLearner.java | 24 +- .../java}/android/gesture/Learner.java | 2 +- .../android/gesture/LetterRecognizer.java | 197 +-- .../android/gesture/OrientedBoundingBox.java | 7 +- .../java}/android/gesture/Prediction.java | 2 +- .../gesture/TouchThroughGestureListener.java | 80 +- core/res/res/raw/latin_lowercase | Bin 36186 -> 28494 bytes tests/sketch/AndroidManifest.xml | 4 +- .../gesture/GestureActionListener.java | 21 - .../com/android/gesture/GestureListener.java | 30 - .../example/ContactListGestureOverlay.java | 27 +- .../android/gesture/example/GestureEntry.java | 21 +- .../gesture/example/GestureLibViewer.java | 10 +- tests/sketch/tools/Converter.java | 10 +- 23 files changed, 1369 insertions(+), 303 deletions(-) rename {tests/sketch/src/com => core/java}/android/gesture/Gesture.java (91%) rename {tests/sketch/src/com => core/java}/android/gesture/GestureConstants.java (86%) rename {tests/sketch/src/com => core/java}/android/gesture/GestureLibrary.java (98%) rename tests/sketch/src/com/android/gesture/GestureOverlay.java => core/java/android/gesture/GestureOverlayView.java (85%) rename {tests/sketch/src/com => core/java}/android/gesture/GesturePoint.java (97%) rename {tests/sketch/src/com => core/java}/android/gesture/GestureStroke.java (85%) rename {tests/sketch/src/com => core/java}/android/gesture/GestureUtilities.java (99%) rename {tests/sketch/src/com => core/java}/android/gesture/Instance.java (99%) rename {tests/sketch/src/com => core/java}/android/gesture/InstanceLearner.java (78%) rename {tests/sketch/src/com => core/java}/android/gesture/Learner.java (98%) rename {tests/sketch/src/com => core/java}/android/gesture/LetterRecognizer.java (55%) rename {tests/sketch/src/com => core/java}/android/gesture/OrientedBoundingBox.java (95%) rename {tests/sketch/src/com => core/java}/android/gesture/Prediction.java (96%) rename tests/sketch/src/com/android/gesture/TouchThroughGesturing.java => core/java/android/gesture/TouchThroughGestureListener.java (64%) delete mode 100644 tests/sketch/src/com/android/gesture/GestureActionListener.java delete mode 100755 tests/sketch/src/com/android/gesture/GestureListener.java diff --git a/api/current.xml b/api/current.xml index 5bbf35242924f..085d8b16ef3c5 100644 --- a/api/current.xml +++ b/api/current.xml @@ -46164,6 +46164,1097 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + strokes = mStrokes; - final int count = strokes.size(); - - for (int i = 0; i < count; i++) { - final GestureStroke stroke = strokes.get(i); - str.append(GestureConstants.STRING_GESTURE_DELIIMITER); - str.append(stroke.toString()); - } - - return str.toString(); - } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Gesture createFromParcel(Parcel in) { Gesture gesture = null; @@ -273,7 +251,7 @@ public class Gesture implements Parcelable { try { gesture = deserialize(inStream); } catch (IOException e) { - Log.e(LOG_TAG, "Error reading Gesture from parcel:", e); + Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e); } finally { GestureUtilities.closeStream(inStream); } @@ -302,7 +280,7 @@ public class Gesture implements Parcelable { serialize(outStream); result = true; } catch (IOException e) { - Log.e(LOG_TAG, "Error writing Gesture to parcel:", e); + Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e); } finally { GestureUtilities.closeStream(outStream); GestureUtilities.closeStream(byteStream); diff --git a/tests/sketch/src/com/android/gesture/GestureConstants.java b/core/java/android/gesture/GestureConstants.java similarity index 86% rename from tests/sketch/src/com/android/gesture/GestureConstants.java rename to core/java/android/gesture/GestureConstants.java index c0f2f3d4c1563..230db0c00c501 100644 --- a/tests/sketch/src/com/android/gesture/GestureConstants.java +++ b/core/java/android/gesture/GestureConstants.java @@ -14,12 +14,9 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; interface GestureConstants { - static final String STRING_GESTURE_DELIIMITER = "#"; - static final String STRING_STROKE_DELIIMITER = ","; - static final int STROKE_STRING_BUFFER_SIZE = 1024; static final int STROKE_POINT_BUFFER_SIZE = 100; // number of points diff --git a/tests/sketch/src/com/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java similarity index 98% rename from tests/sketch/src/com/android/gesture/GestureLibrary.java rename to core/java/android/gesture/GestureLibrary.java index 7b5480caf5191..1cf192ec34461 100644 --- a/tests/sketch/src/com/android/gesture/GestureLibrary.java +++ b/core/java/android/gesture/GestureLibrary.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import android.util.Log; import android.os.SystemClock; @@ -32,7 +32,7 @@ import java.util.HashMap; import java.util.Set; import java.util.Map; -import static com.android.gesture.GestureConstants.LOG_TAG; +import static android.gesture.GestureConstants.LOG_TAG; /** * GestureLibrary maintains gesture examples and makes predictions on a new @@ -190,7 +190,7 @@ public class GestureLibrary { * * @param entryName the entry name */ - public void removeEntireEntry(String entryName) { + public void removeEntry(String entryName) { mNamedGestures.remove(entryName); mClassifier.removeInstances(entryName); mChanged = true; diff --git a/tests/sketch/src/com/android/gesture/GestureOverlay.java b/core/java/android/gesture/GestureOverlayView.java similarity index 85% rename from tests/sketch/src/com/android/gesture/GestureOverlay.java rename to core/java/android/gesture/GestureOverlayView.java index 454cecbf2fa2a..bffd12e636f4d 100755 --- a/tests/sketch/src/com/android/gesture/GestureOverlay.java +++ b/core/java/android/gesture/GestureOverlayView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import android.content.Context; import android.graphics.Bitmap; @@ -23,6 +23,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; +import android.graphics.Color; import android.os.Handler; import android.util.AttributeSet; import android.view.MotionEvent; @@ -35,7 +36,7 @@ import java.util.ArrayList; * widgets. The view can also be opaque. */ -public class GestureOverlay extends View { +public class GestureOverlayView extends View { static final float TOUCH_TOLERANCE = 3; // TODO: Move all these values into XML attributes @@ -52,20 +53,22 @@ public class GestureOverlay extends View { private static final boolean DITHER_FLAG = true; public static final int DEFAULT_GESTURE_COLOR = 0xFFFFFF00; + public static final int DEFAULT_UNCERTAIN_GESTURE_COLOR = Color.argb(60, 255, 255, 0); private static final int REFRESH_RANGE = 10; private static final BlurMaskFilter BLUR_MASK_FILTER = new BlurMaskFilter(1, BlurMaskFilter.Blur.NORMAL); - - // double buffering private Paint mGesturePaint; private final Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG); private Bitmap mBitmap; // with transparent background private Canvas mBitmapCanvas; + private int mCertainGestureColor = DEFAULT_GESTURE_COLOR; + private int mUncertainGestureColor = DEFAULT_UNCERTAIN_GESTURE_COLOR; + // for rendering immediate ink feedback private Rect mInvalidRect = new Rect(); @@ -81,7 +84,7 @@ public class GestureOverlay extends View { private Gesture mCurrentGesture = null; // TODO: Make this a list of WeakReferences - private final ArrayList mGestureListeners = new ArrayList(); + private final ArrayList mOnGestureListeners = new ArrayList(); private ArrayList mPointBuffer = null; // fading out effect @@ -107,12 +110,12 @@ public class GestureOverlay extends View { } }; - public GestureOverlay(Context context) { + public GestureOverlayView(Context context) { super(context); init(); } - public GestureOverlay(Context context, AttributeSet attrs) { + public GestureOverlayView(Context context, AttributeSet attrs) { super(context, attrs); init(); } @@ -130,7 +133,7 @@ public class GestureOverlay extends View { * * @param color */ - public void setGestureColor(int color) { + public void setGestureDrawingColor(int color) { mGesturePaint.setColor(color); if (mCurrentGesture != null) { mBitmap.eraseColor(TRANSPARENT_BACKGROUND); @@ -138,6 +141,22 @@ public class GestureOverlay extends View { } } + public void setGestureColor(int color) { + mCertainGestureColor = color; + } + + public void setUncertainGestureColor(int color) { + mUncertainGestureColor = color; + } + + public int getUncertainGestureColor() { + return mUncertainGestureColor; + } + + public int getGestureColor() { + return mCertainGestureColor; + } + /** * Set the gesture to be shown in the view * @@ -199,12 +218,12 @@ public class GestureOverlay extends View { } } - public void addGestureListener(GestureListener listener) { - mGestureListeners.add(listener); + public void addOnGestureListener(OnGestureListener listener) { + mOnGestureListeners.add(listener); } - public void removeGestureListener(GestureListener listener) { - mGestureListeners.remove(listener); + public void removeOnGestureListener(OnGestureListener listener) { + mOnGestureListeners.remove(listener); } @Override @@ -281,11 +300,11 @@ public class GestureOverlay extends View { private Rect touchStart(MotionEvent event) { // pass the event to handlers - final ArrayList listeners = mGestureListeners; + final ArrayList listeners = mOnGestureListeners; final int count = listeners.size(); for (int i = 0; i < count; i++) { - GestureListener listener = listeners.get(i); - listener.onStartGesture(this, event); + OnGestureListener listener = listeners.get(i); + listener.onGestureStarted(this, event); } // if there is fading out going on, stop it. @@ -358,7 +377,7 @@ public class GestureOverlay extends View { mPointBuffer.add(new GesturePoint(x, y, event.getEventTime())); // pass the event to handlers - final ArrayList listeners = mGestureListeners; + final ArrayList listeners = mOnGestureListeners; final int count = listeners.size(); for (int i = 0; i < count; i++) { listeners.get(i).onGesture(this, event); @@ -377,14 +396,24 @@ public class GestureOverlay extends View { mGesturePaint.setMaskFilter(null); // pass the event to handlers - final ArrayList listeners = mGestureListeners; + final ArrayList listeners = mOnGestureListeners; final int count = listeners.size(); for (int i = 0; i < count; i++) { - listeners.get(i).onFinishGesture(this, event); + listeners.get(i).onGestureEnded(this, event); } mPath = null; mPointBuffer = null; } + /** + * An interface for processing gesture events + */ + public static interface OnGestureListener { + public void onGestureStarted(GestureOverlayView overlay, MotionEvent event); + + public void onGesture(GestureOverlayView overlay, MotionEvent event); + + public void onGestureEnded(GestureOverlayView overlay, MotionEvent event); + } } diff --git a/tests/sketch/src/com/android/gesture/GesturePoint.java b/core/java/android/gesture/GesturePoint.java similarity index 97% rename from tests/sketch/src/com/android/gesture/GesturePoint.java rename to core/java/android/gesture/GesturePoint.java index 560f893cf0521..3698011fc7fcd 100644 --- a/tests/sketch/src/com/android/gesture/GesturePoint.java +++ b/core/java/android/gesture/GesturePoint.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import java.io.DataInputStream; import java.io.IOException; diff --git a/tests/sketch/src/com/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java similarity index 85% rename from tests/sketch/src/com/android/gesture/GestureStroke.java rename to core/java/android/gesture/GestureStroke.java index 79b42fe9e786a..5160a76df7d19 100644 --- a/tests/sketch/src/com/android/gesture/GestureStroke.java +++ b/core/java/android/gesture/GestureStroke.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import android.graphics.Canvas; import android.graphics.Matrix; @@ -126,16 +126,16 @@ public class GestureStroke { * @param width the width of the bounding box of the target path * @param height the height of the bounding box of the target path * @param numSample the number of points needed + * * @return the path */ public Path toPath(float width, float height, int numSample) { final float[] pts = GestureUtilities.temporalSampling(this, numSample); final RectF rect = boundingBox; - final float scale = height / rect.height(); final Matrix matrix = new Matrix(); matrix.setTranslate(-rect.left, -rect.top); - matrix.postScale(scale, scale); + matrix.postScale(width / rect.width(), height / rect.height()); matrix.mapPoints(pts); float mX = 0; @@ -156,7 +156,8 @@ public class GestureStroke { } else { float dx = Math.abs(x - mX); float dy = Math.abs(y - mY); - if (dx >= GestureOverlay.TOUCH_TOLERANCE || dy >= GestureOverlay.TOUCH_TOLERANCE) { + if (dx >= GestureOverlayView.TOUCH_TOLERANCE || + dy >= GestureOverlayView.TOUCH_TOLERANCE) { path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); mX = x; mY = y; @@ -198,29 +199,10 @@ public class GestureStroke { } /** - * Convert the stroke to string + * Invalidate the cached path that is used to render the stroke */ - @Override - public String toString() { - final StringBuilder str = new StringBuilder(GestureConstants.STROKE_STRING_BUFFER_SIZE); - final float[] pts = points; - final long[] times = timestamps; - final int count = points.length; - - for (int i = 0; i < count; i += 2) { - str.append(pts[i]).append(GestureConstants.STRING_STROKE_DELIIMITER); - str.append(pts[i + 1]).append(GestureConstants.STRING_STROKE_DELIIMITER); - str.append(times[i / 2]).append(GestureConstants.STRING_STROKE_DELIIMITER); - } - - return str.toString(); - } - - /** - * Invalidate the cached path that is used for rendering the stroke - */ - public void invalidate() { - mCachedPath = null; + public void clearPath() { + if (mCachedPath != null) mCachedPath.rewind(); } /** diff --git a/tests/sketch/src/com/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java similarity index 99% rename from tests/sketch/src/com/android/gesture/GestureUtilities.java rename to core/java/android/gesture/GestureUtilities.java index 6d73c2db7841f..e47856c1a8610 100755 --- a/tests/sketch/src/com/android/gesture/GestureUtilities.java +++ b/core/java/android/gesture/GestureUtilities.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import android.graphics.RectF; import android.graphics.Matrix; @@ -25,7 +25,7 @@ import java.util.Arrays; import java.io.Closeable; import java.io.IOException; -import static com.android.gesture.GestureConstants.*; +import static android.gesture.GestureConstants.*; final class GestureUtilities { private static final int TEMPORAL_SAMPLING_RATE = 16; diff --git a/tests/sketch/src/com/android/gesture/Instance.java b/core/java/android/gesture/Instance.java similarity index 99% rename from tests/sketch/src/com/android/gesture/Instance.java rename to core/java/android/gesture/Instance.java index 502a0fa4ff01c..7922faba3f981 100755 --- a/tests/sketch/src/com/android/gesture/Instance.java +++ b/core/java/android/gesture/Instance.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import android.graphics.Matrix; diff --git a/tests/sketch/src/com/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java similarity index 78% rename from tests/sketch/src/com/android/gesture/InstanceLearner.java rename to core/java/android/gesture/InstanceLearner.java index 4495256eb80ba..1739cdcbe915a 100644 --- a/tests/sketch/src/com/android/gesture/InstanceLearner.java +++ b/core/java/android/gesture/InstanceLearner.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import android.util.Config; import android.util.Log; +import static android.gesture.GestureConstants.*; import java.util.ArrayList; import java.util.Collections; @@ -30,9 +31,6 @@ import java.util.TreeMap; */ class InstanceLearner extends Learner { - - private static final String LOGTAG = "InstanceLearner"; - @Override ArrayList classify(int gestureType, float[] vector) { ArrayList predictions = new ArrayList(); @@ -63,19 +61,15 @@ class InstanceLearner extends Learner { } double sum = 0; - Iterator lableIterator = label2score.keySet().iterator(); - while (lableIterator.hasNext()) { - String name = lableIterator.next(); + for (String name : label2score.keySet()) { double score = label2score.get(name); sum += score; predictions.add(new Prediction(name, score)); } // normalize - Iterator predictionIterator = predictions.iterator(); - while (predictionIterator.hasNext()) { - Prediction name = predictionIterator.next(); - name.score /= sum; + for (Prediction prediction : predictions) { + prediction.score /= sum; } Collections.sort(predictions, new Comparator() { @@ -92,14 +86,6 @@ class InstanceLearner extends Learner { } }); - if (Config.DEBUG) { - predictionIterator = predictions.iterator(); - while (predictionIterator.hasNext()) { - Prediction name = predictionIterator.next(); - Log.v(LOGTAG, "prediction [" + name.name + " = " + name.score + "]"); - } - } - return predictions; } } diff --git a/tests/sketch/src/com/android/gesture/Learner.java b/core/java/android/gesture/Learner.java similarity index 98% rename from tests/sketch/src/com/android/gesture/Learner.java rename to core/java/android/gesture/Learner.java index 15b20536feaba..feacde5f97365 100755 --- a/tests/sketch/src/com/android/gesture/Learner.java +++ b/core/java/android/gesture/Learner.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import java.util.ArrayList; diff --git a/tests/sketch/src/com/android/gesture/LetterRecognizer.java b/core/java/android/gesture/LetterRecognizer.java similarity index 55% rename from tests/sketch/src/com/android/gesture/LetterRecognizer.java rename to core/java/android/gesture/LetterRecognizer.java index 07a5f313c9315..44767469bfb19 100644 --- a/tests/sketch/src/com/android/gesture/LetterRecognizer.java +++ b/core/java/android/gesture/LetterRecognizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import android.content.Context; import android.content.res.Resources; @@ -28,10 +28,13 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import static com.android.gesture.GestureConstants.LOG_TAG; +import static android.gesture.GestureConstants.LOG_TAG; public class LetterRecognizer { - public final static int LATIN_LOWERCASE = 0; + public final static int RECOGNIZER_LATIN_LOWERCASE = 0; + static final String GESTURE_FILE_NAME = "letters.gestures"; + + private final static int ADJUST_RANGE = 3; private SigmoidUnit[] mHiddenLayer; private SigmoidUnit[] mOutputLayer; @@ -40,59 +43,57 @@ public class LetterRecognizer { private final int mPatchSize; - static final String GESTURE_FILE_NAME = "letters.gestures"; + private GestureLibrary mGestureLibrary; - private GestureLibrary mGestureLibrary; - private final static int ADJUST_RANGE = 3; - private static class SigmoidUnit { final float[] mWeights; + private boolean mComputed; + private float mResult; + SigmoidUnit(float[] weights) { mWeights = weights; } private float compute(float[] inputs) { - float sum = 0; + if (!mComputed) { + float sum = 0; - final int count = inputs.length; - final float[] weights = mWeights; + final int count = inputs.length; + final float[] weights = mWeights; - for (int i = 0; i < count; i++) { - sum += inputs[i] * weights[i]; + for (int i = 0; i < count; i++) { + sum += inputs[i] * weights[i]; + } + sum += weights[weights.length - 1]; + + mResult = 1.0f / (float) (1 + Math.exp(-sum)); + mComputed = true; } - sum += weights[weights.length - 1]; - - return 1.0f / (float) (1 + Math.exp(-sum)); + return mResult; } } - private LetterRecognizer(int numOfInput, int numOfHidden, String[] classes) { - mPatchSize = (int)Math.sqrt(numOfInput); - mHiddenLayer = new SigmoidUnit[numOfHidden]; - mClasses = classes; - mOutputLayer = new SigmoidUnit[classes.length]; - } - - public void save() { - mGestureLibrary.save(); - } - public static LetterRecognizer getLetterRecognizer(Context context, int type) { switch (type) { - case LATIN_LOWERCASE: { + 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]; + mClasses = classes; + mOutputLayer = new SigmoidUnit[classes.length]; + } + public ArrayList recognize(Gesture gesture) { float[] query = GestureUtilities.spatialSampling(gesture, mPatchSize); ArrayList predictions = classify(query); - if (mGestureLibrary != null) { - adjustPrediction(gesture, predictions); - } + adjustPrediction(gesture, predictions); return predictions; } @@ -153,38 +154,14 @@ public class LetterRecognizer { in = new DataInputStream(new BufferedInputStream(resources.openRawResource(resourceID), GestureConstants.IO_BUFFER_SIZE)); - final int iCount = in.readInt(); - final int hCount = in.readInt(); - final int oCount = in.readInt(); + final int version = in.readShort(); - final String[] classes = new String[oCount]; - for (int i = 0; i < classes.length; i++) { - classes[i] = in.readUTF(); + switch (version) { + case 1: + classifier = readV1(in); + break; } - classifier = new LetterRecognizer(iCount, hCount, classes); - SigmoidUnit[] hiddenLayer = new SigmoidUnit[hCount]; - SigmoidUnit[] outputLayer = new SigmoidUnit[oCount]; - - for (int i = 0; i < hCount; i++) { - float[] weights = new float[iCount + 1]; - for (int j = 0; j <= iCount; j++) { - weights[j] = in.readFloat(); - } - hiddenLayer[i] = new SigmoidUnit(weights); - } - - for (int i = 0; i < oCount; i++) { - float[] weights = new float[hCount + 1]; - for (int j = 0; j <= hCount; j++) { - weights[j] = in.readFloat(); - } - outputLayer[i] = new SigmoidUnit(weights); - } - - classifier.mHiddenLayer = hiddenLayer; - classifier.mOutputLayer = outputLayer; - } catch (IOException e) { Log.d(LOG_TAG, "Failed to load handwriting data:", e); } finally { @@ -193,9 +170,65 @@ public class LetterRecognizer { return classifier; } - - public void enablePersonalization(boolean enable) { - if (enable) { + + private static LetterRecognizer readV1(DataInputStream in) throws IOException { + + final int iCount = in.readInt(); + final int hCount = in.readInt(); + final int oCount = in.readInt(); + + final String[] classes = new String[oCount]; + for (int i = 0; i < classes.length; i++) { + classes[i] = in.readUTF(); + } + + final LetterRecognizer classifier = new LetterRecognizer(iCount, hCount, classes); + final SigmoidUnit[] hiddenLayer = new SigmoidUnit[hCount]; + final SigmoidUnit[] outputLayer = new SigmoidUnit[oCount]; + + for (int i = 0; i < hCount; i++) { + final float[] weights = new float[iCount + 1]; + for (int j = 0; j <= iCount; j++) { + weights[j] = in.readFloat(); + } + hiddenLayer[i] = new SigmoidUnit(weights); + } + + for (int i = 0; i < oCount; i++) { + final float[] weights = new float[hCount + 1]; + for (int j = 0; j <= hCount; j++) { + weights[j] = in.readFloat(); + } + outputLayer[i] = new SigmoidUnit(weights); + } + + classifier.mHiddenLayer = hiddenLayer; + classifier.mOutputLayer = outputLayer; + + return classifier; + } + + /** + * TODO: Publish this API once we figure out where we should save the personzlied + * gestures, and how to do so across all apps + * + * @hide + */ + public boolean save() { + if (mGestureLibrary != null) { + return mGestureLibrary.save(); + } + return false; + } + + /** + * TODO: Publish this API once we figure out where we should save the personzlied + * 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(); @@ -204,24 +237,36 @@ public class LetterRecognizer { } } + /** + * TODO: Publish this API once we figure out where we should save the personzlied + * gestures, and how to do so across all apps + * + * @hide + */ public void addExample(String letter, Gesture example) { - mGestureLibrary.addGesture(letter, example); + if (mGestureLibrary != null) { + mGestureLibrary.addGesture(letter, example); + } } private void adjustPrediction(Gesture query, ArrayList predictions) { - ArrayList results = mGestureLibrary.recognize(query); - HashMap topNList = new HashMap(); - for (int j = 0; j < ADJUST_RANGE; j++) { - Prediction prediction = predictions.remove(0); - topNList.put(prediction.name, prediction); - } - int count = results.size(); - for (int j = count - 1; j >= 0 && !topNList.isEmpty(); j--) { - Prediction item = results.get(j); - Prediction original = topNList.get(item.name); - if (original != null) { - predictions.add(0, original); - topNList.remove(item.name); + if (mGestureLibrary != null) { + final ArrayList results = mGestureLibrary.recognize(query); + final HashMap topNList = new HashMap(); + + for (int j = 0; j < ADJUST_RANGE; j++) { + Prediction prediction = predictions.remove(0); + topNList.put(prediction.name, prediction); + } + + final int count = results.size(); + for (int j = count - 1; j >= 0 && !topNList.isEmpty(); j--) { + final Prediction item = results.get(j); + final Prediction original = topNList.get(item.name); + if (original != null) { + predictions.add(0, original); + topNList.remove(item.name); + } } } } diff --git a/tests/sketch/src/com/android/gesture/OrientedBoundingBox.java b/core/java/android/gesture/OrientedBoundingBox.java similarity index 95% rename from tests/sketch/src/com/android/gesture/OrientedBoundingBox.java rename to core/java/android/gesture/OrientedBoundingBox.java index a07d125d52559..f1335ee122322 100644 --- a/tests/sketch/src/com/android/gesture/OrientedBoundingBox.java +++ b/core/java/android/gesture/OrientedBoundingBox.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; import android.graphics.Matrix; import android.graphics.Path; @@ -47,6 +47,11 @@ public class OrientedBoundingBox { } } + /** + * Currently used for debugging purpose only. + * + * @hide + */ public Path toPath() { Path path = new Path(); float[] point = new float[2]; diff --git a/tests/sketch/src/com/android/gesture/Prediction.java b/core/java/android/gesture/Prediction.java similarity index 96% rename from tests/sketch/src/com/android/gesture/Prediction.java rename to core/java/android/gesture/Prediction.java index 92d3ba4af604a..ce6ad5745df6c 100755 --- a/tests/sketch/src/com/android/gesture/Prediction.java +++ b/core/java/android/gesture/Prediction.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; public class Prediction { public final String name; diff --git a/tests/sketch/src/com/android/gesture/TouchThroughGesturing.java b/core/java/android/gesture/TouchThroughGestureListener.java similarity index 64% rename from tests/sketch/src/com/android/gesture/TouchThroughGesturing.java rename to core/java/android/gesture/TouchThroughGestureListener.java index fc878c8fd221e..7621ddf2781f2 100644 --- a/tests/sketch/src/com/android/gesture/TouchThroughGesturing.java +++ b/core/java/android/gesture/TouchThroughGestureListener.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.gesture; +package android.gesture; -import android.graphics.Color; import android.view.MotionEvent; import android.view.View; import java.util.ArrayList; +import java.lang.ref.WeakReference; /** * TouchThroughGesturing implements the interaction behavior that allows a user @@ -28,19 +28,15 @@ import java.util.ArrayList; * still allows a user to perform basic interactions (clicking, scrolling and panning) * with the underlying widget. */ - -public class TouchThroughGesturing implements GestureListener { +public class TouchThroughGestureListener implements GestureOverlayView.OnGestureListener { public static final int SINGLE_STROKE = 0; public static final int MULTIPLE_STROKE = 1; + // TODO: Add properties for all these private static final float STROKE_LENGTH_THRESHOLD = 30; private static final float SQUARENESS_THRESHOLD = 0.275f; private static final float ANGLE_THRESHOLD = 40; - private static final boolean STEAL_EVENTS = false; - - public static final int DEFAULT_UNCERTAIN_GESTURE_COLOR = Color.argb(60, 255, 255, 0); - private boolean mIsGesturing = false; private float mTotalLength; @@ -48,18 +44,23 @@ public class TouchThroughGesturing implements GestureListener { private float mX; private float mY; - // TODO: Use WeakReference? - private View mModel; + private WeakReference mModel; private int mGestureType = SINGLE_STROKE; - private int mUncertainGestureColor = DEFAULT_UNCERTAIN_GESTURE_COLOR; // TODO: Use WeakReferences - private final ArrayList mActionListeners = - new ArrayList(); + private final ArrayList mPerformedListeners = + new ArrayList(); - public TouchThroughGesturing(View model) { - mModel = model; + private boolean mStealEvents = false; + + public TouchThroughGestureListener(View model) { + this(model, false); + } + + public TouchThroughGestureListener(View model, boolean stealEvents) { + mModel = new WeakReference(model); + mStealEvents = stealEvents; } /** @@ -70,11 +71,7 @@ public class TouchThroughGesturing implements GestureListener { mGestureType = type; } - public void setUncertainGestureColor(int color) { - mUncertainGestureColor = color; - } - - public void onStartGesture(GestureOverlay overlay, MotionEvent event) { + public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) { if (mGestureType == MULTIPLE_STROKE) { overlay.cancelFadingOut(); } @@ -86,16 +83,21 @@ public class TouchThroughGesturing implements GestureListener { if (mGestureType == SINGLE_STROKE || overlay.getCurrentGesture() == null || overlay.getCurrentGesture().getStrokesCount() == 0) { - overlay.setGestureColor(mUncertainGestureColor); + overlay.setGestureDrawingColor(overlay.getUncertainGestureColor()); } - mModel.dispatchTouchEvent(event); + dispatchEventToModel(event); } - public void onGesture(GestureOverlay overlay, MotionEvent event) { + private void dispatchEventToModel(MotionEvent event) { + View v = mModel.get(); + if (v != null) v.dispatchTouchEvent(event); + } + + public void onGesture(GestureOverlayView overlay, MotionEvent event) { //noinspection PointlessBooleanExpression - if (!STEAL_EVENTS) { - mModel.dispatchTouchEvent(event); + if (!mStealEvents) { + dispatchEventToModel(event); } if (mIsGesturing) { @@ -107,7 +109,7 @@ public class TouchThroughGesturing implements GestureListener { final float dx = x - mX; final float dy = y - mY; - mTotalLength += (float)Math.sqrt(dx * dx + dy * dy); + mTotalLength += (float) Math.sqrt(dx * dx + dy * dy); mX = x; mY = y; @@ -120,8 +122,8 @@ public class TouchThroughGesturing implements GestureListener { } if (box.squareness > SQUARENESS_THRESHOLD || angle < ANGLE_THRESHOLD) { mIsGesturing = true; - overlay.setGestureColor(GestureOverlay.DEFAULT_GESTURE_COLOR); - if (STEAL_EVENTS) { + overlay.setGestureDrawingColor(overlay.getGestureColor()); + if (mStealEvents) { event = MotionEvent.obtain(event.getDownTime(), System.currentTimeMillis(), MotionEvent.ACTION_UP, x, y, event.getPressure(), event.getSize(), event.getMetaState(), event.getXPrecision(), event.getYPrecision(), @@ -130,36 +132,40 @@ public class TouchThroughGesturing implements GestureListener { } } - if (STEAL_EVENTS) { - mModel.dispatchTouchEvent(event); + if (mStealEvents) { + dispatchEventToModel(event); } } - public void onFinishGesture(GestureOverlay overlay, MotionEvent event) { + public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) { if (mIsGesturing) { overlay.clear(true); - final ArrayList listeners = mActionListeners; + final ArrayList listeners = mPerformedListeners; final int count = listeners.size(); for (int i = 0; i < count; i++) { listeners.get(i).onGesturePerformed(overlay, overlay.getCurrentGesture()); } } else { - mModel.dispatchTouchEvent(event); + dispatchEventToModel(event); overlay.clear(false); } } - public void addGestureActionListener(GestureActionListener listener) { - mActionListeners.add(listener); + public void addOnGestureActionListener(OnGesturePerformedListener listener) { + mPerformedListeners.add(listener); } - public void removeGestureActionListener(GestureActionListener listener) { - mActionListeners.remove(listener); + public void removeOnGestureActionListener(OnGesturePerformedListener listener) { + mPerformedListeners.remove(listener); } public boolean isGesturing() { return mIsGesturing; } + + public static interface OnGesturePerformedListener { + public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture); + } } diff --git a/core/res/res/raw/latin_lowercase b/core/res/res/raw/latin_lowercase index 9c747d69facb1c45f38ad16970cdf3b05c9bed47..5c38b911a58fa8a5a88eca6fdbc3383b91fd1204 100644 GIT binary patch literal 28494 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 z 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 diff --git a/tests/sketch/AndroidManifest.xml b/tests/sketch/AndroidManifest.xml index 78df234efb299..fbf3a09bd224f 100755 --- a/tests/sketch/AndroidManifest.xml +++ b/tests/sketch/AndroidManifest.xml @@ -14,9 +14,7 @@ limitations under the License. --> + package="com.android.gesture.example"> diff --git a/tests/sketch/src/com/android/gesture/GestureActionListener.java b/tests/sketch/src/com/android/gesture/GestureActionListener.java deleted file mode 100644 index c9c523299f414..0000000000000 --- a/tests/sketch/src/com/android/gesture/GestureActionListener.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 com.android.gesture; - -public interface GestureActionListener { - public void onGesturePerformed(GestureOverlay overlay, Gesture gesture); -} diff --git a/tests/sketch/src/com/android/gesture/GestureListener.java b/tests/sketch/src/com/android/gesture/GestureListener.java deleted file mode 100755 index 9b5071401fd0a..0000000000000 --- a/tests/sketch/src/com/android/gesture/GestureListener.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 com.android.gesture; - -import android.view.MotionEvent; - -/** - * An interface for processing gesture events - */ -public interface GestureListener { - public void onStartGesture(GestureOverlay overlay, MotionEvent event); - - public void onGesture(GestureOverlay overlay, MotionEvent event); - - public void onFinishGesture(GestureOverlay overlay, MotionEvent event); -} diff --git a/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java b/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java index 6eb2f2309cb49..eda42248ce452 100644 --- a/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java +++ b/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java @@ -30,12 +30,11 @@ import android.view.Window; import android.widget.AdapterView; import android.widget.ListView; -import com.android.gesture.Gesture; -import com.android.gesture.GestureActionListener; -import com.android.gesture.GestureOverlay; -import com.android.gesture.LetterRecognizer; -import com.android.gesture.Prediction; -import com.android.gesture.TouchThroughGesturing; +import android.gesture.Gesture; +import android.gesture.GestureOverlayView; +import android.gesture.LetterRecognizer; +import android.gesture.Prediction; +import android.gesture.TouchThroughGestureListener; import java.util.ArrayList; @@ -52,7 +51,7 @@ public class ContactListGestureOverlay extends Activity { private ContactAdapter mContactAdapter; - private TouchThroughGesturing mGestureProcessor; + private TouchThroughGestureListener mGestureProcessor; private LetterRecognizer mRecognizer; @@ -67,7 +66,7 @@ public class ContactListGestureOverlay extends Activity { setProgressBarIndeterminateVisibility(true); // create a letter recognizer - mRecognizer = LetterRecognizer.getLetterRecognizer(this, LetterRecognizer.LATIN_LOWERCASE); + mRecognizer = LetterRecognizer.getLetterRecognizer(this, LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE); // load the contact list mContactList = (ListView) findViewById(R.id.list); @@ -95,11 +94,11 @@ public class ContactListGestureOverlay extends Activity { setProgressBarIndeterminateVisibility(false); // add a gesture overlay on top of the ListView - GestureOverlay overlay = new GestureOverlay(this); - mGestureProcessor = new TouchThroughGesturing(mContactList); - mGestureProcessor.setGestureType(TouchThroughGesturing.MULTIPLE_STROKE); - mGestureProcessor.addGestureActionListener(new GestureActionListener() { - public void onGesturePerformed(GestureOverlay overlay, Gesture gesture) { + GestureOverlayView overlay = new GestureOverlayView(this); + mGestureProcessor = new TouchThroughGestureListener(mContactList); + mGestureProcessor.setGestureType(TouchThroughGestureListener.MULTIPLE_STROKE); + mGestureProcessor.addOnGestureActionListener(new TouchThroughGestureListener.OnGesturePerformedListener() { + public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) { ArrayList predictions = mRecognizer.recognize(gesture); if (!predictions.isEmpty()) { Log.v(LOGTAG, "1st Prediction : " + predictions.get(0).name); @@ -112,7 +111,7 @@ public class ContactListGestureOverlay extends Activity { } } }); - overlay.addGestureListener(mGestureProcessor); + overlay.addOnGestureListener(mGestureProcessor); ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT); this.addContentView(overlay, params); diff --git a/tests/sketch/src/com/android/gesture/example/GestureEntry.java b/tests/sketch/src/com/android/gesture/example/GestureEntry.java index 53ba481bb77d3..200f89f037867 100644 --- a/tests/sketch/src/com/android/gesture/example/GestureEntry.java +++ b/tests/sketch/src/com/android/gesture/example/GestureEntry.java @@ -34,12 +34,11 @@ import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; import android.widget.AdapterView.OnItemSelectedListener; +import android.gesture.Gesture; -import com.android.gesture.Gesture; -import com.android.gesture.GestureLibrary; -import com.android.gesture.GestureListener; -import com.android.gesture.GestureOverlay; -import com.android.gesture.Prediction; +import android.gesture.GestureLibrary; +import android.gesture.GestureOverlayView; +import android.gesture.Prediction; import java.io.File; import java.util.ArrayList; @@ -57,7 +56,7 @@ public class GestureEntry extends Activity { private static final int VIEW_ID = Menu.FIRST + 1; - private GestureOverlay mGesturePad; + private GestureOverlayView mGesturePad; private Spinner mRecognitionResult; @@ -96,17 +95,17 @@ public class GestureEntry extends Activity { }); // create the area for drawing a gesture - mGesturePad = (GestureOverlay) findViewById(R.id.drawingpad); + mGesturePad = (GestureOverlayView) findViewById(R.id.drawingpad); mGesturePad.setBackgroundColor(Color.BLACK); - mGesturePad.addGestureListener(new GestureListener() { - public void onFinishGesture(GestureOverlay overlay, MotionEvent event) { + mGesturePad.addOnGestureListener(new GestureOverlayView.OnGestureListener() { + public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) { recognize(overlay.getCurrentGesture()); } - public void onGesture(GestureOverlay overlay, MotionEvent event) { + public void onGesture(GestureOverlayView overlay, MotionEvent event) { } - public void onStartGesture(GestureOverlay overlay, MotionEvent event) { + public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) { overlay.clear(false); } }); diff --git a/tests/sketch/src/com/android/gesture/example/GestureLibViewer.java b/tests/sketch/src/com/android/gesture/example/GestureLibViewer.java index ca541105e6033..aa07e7b972f93 100755 --- a/tests/sketch/src/com/android/gesture/example/GestureLibViewer.java +++ b/tests/sketch/src/com/android/gesture/example/GestureLibViewer.java @@ -26,10 +26,10 @@ import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; import android.widget.AdapterView.OnItemSelectedListener; +import android.gesture.Gesture; -import com.android.gesture.Gesture; -import com.android.gesture.GestureLibrary; -import com.android.gesture.GestureOverlay; +import android.gesture.GestureLibrary; +import android.gesture.GestureOverlayView; import java.util.ArrayList; import java.util.Collections; @@ -41,7 +41,7 @@ import java.util.Collections; public class GestureLibViewer extends Activity { - private GestureOverlay mGesturePad; + private GestureOverlayView mGesturePad; private Spinner mGestureCategory; @@ -90,7 +90,7 @@ public class GestureLibViewer extends Activity { setContentView(R.layout.gestureviewer); // create the area for drawing a gesture - mGesturePad = (GestureOverlay) findViewById(R.id.drawingpad); + mGesturePad = (GestureOverlayView) findViewById(R.id.drawingpad); mGesturePad.setEnabled(false); // init the gesture library diff --git a/tests/sketch/tools/Converter.java b/tests/sketch/tools/Converter.java index b4654f8c24dc9..c0391d8612175 100644 --- a/tests/sketch/tools/Converter.java +++ b/tests/sketch/tools/Converter.java @@ -15,6 +15,7 @@ import java.io.BufferedInputStream; */ public class Converter { private final File mFile; + private static final short VERSION_NUMBER = 1; Converter(File file) { mFile = file; @@ -63,10 +64,10 @@ public class Converter { iWeights = new float[hCount][]; for (int i = 0; i < hCount; i++) { - iWeights[i] = new float[iCount]; + iWeights[i] = new float[iCount + 1]; line = reader.readLine(); startIndex = 0; - for (int j = 0; j < iCount; j++) { + for (int j = 0; j <= iCount; j++) { endIndex = line.indexOf(" ", startIndex); iWeights[i][j] = Float.parseFloat(line.substring(startIndex, endIndex)); startIndex = endIndex + 1; @@ -75,10 +76,10 @@ public class Converter { oWeights = new float[oCount][]; for (int i = 0; i < oCount; i++) { - oWeights[i] = new float[hCount]; + oWeights[i] = new float[hCount + 1]; line = reader.readLine(); startIndex = 0; - for (int j = 0; j < hCount; j++) { + for (int j = 0; j <= hCount; j++) { endIndex = line.indexOf(" ", startIndex); oWeights[i][j] = Float.parseFloat(line.substring(startIndex, endIndex)); startIndex = endIndex + 1; @@ -105,6 +106,7 @@ public class Converter { try { out = new DataOutputStream(new FileOutputStream(mFile)); + out.writeShort(VERSION_NUMBER); out.writeInt(iCount); out.writeInt(hCount); out.writeInt(oCount);