1
2
3
4
5
6
7
8
9
10
11
12
13
14 package com.googlecode.mgwt.dom.client.recognizer.tap;
15
16 import com.google.gwt.event.shared.HasHandlers;
17 import com.googlecode.mgwt.collection.shared.CollectionFactory;
18 import com.googlecode.mgwt.collection.shared.LightArray;
19 import com.googlecode.mgwt.dom.client.event.touch.Touch;
20 import com.googlecode.mgwt.dom.client.event.touch.TouchCancelEvent;
21 import com.googlecode.mgwt.dom.client.event.touch.TouchEndEvent;
22 import com.googlecode.mgwt.dom.client.event.touch.TouchHandler;
23 import com.googlecode.mgwt.dom.client.event.touch.TouchMoveEvent;
24 import com.googlecode.mgwt.dom.client.event.touch.TouchStartEvent;
25 import com.googlecode.mgwt.dom.client.event.touch.TouchUtil;
26 import com.googlecode.mgwt.dom.client.recognizer.SystemTimeProvider;
27 import com.googlecode.mgwt.dom.client.recognizer.TimeProvider;
28
29
30
31
32
33
34
35 public class MultiTapRecognizer implements TouchHandler {
36
37 public static final int DEFAULT_DISTANCE = 15;
38 public static final int DEFAULT_TIME_IN_MS = 300;
39
40 private final HasHandlers source;
41 private final int distance;
42 private final int time;
43 private final int numberOfTabs;
44
45 private int touchCount;
46
47 private LightArray<Touch> touches;
48 private final int numberOfFingers;
49
50 private TimeProvider timeProvider;
51
52 private enum State {
53 READY, FINGERS_GOING_DOWN, FINGERS_GOING_UP, INVALID
54 }
55
56 private State state;
57 private int foundTaps;
58 private int touchMax;
59 private long lastTime;
60
61 private LightArray<LightArray<Touch>> savedStartTouches;
62
63
64
65
66
67
68
69 public MultiTapRecognizer(HasHandlers source, int numberOfFingers) {
70 this(source, numberOfFingers, 1, DEFAULT_DISTANCE, DEFAULT_TIME_IN_MS);
71 }
72
73
74
75
76
77
78
79
80 public MultiTapRecognizer(HasHandlers source, int numberOfFingers, int numberOfTabs) {
81 this(source, numberOfFingers, numberOfTabs, DEFAULT_DISTANCE, DEFAULT_TIME_IN_MS);
82 }
83
84
85
86
87
88
89
90
91
92 public MultiTapRecognizer(HasHandlers source, int numberOfFingers, int numberOfTabs, int distance) {
93 this(source, numberOfFingers, numberOfTabs, distance, DEFAULT_TIME_IN_MS);
94 }
95
96
97
98
99
100
101
102
103
104
105 public MultiTapRecognizer(HasHandlers source, int numberOfFingers, int numberOfTabs, int distance, int time) {
106
107 if (source == null)
108 throw new IllegalArgumentException("source can not be null");
109
110 if (numberOfFingers < 1) {
111 throw new IllegalArgumentException("numberOfFingers > 0");
112 }
113
114 if (numberOfTabs < 1) {
115 throw new IllegalArgumentException("numberOfTabs > 0");
116 }
117
118 if (distance < 0)
119 throw new IllegalArgumentException("distance > 0");
120
121 if (time < 1) {
122 throw new IllegalArgumentException("time > 0");
123 }
124 this.source = source;
125 this.numberOfFingers = numberOfFingers;
126 this.numberOfTabs = numberOfTabs;
127 this.distance = distance;
128 this.time = time;
129 touchCount = 0;
130 touches = CollectionFactory.constructArray();
131 savedStartTouches = CollectionFactory.constructArray();
132 state = State.READY;
133 foundTaps = 0;
134 timeProvider = new SystemTimeProvider();
135
136 }
137
138
139
140
141
142
143
144
145 @Override
146 public void onTouchStart(TouchStartEvent event) {
147 touchCount++;
148 LightArray<Touch> currentTouches = event.getTouches();
149
150 switch (state) {
151 case READY:
152 touches.push(TouchUtil.cloneTouch(currentTouches.get(touchCount - 1)));
153 state = State.FINGERS_GOING_DOWN;
154 break;
155
156 case FINGERS_GOING_DOWN:
157 touches.push(TouchUtil.cloneTouch(currentTouches.get(touchCount - 1)));
158 break;
159
160 case FINGERS_GOING_UP:
161 default:
162 state = State.INVALID;
163 break;
164 }
165
166 if (touchCount > numberOfFingers) {
167 state = State.INVALID;
168 }
169
170 }
171
172
173
174
175
176
177
178
179 @Override
180 public void onTouchMove(TouchMoveEvent event) {
181 switch (state) {
182 case FINGERS_GOING_DOWN:
183 case FINGERS_GOING_UP:
184
185 LightArray<Touch> currentTouches = event.getTouches();
186 for (int i = 0; i < currentTouches.length(); i++) {
187 Touch currentTouch = currentTouches.get(i);
188 for (int j = 0; j < touches.length(); j++) {
189 Touch startTouch = touches.get(j);
190 if (currentTouch.getIdentifier() == startTouch.getIdentifier()) {
191 if (Math.abs(currentTouch.getPageX() - startTouch.getPageX()) > distance || Math.abs(currentTouch.getPageY() - startTouch.getPageY()) > distance) {
192 state = State.INVALID;
193 break;
194 }
195 }
196 if (state == State.INVALID) {
197 break;
198 }
199 }
200 }
201
202 break;
203
204 default:
205 break;
206 }
207
208 }
209
210
211
212
213
214
215
216
217 @Override
218 public void onTouchEnd(TouchEndEvent event) {
219
220 switch (state) {
221 case FINGERS_GOING_DOWN:
222 state = State.FINGERS_GOING_UP;
223 touchMax = touchCount;
224
225 touchCount--;
226 handleTouchEnd();
227 break;
228 case FINGERS_GOING_UP:
229 touchCount--;
230 handleTouchEnd();
231 break;
232
233 case INVALID:
234 case READY:
235 savedStartTouches = CollectionFactory.constructArray();
236 if (event.getTouches().length() == 0)
237 reset();
238 break;
239 default:
240 reset();
241 break;
242 }
243
244 }
245
246
247
248
249
250
251
252
253 @Override
254 public void onTouchCanceled(TouchCancelEvent event) {
255 state = State.INVALID;
256 reset();
257
258 }
259
260 protected void handleTouchEnd() {
261 if (touchCount == 0) {
262
263 if (foundTaps > 0) {
264
265 if (timeProvider.getTime() - lastTime > time) {
266 savedStartTouches = CollectionFactory.constructArray();
267 reset();
268 return;
269 }
270 }
271 foundTaps++;
272 lastTime = timeProvider.getTime();
273
274
275 savedStartTouches.push(touches);
276
277 if (foundTaps == numberOfTabs) {
278
279 MultiTapEvent multiTapEvent = new MultiTapEvent(touchMax, numberOfTabs, savedStartTouches);
280 source.fireEvent(multiTapEvent);
281 savedStartTouches = CollectionFactory.constructArray();
282 reset();
283 } else {
284 state = State.READY;
285 touches = CollectionFactory.constructArray();
286 }
287
288 }
289 }
290
291 protected void reset() {
292 touchCount = 0;
293 foundTaps = 0;
294 touches = CollectionFactory.constructArray();
295 state = State.READY;
296 }
297
298 protected void setTimeProvider(TimeProvider timeProvider) {
299 if (timeProvider == null) {
300 throw new IllegalArgumentException("timeprovider can not be null");
301 }
302 this.timeProvider = timeProvider;
303 }
304
305 }