Home | History | Annotate | Download | only in gesture
      1 /*
      2  * Copyright (C) 2008-2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.gesture;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.Canvas;
     21 import android.graphics.Paint;
     22 import android.graphics.Path;
     23 import android.graphics.RectF;
     24 import android.os.Parcel;
     25 import android.os.Parcelable;
     26 import android.util.Log;
     27 
     28 import java.io.IOException;
     29 import java.io.DataOutputStream;
     30 import java.io.DataInputStream;
     31 import java.io.ByteArrayOutputStream;
     32 import java.io.ByteArrayInputStream;
     33 import java.util.ArrayList;
     34 import java.util.concurrent.atomic.AtomicInteger;
     35 
     36 /**
     37  * A gesture is a hand-drawn shape on a touch screen. It can have one or multiple strokes.
     38  * Each stroke is a sequence of timed points. A user-defined gesture can be recognized by
     39  * a GestureLibrary.
     40  */
     41 
     42 public class Gesture implements Parcelable {
     43     private static final long GESTURE_ID_BASE = System.currentTimeMillis();
     44 
     45     private static final int BITMAP_RENDERING_WIDTH = 2;
     46 
     47     private static final boolean BITMAP_RENDERING_ANTIALIAS = true;
     48     private static final boolean BITMAP_RENDERING_DITHER = true;
     49 
     50     private static final AtomicInteger sGestureCount = new AtomicInteger(0);
     51 
     52     private final RectF mBoundingBox = new RectF();
     53 
     54     // the same as its instance ID
     55     private long mGestureID;
     56 
     57     private final ArrayList<GestureStroke> mStrokes = new ArrayList<GestureStroke>();
     58 
     59     public Gesture() {
     60         mGestureID = GESTURE_ID_BASE + sGestureCount.incrementAndGet();
     61     }
     62 
     63     @Override
     64     public Object clone() {
     65         Gesture gesture = new Gesture();
     66         gesture.mBoundingBox.set(mBoundingBox.left, mBoundingBox.top,
     67                                         mBoundingBox.right, mBoundingBox.bottom);
     68         final int count = mStrokes.size();
     69         for (int i = 0; i < count; i++) {
     70             GestureStroke stroke = mStrokes.get(i);
     71             gesture.mStrokes.add((GestureStroke)stroke.clone());
     72         }
     73         return gesture;
     74     }
     75 
     76     /**
     77      * @return all the strokes of the gesture
     78      */
     79     public ArrayList<GestureStroke> getStrokes() {
     80         return mStrokes;
     81     }
     82 
     83     /**
     84      * @return the number of strokes included by this gesture
     85      */
     86     public int getStrokesCount() {
     87         return mStrokes.size();
     88     }
     89 
     90     /**
     91      * Adds a stroke to the gesture.
     92      *
     93      * @param stroke
     94      */
     95     public void addStroke(GestureStroke stroke) {
     96         mStrokes.add(stroke);
     97         mBoundingBox.union(stroke.boundingBox);
     98     }
     99 
    100     /**
    101      * Calculates the total length of the gesture. When there are multiple strokes in
    102      * the gesture, this returns the sum of the lengths of all the strokes.
    103      *
    104      * @return the length of the gesture
    105      */
    106     public float getLength() {
    107         int len = 0;
    108         final ArrayList<GestureStroke> strokes = mStrokes;
    109         final int count = strokes.size();
    110 
    111         for (int i = 0; i < count; i++) {
    112             len += strokes.get(i).length;
    113         }
    114 
    115         return len;
    116     }
    117 
    118     /**
    119      * @return the bounding box of the gesture
    120      */
    121     public RectF getBoundingBox() {
    122         return mBoundingBox;
    123     }
    124 
    125     public Path toPath() {
    126         return toPath(null);
    127     }
    128 
    129     public Path toPath(Path path) {
    130         if (path == null) path = new Path();
    131 
    132         final ArrayList<GestureStroke> strokes = mStrokes;
    133         final int count = strokes.size();
    134 
    135         for (int i = 0; i < count; i++) {
    136             path.addPath(strokes.get(i).getPath());
    137         }
    138 
    139         return path;
    140     }
    141 
    142     public Path toPath(int width, int height, int edge, int numSample) {
    143         return toPath(null, width, height, edge, numSample);
    144     }
    145 
    146     public Path toPath(Path path, int width, int height, int edge, int numSample) {
    147         if (path == null) path = new Path();
    148 
    149         final ArrayList<GestureStroke> strokes = mStrokes;
    150         final int count = strokes.size();
    151 
    152         for (int i = 0; i < count; i++) {
    153             path.addPath(strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample));
    154         }
    155 
    156         return path;
    157     }
    158 
    159     /**
    160      * Sets the id of the gesture.
    161      *
    162      * @param id
    163      */
    164     void setID(long id) {
    165         mGestureID = id;
    166     }
    167 
    168     /**
    169      * @return the id of the gesture
    170      */
    171     public long getID() {
    172         return mGestureID;
    173     }
    174 
    175     /**
    176      * Creates a bitmap of the gesture with a transparent background.
    177      *
    178      * @param width width of the target bitmap
    179      * @param height height of the target bitmap
    180      * @param edge the edge
    181      * @param numSample
    182      * @param color
    183      * @return the bitmap
    184      */
    185     public Bitmap toBitmap(int width, int height, int edge, int numSample, int color) {
    186         final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    187         final Canvas canvas = new Canvas(bitmap);
    188 
    189         canvas.translate(edge, edge);
    190 
    191         final Paint paint = new Paint();
    192         paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS);
    193         paint.setDither(BITMAP_RENDERING_DITHER);
    194         paint.setColor(color);
    195         paint.setStyle(Paint.Style.STROKE);
    196         paint.setStrokeJoin(Paint.Join.ROUND);
    197         paint.setStrokeCap(Paint.Cap.ROUND);
    198         paint.setStrokeWidth(BITMAP_RENDERING_WIDTH);
    199 
    200         final ArrayList<GestureStroke> strokes = mStrokes;
    201         final int count = strokes.size();
    202 
    203         for (int i = 0; i < count; i++) {
    204             Path path = strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample);
    205             canvas.drawPath(path, paint);
    206         }
    207 
    208         return bitmap;
    209     }
    210 
    211     /**
    212      * Creates a bitmap of the gesture with a transparent background.
    213      *
    214      * @param width
    215      * @param height
    216      * @param inset
    217      * @param color
    218      * @return the bitmap
    219      */
    220     public Bitmap toBitmap(int width, int height, int inset, int color) {
    221         final Bitmap bitmap = Bitmap.createBitmap(width, height,
    222                 Bitmap.Config.ARGB_8888);
    223         final Canvas canvas = new Canvas(bitmap);
    224 
    225         final Paint paint = new Paint();
    226         paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS);
    227         paint.setDither(BITMAP_RENDERING_DITHER);
    228         paint.setColor(color);
    229         paint.setStyle(Paint.Style.STROKE);
    230         paint.setStrokeJoin(Paint.Join.ROUND);
    231         paint.setStrokeCap(Paint.Cap.ROUND);
    232         paint.setStrokeWidth(BITMAP_RENDERING_WIDTH);
    233 
    234         final Path path = toPath();
    235         final RectF bounds = new RectF();
    236         path.computeBounds(bounds, true);
    237 
    238         final float sx = (width - 2 * inset) / bounds.width();
    239         final float sy = (height - 2 * inset) / bounds.height();
    240         final float scale = sx > sy ? sy : sx;
    241         paint.setStrokeWidth(2.0f / scale);
    242 
    243         path.offset(-bounds.left + (width - bounds.width() * scale) / 2.0f,
    244                 -bounds.top + (height - bounds.height() * scale) / 2.0f);
    245 
    246         canvas.translate(inset, inset);
    247         canvas.scale(scale, scale);
    248 
    249         canvas.drawPath(path, paint);
    250 
    251         return bitmap;
    252     }
    253 
    254     void serialize(DataOutputStream out) throws IOException {
    255         final ArrayList<GestureStroke> strokes = mStrokes;
    256         final int count = strokes.size();
    257 
    258         // Write gesture ID
    259         out.writeLong(mGestureID);
    260         // Write number of strokes
    261         out.writeInt(count);
    262 
    263         for (int i = 0; i < count; i++) {
    264             strokes.get(i).serialize(out);
    265         }
    266     }
    267 
    268     static Gesture deserialize(DataInputStream in) throws IOException {
    269         final Gesture gesture = new Gesture();
    270 
    271         // Gesture ID
    272         gesture.mGestureID = in.readLong();
    273         // Number of strokes
    274         final int count = in.readInt();
    275 
    276         for (int i = 0; i < count; i++) {
    277             gesture.addStroke(GestureStroke.deserialize(in));
    278         }
    279 
    280         return gesture;
    281     }
    282 
    283     public static final Parcelable.Creator<Gesture> CREATOR = new Parcelable.Creator<Gesture>() {
    284         public Gesture createFromParcel(Parcel in) {
    285             Gesture gesture = null;
    286             final long gestureID = in.readLong();
    287 
    288             final DataInputStream inStream = new DataInputStream(
    289                     new ByteArrayInputStream(in.createByteArray()));
    290 
    291             try {
    292                 gesture = deserialize(inStream);
    293             } catch (IOException e) {
    294                 Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e);
    295             } finally {
    296                 GestureUtils.closeStream(inStream);
    297             }
    298 
    299             if (gesture != null) {
    300                 gesture.mGestureID = gestureID;
    301             }
    302 
    303             return gesture;
    304         }
    305 
    306         public Gesture[] newArray(int size) {
    307             return new Gesture[size];
    308         }
    309     };
    310 
    311     public void writeToParcel(Parcel out, int flags) {
    312         out.writeLong(mGestureID);
    313 
    314         boolean result = false;
    315         final ByteArrayOutputStream byteStream =
    316                 new ByteArrayOutputStream(GestureConstants.IO_BUFFER_SIZE);
    317         final DataOutputStream outStream = new DataOutputStream(byteStream);
    318 
    319         try {
    320             serialize(outStream);
    321             result = true;
    322         } catch (IOException e) {
    323             Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e);
    324         } finally {
    325             GestureUtils.closeStream(outStream);
    326             GestureUtils.closeStream(byteStream);
    327         }
    328 
    329         if (result) {
    330             out.writeByteArray(byteStream.toByteArray());
    331         }
    332     }
    333 
    334     public int describeContents() {
    335         return 0;
    336     }
    337 }
    338 
    339