16e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato/*
26e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * Copyright (C) 2008-2009 The Android Open Source Project
36e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato *
46e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * Licensed under the Apache License, Version 2.0 (the "License");
56e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * you may not use this file except in compliance with the License.
66e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * You may obtain a copy of the License at
76e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato *
86e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato *      http://www.apache.org/licenses/LICENSE-2.0
96e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato *
106e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * Unless required by applicable law or agreed to in writing, software
116e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * distributed under the License is distributed on an "AS IS" BASIS,
126e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * See the License for the specific language governing permissions and
146e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato * limitations under the License.
156e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato */
166e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
17db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guypackage android.gesture;
186e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
196e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport android.graphics.Bitmap;
206e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport android.graphics.Canvas;
216e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport android.graphics.Paint;
226e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport android.graphics.Path;
236e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport android.graphics.RectF;
246e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport android.os.Parcel;
256e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport android.os.Parcelable;
26b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guyimport android.util.Log;
276e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
286e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport java.io.IOException;
29b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guyimport java.io.DataOutputStream;
30b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guyimport java.io.DataInputStream;
31b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guyimport java.io.ByteArrayOutputStream;
32b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guyimport java.io.ByteArrayInputStream;
336e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratoimport java.util.ArrayList;
34ef0e9ae0ddbf70f5e30fbfbffe379e3026b9dc13Romain Guyimport java.util.concurrent.atomic.AtomicInteger;
356e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
366e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato/**
376fc1f151341c628125e7d20c1667a23862f32ce4Yang Li * A gesture is a hand-drawn shape on a touch screen. It can have one or multiple strokes.
386fc1f151341c628125e7d20c1667a23862f32ce4Yang Li * Each stroke is a sequence of timed points. A user-defined gesture can be recognized by
3902739a8eecce26783350041da332f8daf6691e25Romain Guy * a GestureLibrary.
406e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato */
416e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
426e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onoratopublic class Gesture implements Parcelable {
4335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private static final long GESTURE_ID_BASE = System.currentTimeMillis();
4435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
4535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private static final int BITMAP_RENDERING_WIDTH = 2;
4635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
4735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private static final boolean BITMAP_RENDERING_ANTIALIAS = true;
4835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private static final boolean BITMAP_RENDERING_DITHER = true;
4935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
50ef0e9ae0ddbf70f5e30fbfbffe379e3026b9dc13Romain Guy    private static final AtomicInteger sGestureCount = new AtomicInteger(0);
5135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
529ffe1ae56bf495b1ada1e9c1da523db0677d98b7Romain Guy    private final RectF mBoundingBox = new RectF();
5335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
5435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    // the same as its instance ID
5535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private long mGestureID;
5635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
57c534727972c3835ed997e84a349f259915ef2cddRomain Guy    private final ArrayList<GestureStroke> mStrokes = new ArrayList<GestureStroke>();
586e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
596e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    public Gesture() {
60ef0e9ae0ddbf70f5e30fbfbffe379e3026b9dc13Romain Guy        mGestureID = GESTURE_ID_BASE + sGestureCount.incrementAndGet();
616e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
626e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
636fc1f151341c628125e7d20c1667a23862f32ce4Yang Li    @Override
646fc1f151341c628125e7d20c1667a23862f32ce4Yang Li    public Object clone() {
656fc1f151341c628125e7d20c1667a23862f32ce4Yang Li        Gesture gesture = new Gesture();
666fc1f151341c628125e7d20c1667a23862f32ce4Yang Li        gesture.mBoundingBox.set(mBoundingBox.left, mBoundingBox.top,
676fc1f151341c628125e7d20c1667a23862f32ce4Yang Li                                        mBoundingBox.right, mBoundingBox.bottom);
686fc1f151341c628125e7d20c1667a23862f32ce4Yang Li        final int count = mStrokes.size();
696fc1f151341c628125e7d20c1667a23862f32ce4Yang Li        for (int i = 0; i < count; i++) {
706fc1f151341c628125e7d20c1667a23862f32ce4Yang Li            GestureStroke stroke = mStrokes.get(i);
716fc1f151341c628125e7d20c1667a23862f32ce4Yang Li            gesture.mStrokes.add((GestureStroke)stroke.clone());
726fc1f151341c628125e7d20c1667a23862f32ce4Yang Li        }
736fc1f151341c628125e7d20c1667a23862f32ce4Yang Li        return gesture;
746fc1f151341c628125e7d20c1667a23862f32ce4Yang Li    }
756fc1f151341c628125e7d20c1667a23862f32ce4Yang Li
7635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    /**
7735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @return all the strokes of the gesture
7835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     */
7935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    public ArrayList<GestureStroke> getStrokes() {
8035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        return mStrokes;
81935fd37d27804539b141fa074c060d2c15524424Yang Li    }
8235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
8335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    /**
8435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @return the number of strokes included by this gesture
8535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     */
8635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    public int getStrokesCount() {
8735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        return mStrokes.size();
886e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
896e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
9035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    /**
916fc1f151341c628125e7d20c1667a23862f32ce4Yang Li     * Adds a stroke to the gesture.
9235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     *
9335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param stroke
9435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     */
9535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    public void addStroke(GestureStroke stroke) {
9635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mStrokes.add(stroke);
979ffe1ae56bf495b1ada1e9c1da523db0677d98b7Romain Guy        mBoundingBox.union(stroke.boundingBox);
986e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
996e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato
1006e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    /**
1016fc1f151341c628125e7d20c1667a23862f32ce4Yang Li     * Calculates the total length of the gesture. When there are multiple strokes in
1026fc1f151341c628125e7d20c1667a23862f32ce4Yang Li     * the gesture, this returns the sum of the lengths of all the strokes.
10335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     *
1046e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato     * @return the length of the gesture
1056e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato     */
1066e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    public float getLength() {
10735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        int len = 0;
108c534727972c3835ed997e84a349f259915ef2cddRomain Guy        final ArrayList<GestureStroke> strokes = mStrokes;
109c534727972c3835ed997e84a349f259915ef2cddRomain Guy        final int count = strokes.size();
110c534727972c3835ed997e84a349f259915ef2cddRomain Guy
11135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        for (int i = 0; i < count; i++) {
112c534727972c3835ed997e84a349f259915ef2cddRomain Guy            len += strokes.get(i).length;
11335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
114c534727972c3835ed997e84a349f259915ef2cddRomain Guy
11535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        return len;
1166e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
11735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
11835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    /**
11935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @return the bounding box of the gesture
12035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     */
12135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    public RectF getBoundingBox() {
12235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        return mBoundingBox;
1236e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
12435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
12582f3495b146b267f3786997752cef25310176349Romain Guy    public Path toPath() {
12682f3495b146b267f3786997752cef25310176349Romain Guy        return toPath(null);
12782f3495b146b267f3786997752cef25310176349Romain Guy    }
12882f3495b146b267f3786997752cef25310176349Romain Guy
12982f3495b146b267f3786997752cef25310176349Romain Guy    public Path toPath(Path path) {
13082f3495b146b267f3786997752cef25310176349Romain Guy        if (path == null) path = new Path();
13182f3495b146b267f3786997752cef25310176349Romain Guy
13282f3495b146b267f3786997752cef25310176349Romain Guy        final ArrayList<GestureStroke> strokes = mStrokes;
13382f3495b146b267f3786997752cef25310176349Romain Guy        final int count = strokes.size();
13482f3495b146b267f3786997752cef25310176349Romain Guy
13582f3495b146b267f3786997752cef25310176349Romain Guy        for (int i = 0; i < count; i++) {
13682f3495b146b267f3786997752cef25310176349Romain Guy            path.addPath(strokes.get(i).getPath());
13782f3495b146b267f3786997752cef25310176349Romain Guy        }
13882f3495b146b267f3786997752cef25310176349Romain Guy
13982f3495b146b267f3786997752cef25310176349Romain Guy        return path;
14082f3495b146b267f3786997752cef25310176349Romain Guy    }
14182f3495b146b267f3786997752cef25310176349Romain Guy
14282f3495b146b267f3786997752cef25310176349Romain Guy    public Path toPath(int width, int height, int edge, int numSample) {
14382f3495b146b267f3786997752cef25310176349Romain Guy        return toPath(null, width, height, edge, numSample);
14482f3495b146b267f3786997752cef25310176349Romain Guy    }
14582f3495b146b267f3786997752cef25310176349Romain Guy
14682f3495b146b267f3786997752cef25310176349Romain Guy    public Path toPath(Path path, int width, int height, int edge, int numSample) {
14782f3495b146b267f3786997752cef25310176349Romain Guy        if (path == null) path = new Path();
14882f3495b146b267f3786997752cef25310176349Romain Guy
14982f3495b146b267f3786997752cef25310176349Romain Guy        final ArrayList<GestureStroke> strokes = mStrokes;
15082f3495b146b267f3786997752cef25310176349Romain Guy        final int count = strokes.size();
15182f3495b146b267f3786997752cef25310176349Romain Guy
15282f3495b146b267f3786997752cef25310176349Romain Guy        for (int i = 0; i < count; i++) {
15382f3495b146b267f3786997752cef25310176349Romain Guy            path.addPath(strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample));
15482f3495b146b267f3786997752cef25310176349Romain Guy        }
15582f3495b146b267f3786997752cef25310176349Romain Guy
15682f3495b146b267f3786997752cef25310176349Romain Guy        return path;
15782f3495b146b267f3786997752cef25310176349Romain Guy    }
15882f3495b146b267f3786997752cef25310176349Romain Guy
15935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    /**
1606fc1f151341c628125e7d20c1667a23862f32ce4Yang Li     * Sets the id of the gesture.
16135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     *
16235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param id
16335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     */
16435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    void setID(long id) {
16535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mGestureID = id;
1666e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
16735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
16835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    /**
16935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @return the id of the gesture
17035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     */
1716e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    public long getID() {
17235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        return mGestureID;
1736e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
17435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
1756e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    /**
1766fc1f151341c628125e7d20c1667a23862f32ce4Yang Li     * Creates a bitmap of the gesture with a transparent background.
17735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     *
17835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param width width of the target bitmap
17935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param height height of the target bitmap
18035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param edge the edge
18135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param numSample
18235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param color
18335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @return the bitmap
184935fd37d27804539b141fa074c060d2c15524424Yang Li     */
18535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    public Bitmap toBitmap(int width, int height, int edge, int numSample, int color) {
186b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
187b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final Canvas canvas = new Canvas(bitmap);
188b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
18935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        canvas.translate(edge, edge);
190b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
191b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final Paint paint = new Paint();
19235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS);
19335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setDither(BITMAP_RENDERING_DITHER);
19435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setColor(color);
19535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setStyle(Paint.Style.STROKE);
19635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setStrokeJoin(Paint.Join.ROUND);
19735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setStrokeCap(Paint.Cap.ROUND);
19835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setStrokeWidth(BITMAP_RENDERING_WIDTH);
199c534727972c3835ed997e84a349f259915ef2cddRomain Guy
200c534727972c3835ed997e84a349f259915ef2cddRomain Guy        final ArrayList<GestureStroke> strokes = mStrokes;
201c534727972c3835ed997e84a349f259915ef2cddRomain Guy        final int count = strokes.size();
202c534727972c3835ed997e84a349f259915ef2cddRomain Guy
20335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        for (int i = 0; i < count; i++) {
204c534727972c3835ed997e84a349f259915ef2cddRomain Guy            Path path = strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample);
20535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            canvas.drawPath(path, paint);
206935fd37d27804539b141fa074c060d2c15524424Yang Li        }
20735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
20835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        return bitmap;
209935fd37d27804539b141fa074c060d2c15524424Yang Li    }
21035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
211935fd37d27804539b141fa074c060d2c15524424Yang Li    /**
2126fc1f151341c628125e7d20c1667a23862f32ce4Yang Li     * Creates a bitmap of the gesture with a transparent background.
21335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     *
21435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param width
21535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param height
216cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy     * @param inset
21735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @param color
21835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li     * @return the bitmap
2196e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato     */
220cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy    public Bitmap toBitmap(int width, int height, int inset, int color) {
221cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        final Bitmap bitmap = Bitmap.createBitmap(width, height,
222cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy                Bitmap.Config.ARGB_8888);
223b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final Canvas canvas = new Canvas(bitmap);
224b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
225b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final Paint paint = new Paint();
22635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS);
22735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setDither(BITMAP_RENDERING_DITHER);
22835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setColor(color);
2296e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        paint.setStyle(Paint.Style.STROKE);
2306e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        paint.setStrokeJoin(Paint.Join.ROUND);
2316e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        paint.setStrokeCap(Paint.Cap.ROUND);
23235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        paint.setStrokeWidth(BITMAP_RENDERING_WIDTH);
233b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
234cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        final Path path = toPath();
235cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        final RectF bounds = new RectF();
236cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        path.computeBounds(bounds, true);
237b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
238cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        final float sx = (width - 2 * inset) / bounds.width();
239cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        final float sy = (height - 2 * inset) / bounds.height();
240cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        final float scale = sx > sy ? sy : sx;
241cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        paint.setStrokeWidth(2.0f / scale);
242cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy
243cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        path.offset(-bounds.left + (width - bounds.width() * scale) / 2.0f,
244cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy                -bounds.top + (height - bounds.height() * scale) / 2.0f);
245cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy
246cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        canvas.translate(inset, inset);
247cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        canvas.scale(scale, scale);
248cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy
249cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        canvas.drawPath(path, paint);
25035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
2516e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        return bitmap;
2526e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
25335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
254b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy    void serialize(DataOutputStream out) throws IOException {
255b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final ArrayList<GestureStroke> strokes = mStrokes;
256b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final int count = strokes.size();
257b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
258b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        // Write gesture ID
259b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        out.writeLong(mGestureID);
260b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        // Write number of strokes
261b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        out.writeInt(count);
262b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
26335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        for (int i = 0; i < count; i++) {
264b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            strokes.get(i).serialize(out);
2656e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        }
2666e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
26735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
268b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy    static Gesture deserialize(DataInputStream in) throws IOException {
269b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final Gesture gesture = new Gesture();
270b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
271b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        // Gesture ID
272b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        gesture.mGestureID = in.readLong();
273b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        // Number of strokes
274b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final int count = in.readInt();
275b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
276b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        for (int i = 0; i < count; i++) {
277b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            gesture.addStroke(GestureStroke.deserialize(in));
2786e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        }
279b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
280b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        return gesture;
2816e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
28235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
28335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    public static final Parcelable.Creator<Gesture> CREATOR = new Parcelable.Creator<Gesture>() {
2846e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        public Gesture createFromParcel(Parcel in) {
285b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            Gesture gesture = null;
286b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            final long gestureID = in.readLong();
287b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
288b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            final DataInputStream inStream = new DataInputStream(
289b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy                    new ByteArrayInputStream(in.createByteArray()));
290b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
291b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            try {
292b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy                gesture = deserialize(inStream);
293b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            } catch (IOException e) {
294db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy                Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e);
295b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            } finally {
29646c53129c6f27c9193ab195a69cb50591b8c1fa2Romain Guy                GestureUtils.closeStream(inStream);
297b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            }
298b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
299b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            if (gesture != null) {
300b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy                gesture.mGestureID = gestureID;
301b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            }
302b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
30335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            return gesture;
3046e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        }
30535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3066e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        public Gesture[] newArray(int size) {
3076e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato            return new Gesture[size];
3086e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato        }
3096e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    };
31035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3116e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    public void writeToParcel(Parcel out, int flags) {
312b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        out.writeLong(mGestureID);
313b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
314b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        boolean result = false;
315b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final ByteArrayOutputStream byteStream =
316b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy                new ByteArrayOutputStream(GestureConstants.IO_BUFFER_SIZE);
317b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        final DataOutputStream outStream = new DataOutputStream(byteStream);
318b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
319b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        try {
320b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            serialize(outStream);
321b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            result = true;
322b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        } catch (IOException e) {
323db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy            Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e);
324b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        } finally {
32546c53129c6f27c9193ab195a69cb50591b8c1fa2Romain Guy            GestureUtils.closeStream(outStream);
32646c53129c6f27c9193ab195a69cb50591b8c1fa2Romain Guy            GestureUtils.closeStream(byteStream);
327b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        }
328b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
329b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        if (result) {
330b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy            out.writeByteArray(byteStream.toByteArray());
331b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        }
3326e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
33335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3346e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    public int describeContents() {
335b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy        return 0;
3366e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato    }
3376e93a3db56d6add29b43077718a4cad9ccfc047fJoe Onorato}
338b6d99b7d17fd1bb1326a70744bd01be5d1586487Romain Guy
339