blob: c6a2a875352b0f78f88aba3e6f65589acb261120 [file] [log] [blame]
Joe Onorato6e93a3d2009-04-17 14:18:46 -07001/*
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
Romain Guydb567c32009-05-21 16:23:21 -070017package android.gesture;
Joe Onorato6e93a3d2009-04-17 14:18:46 -070018
19import android.graphics.Bitmap;
20import android.graphics.Canvas;
Joe Onorato6e93a3d2009-04-17 14:18:46 -070021import android.graphics.Paint;
22import android.graphics.Path;
Joe Onorato6e93a3d2009-04-17 14:18:46 -070023import android.graphics.RectF;
24import android.os.Parcel;
25import android.os.Parcelable;
Romain Guyb6d99b72009-05-21 15:05:50 -070026import android.util.Log;
Joe Onorato6e93a3d2009-04-17 14:18:46 -070027
28import java.io.IOException;
Romain Guyb6d99b72009-05-21 15:05:50 -070029import java.io.DataOutputStream;
30import java.io.DataInputStream;
31import java.io.ByteArrayOutputStream;
32import java.io.ByteArrayInputStream;
Joe Onorato6e93a3d2009-04-17 14:18:46 -070033import java.util.ArrayList;
Romain Guyef0e9ae2009-07-10 14:11:26 -070034import java.util.concurrent.atomic.AtomicInteger;
Joe Onorato6e93a3d2009-04-17 14:18:46 -070035
36/**
Yang Li6fc1f152010-01-15 14:35:27 -080037 * 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
Romain Guy02739a82011-05-16 11:43:18 -070039 * a GestureLibrary.
Joe Onorato6e93a3d2009-04-17 14:18:46 -070040 */
41
42public class Gesture implements Parcelable {
Yang Li35aa84b2009-05-18 18:29:05 -070043 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;
Yang Li35aa84b2009-05-18 18:29:05 -070048 private static final boolean BITMAP_RENDERING_DITHER = true;
49
Romain Guyef0e9ae2009-07-10 14:11:26 -070050 private static final AtomicInteger sGestureCount = new AtomicInteger(0);
Yang Li35aa84b2009-05-18 18:29:05 -070051
Romain Guy9ffe1ae2009-05-29 12:28:23 -070052 private final RectF mBoundingBox = new RectF();
Yang Li35aa84b2009-05-18 18:29:05 -070053
54 // the same as its instance ID
55 private long mGestureID;
56
Romain Guyc5347272009-05-20 10:37:13 -070057 private final ArrayList<GestureStroke> mStrokes = new ArrayList<GestureStroke>();
Joe Onorato6e93a3d2009-04-17 14:18:46 -070058
59 public Gesture() {
Romain Guyef0e9ae2009-07-10 14:11:26 -070060 mGestureID = GESTURE_ID_BASE + sGestureCount.incrementAndGet();
Yang Lif8173412009-05-15 13:55:56 -070061 }
62
Yang Li6fc1f152010-01-15 14:35:27 -080063 @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
Yang Lif8173412009-05-15 13:55:56 -070076 /**
Yang Li35aa84b2009-05-18 18:29:05 -070077 * @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 /**
Yang Li6fc1f152010-01-15 14:35:27 -080091 * Adds a stroke to the gesture.
Yang Li35aa84b2009-05-18 18:29:05 -070092 *
93 * @param stroke
94 */
95 public void addStroke(GestureStroke stroke) {
96 mStrokes.add(stroke);
Romain Guy9ffe1ae2009-05-29 12:28:23 -070097 mBoundingBox.union(stroke.boundingBox);
Yang Li35aa84b2009-05-18 18:29:05 -070098 }
99
100 /**
Yang Li6fc1f152010-01-15 14:35:27 -0800101 * 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.
Yang Li35aa84b2009-05-18 18:29:05 -0700103 *
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700104 * @return the length of the gesture
105 */
106 public float getLength() {
Yang Li35aa84b2009-05-18 18:29:05 -0700107 int len = 0;
Romain Guyc5347272009-05-20 10:37:13 -0700108 final ArrayList<GestureStroke> strokes = mStrokes;
109 final int count = strokes.size();
110
Yang Li35aa84b2009-05-18 18:29:05 -0700111 for (int i = 0; i < count; i++) {
Romain Guyc5347272009-05-20 10:37:13 -0700112 len += strokes.get(i).length;
Yang Li35aa84b2009-05-18 18:29:05 -0700113 }
Romain Guyc5347272009-05-20 10:37:13 -0700114
Yang Li35aa84b2009-05-18 18:29:05 -0700115 return len;
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700116 }
Yang Li35aa84b2009-05-18 18:29:05 -0700117
118 /**
119 * @return the bounding box of the gesture
120 */
121 public RectF getBoundingBox() {
122 return mBoundingBox;
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700123 }
Yang Li35aa84b2009-05-18 18:29:05 -0700124
Romain Guy82f34952009-05-24 18:40:45 -0700125 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
Yang Li35aa84b2009-05-18 18:29:05 -0700159 /**
Yang Li6fc1f152010-01-15 14:35:27 -0800160 * Sets the id of the gesture.
Yang Li35aa84b2009-05-18 18:29:05 -0700161 *
162 * @param id
163 */
164 void setID(long id) {
165 mGestureID = id;
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700166 }
Yang Li35aa84b2009-05-18 18:29:05 -0700167
168 /**
169 * @return the id of the gesture
170 */
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700171 public long getID() {
Yang Li35aa84b2009-05-18 18:29:05 -0700172 return mGestureID;
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700173 }
Yang Li35aa84b2009-05-18 18:29:05 -0700174
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700175 /**
Yang Li6fc1f152010-01-15 14:35:27 -0800176 * Creates a bitmap of the gesture with a transparent background.
Yang Li35aa84b2009-05-18 18:29:05 -0700177 *
178 * @param width width of the target bitmap
179 * @param height height of the target bitmap
180 * @param edge the edge
Yang Li935fd372009-05-15 16:51:46 -0700181 * @param numSample
Yang Li35aa84b2009-05-18 18:29:05 -0700182 * @param color
183 * @return the bitmap
Yang Lif8173412009-05-15 13:55:56 -0700184 */
Yang Li35aa84b2009-05-18 18:29:05 -0700185 public Bitmap toBitmap(int width, int height, int edge, int numSample, int color) {
Romain Guyb6d99b72009-05-21 15:05:50 -0700186 final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
187 final Canvas canvas = new Canvas(bitmap);
188
Yang Li35aa84b2009-05-18 18:29:05 -0700189 canvas.translate(edge, edge);
Romain Guyb6d99b72009-05-21 15:05:50 -0700190
191 final Paint paint = new Paint();
Yang Li35aa84b2009-05-18 18:29:05 -0700192 paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS);
193 paint.setDither(BITMAP_RENDERING_DITHER);
194 paint.setColor(color);
Yang Lif8173412009-05-15 13:55:56 -0700195 paint.setStyle(Paint.Style.STROKE);
196 paint.setStrokeJoin(Paint.Join.ROUND);
197 paint.setStrokeCap(Paint.Cap.ROUND);
Yang Li35aa84b2009-05-18 18:29:05 -0700198 paint.setStrokeWidth(BITMAP_RENDERING_WIDTH);
Romain Guyc5347272009-05-20 10:37:13 -0700199
200 final ArrayList<GestureStroke> strokes = mStrokes;
201 final int count = strokes.size();
202
Yang Li35aa84b2009-05-18 18:29:05 -0700203 for (int i = 0; i < count; i++) {
Romain Guyc5347272009-05-20 10:37:13 -0700204 Path path = strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample);
Yang Li35aa84b2009-05-18 18:29:05 -0700205 canvas.drawPath(path, paint);
206 }
207
Yang Lif8173412009-05-15 13:55:56 -0700208 return bitmap;
209 }
Yang Li35aa84b2009-05-18 18:29:05 -0700210
Yang Lif8173412009-05-15 13:55:56 -0700211 /**
Yang Li6fc1f152010-01-15 14:35:27 -0800212 * Creates a bitmap of the gesture with a transparent background.
Yang Li35aa84b2009-05-18 18:29:05 -0700213 *
214 * @param width
215 * @param height
Romain Guycfbe8cf2009-06-10 01:21:20 -0700216 * @param inset
Yang Li35aa84b2009-05-18 18:29:05 -0700217 * @param color
218 * @return the bitmap
219 */
Romain Guycfbe8cf2009-06-10 01:21:20 -0700220 public Bitmap toBitmap(int width, int height, int inset, int color) {
221 final Bitmap bitmap = Bitmap.createBitmap(width, height,
222 Bitmap.Config.ARGB_8888);
Romain Guyb6d99b72009-05-21 15:05:50 -0700223 final Canvas canvas = new Canvas(bitmap);
224
Romain Guyb6d99b72009-05-21 15:05:50 -0700225 final Paint paint = new Paint();
Yang Li35aa84b2009-05-18 18:29:05 -0700226 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);
Romain Guyb6d99b72009-05-21 15:05:50 -0700233
Romain Guycfbe8cf2009-06-10 01:21:20 -0700234 final Path path = toPath();
235 final RectF bounds = new RectF();
236 path.computeBounds(bounds, true);
Romain Guyb6d99b72009-05-21 15:05:50 -0700237
Romain Guycfbe8cf2009-06-10 01:21:20 -0700238 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);
Yang Li35aa84b2009-05-18 18:29:05 -0700250
251 return bitmap;
252 }
253
Romain Guyb6d99b72009-05-21 15:05:50 -0700254 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
Yang Li35aa84b2009-05-18 18:29:05 -0700263 for (int i = 0; i < count; i++) {
Romain Guyb6d99b72009-05-21 15:05:50 -0700264 strokes.get(i).serialize(out);
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700265 }
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700266 }
Yang Li35aa84b2009-05-18 18:29:05 -0700267
Romain Guyb6d99b72009-05-21 15:05:50 -0700268 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));
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700278 }
Romain Guyb6d99b72009-05-21 15:05:50 -0700279
280 return gesture;
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700281 }
Yang Li35aa84b2009-05-18 18:29:05 -0700282
Yang Li35aa84b2009-05-18 18:29:05 -0700283 public static final Parcelable.Creator<Gesture> CREATOR = new Parcelable.Creator<Gesture>() {
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700284 public Gesture createFromParcel(Parcel in) {
Romain Guyb6d99b72009-05-21 15:05:50 -0700285 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) {
Romain Guydb567c32009-05-21 16:23:21 -0700294 Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e);
Romain Guyb6d99b72009-05-21 15:05:50 -0700295 } finally {
Romain Guy46c53122010-02-04 14:19:50 -0800296 GestureUtils.closeStream(inStream);
Romain Guyb6d99b72009-05-21 15:05:50 -0700297 }
298
299 if (gesture != null) {
300 gesture.mGestureID = gestureID;
301 }
302
Yang Li35aa84b2009-05-18 18:29:05 -0700303 return gesture;
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700304 }
Yang Li35aa84b2009-05-18 18:29:05 -0700305
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700306 public Gesture[] newArray(int size) {
307 return new Gesture[size];
308 }
309 };
Yang Li35aa84b2009-05-18 18:29:05 -0700310
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700311 public void writeToParcel(Parcel out, int flags) {
Romain Guyb6d99b72009-05-21 15:05:50 -0700312 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) {
Romain Guydb567c32009-05-21 16:23:21 -0700323 Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e);
Romain Guyb6d99b72009-05-21 15:05:50 -0700324 } finally {
Romain Guy46c53122010-02-04 14:19:50 -0800325 GestureUtils.closeStream(outStream);
326 GestureUtils.closeStream(byteStream);
Romain Guyb6d99b72009-05-21 15:05:50 -0700327 }
328
329 if (result) {
330 out.writeByteArray(byteStream.toByteArray());
331 }
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700332 }
Yang Li35aa84b2009-05-18 18:29:05 -0700333
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700334 public int describeContents() {
Romain Guyb6d99b72009-05-21 15:05:50 -0700335 return 0;
Joe Onorato6e93a3d2009-04-17 14:18:46 -0700336 }
337}
Romain Guyb6d99b72009-05-21 15:05:50 -0700338