-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinputgestures.go
274 lines (236 loc) · 6.23 KB
/
inputgestures.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package libphonelabgo
import (
"errors"
"fmt"
"math"
)
type TouchScreenEvent struct {
What int `json:"what"`
Timestamp int64 `json:"timestamp"`
TraceTime float64 `json:"tracetime"`
X float64 `json:"x"`
Y float64 `json:"y"`
Code int `json:"code"`
}
func (event *TouchScreenEvent) MonotonicTimestamp() float64 {
if GlobalConf.UseSysTime {
return float64(event.Timestamp) / nsPerSecF
} else {
return event.TraceTime
}
}
// To start with, we only detect scrolls and taps. Further, we don't make any
// distinction between short and long taps. In the future, we may want to add
// more gestures like long tap, double tap, and fling.
const (
TouchScreenEventKey = iota
TouchScreenEventTap
TouchScreenEventScrollStart
TouchScreenEventScroll
TouchScreenEventScrollEnd
)
type GestureDetector struct {
TouchSlop int
// internal state
touchSlopSquare float64
state int
downFocusX float64
lastFocusX float64
downFocusY float64
lastFocusY float64
initialDowntime int64
pointerState GesturePointerState
}
const (
GestureStateNone = iota
GestureStateTapping
GestureStateScrolling
)
type PointerState struct {
X float64
Y float64
}
type GesturePointerState map[int]*PointerState
func (g GesturePointerState) Update(event *IFMotionEventLog) error {
maskedAction := event.GetMaskedAction()
pointerIndex := event.GetActionIndex()
switch maskedAction {
case ACTION_UP:
{
// Remove all pointers
g.Clear()
}
case ACTION_POINTER_UP:
{
// Remove a single pointer
delete(g, pointerIndex)
}
case ACTION_DOWN:
fallthrough
case ACTION_POINTER_DOWN:
{
// Start tracking the pointer
g[pointerIndex] = &PointerState{
X: -1.0,
Y: -1.0,
}
// We also want to update any pointer positions
}
fallthrough
case ACTION_MOVE:
{
// Update all existing pointers
for _, ptr := range event.PointerData {
if data, ok := g[ptr.Id]; !ok {
return fmt.Errorf("Not tracking pointer id: %v", ptr.Id)
} else if ptr.Orientation != 0.0 {
data.X = ptr.YPos
data.Y = ptr.XPos
} else {
data.X = ptr.XPos
data.Y = ptr.YPos
}
}
}
}
return nil
}
func (g GesturePointerState) Clear() {
for key := range g {
delete(g, key)
}
}
// Get the focus point of all the pointers that are down. If no pointers are
// down, this returns (-1, -1). If only a single pointer is down, this returns
// (X, Y) of the last known location. Otherwise, this averages the X and Y
// coordinates of all last known pointer locations.
func (g GesturePointerState) GetFocus() (focusX, focusY float64) {
if len(g) == 0 {
focusY, focusY = -1.0, -1.0
return
}
focusY, focusY = 0.0, 0.0
for _, state := range g {
focusX += state.X
focusY += state.Y
}
focusX /= float64(len(g))
focusY /= float64(len(g))
return
}
func NewGestureDetector(touchSlop int) *GestureDetector {
return &GestureDetector{
TouchSlop: touchSlop,
state: GestureStateNone,
touchSlopSquare: float64(touchSlop) * float64(touchSlop),
pointerState: make(GesturePointerState),
initialDowntime: 0,
}
}
func (detector *GestureDetector) OnTouchEvent(tracetime float64, event *IFMotionEventLog) (*TouchScreenEvent, error) {
if err := detector.pointerState.Update(event); err != nil {
return nil, err
}
// Events on non-primary pointers have the pointer id baked in with the
// action.
maskedAction := event.GetMaskedAction()
// Get the focus position of all pointers
focusX, focusY := detector.pointerState.GetFocus()
switch maskedAction {
case ACTION_POINTER_DOWN:
fallthrough
case ACTION_POINTER_UP:
fallthrough
case ACTION_DOWN:
detector.downFocusX = focusX
detector.lastFocusX = focusX
detector.downFocusY = focusY
detector.lastFocusY = focusY
if maskedAction == ACTION_DOWN {
// State change - start tap (or scroll). We won't
// send an event, though
detector.initialDowntime = event.Timestamp
detector.state = GestureStateTapping
}
case ACTION_MOVE:
{
scrollX := detector.lastFocusX - focusX
scrollY := detector.lastFocusY - focusY
switch detector.state {
default:
{
detector.Cancel()
return nil, errors.New("Received ACTION_MOVE while in empty state")
}
case GestureStateScrolling:
{
// Continue scrolling, if we actually moved.
if (math.Abs(scrollX) >= 1.0) || (math.Abs(scrollY) >= 1.0) {
// TODO: send scroll amounts
outEvent := detector.GenerateTouchScreenEvent(TouchScreenEventScroll, tracetime, event)
detector.lastFocusX = focusX
detector.lastFocusY = focusY
return outEvent, nil
}
}
case GestureStateTapping:
{
// Check if we're still close to the down event(s)
deltaX := focusX - detector.downFocusX
deltaY := focusY - detector.downFocusY
distance := (deltaX * deltaX) + (deltaY * deltaY)
if distance > detector.touchSlopSquare {
// Start scrolling
detector.state = GestureStateScrolling
outEvent := detector.GenerateTouchScreenEvent(TouchScreenEventScrollStart, tracetime, event)
detector.lastFocusX = focusX
detector.lastFocusY = focusY
return outEvent, nil
}
}
}
}
case ACTION_UP:
{
eventWhat := 0
switch detector.state {
default:
detector.Cancel()
return nil, errors.New("Received ACTION_UP while in empty state")
case GestureStateTapping:
eventWhat = TouchScreenEventTap
case GestureStateScrolling:
eventWhat = TouchScreenEventScrollEnd
}
outEvent := detector.GenerateTouchScreenEvent(eventWhat, tracetime, event)
detector.Cancel()
return outEvent, nil
}
case ACTION_CANCEL:
{
detector.Cancel()
}
}
return nil, nil
}
func (detector *GestureDetector) GenerateTouchScreenEvent(what int, tracetime float64,
event *IFMotionEventLog) *TouchScreenEvent {
return &TouchScreenEvent{
What: what,
Timestamp: event.Timestamp,
TraceTime: tracetime,
X: detector.lastFocusX,
Y: detector.lastFocusY,
}
}
func (detector *GestureDetector) State() int {
return detector.state
}
func (detector *GestureDetector) Cancel() {
detector.state = GestureStateNone
detector.downFocusX = 0.0
detector.lastFocusX = 0.0
detector.downFocusY = 0.0
detector.lastFocusY = 0.0
detector.pointerState.Clear()
}