1/*
2 * Copyright 2013 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#include "gestureDetector.h"
18
19//--------------------------------------------------------------------------------
20// gestureDetector.cpp
21//--------------------------------------------------------------------------------
22namespace ndk_helper
23{
24
25//--------------------------------------------------------------------------------
26// includes
27//--------------------------------------------------------------------------------
28
29//--------------------------------------------------------------------------------
30// GestureDetector
31//--------------------------------------------------------------------------------
32GestureDetector::GestureDetector()
33{
34    dp_factor_ = 1.f;
35}
36
37void GestureDetector::SetConfiguration( AConfiguration* config )
38{
39    dp_factor_ = 160.f / AConfiguration_getDensity( config );
40}
41
42//--------------------------------------------------------------------------------
43// TapDetector
44//--------------------------------------------------------------------------------
45GESTURE_STATE TapDetector::Detect( const AInputEvent* motion_event )
46{
47    if( AMotionEvent_getPointerCount( motion_event ) > 1 )
48    {
49        //Only support single touch
50        return false;
51    }
52
53    int32_t action = AMotionEvent_getAction( motion_event );
54    unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
55    switch( flags )
56    {
57    case AMOTION_EVENT_ACTION_DOWN:
58        down_pointer_id_ = AMotionEvent_getPointerId( motion_event, 0 );
59        down_x_ = AMotionEvent_getX( motion_event, 0 );
60        down_y_ = AMotionEvent_getY( motion_event, 0 );
61        break;
62    case AMOTION_EVENT_ACTION_UP:
63    {
64        int64_t eventTime = AMotionEvent_getEventTime( motion_event );
65        int64_t downTime = AMotionEvent_getDownTime( motion_event );
66        if( eventTime - downTime <= TAP_TIMEOUT )
67        {
68            if( down_pointer_id_ == AMotionEvent_getPointerId( motion_event, 0 ) )
69            {
70                float x = AMotionEvent_getX( motion_event, 0 ) - down_x_;
71                float y = AMotionEvent_getY( motion_event, 0 ) - down_y_;
72                if( x * x + y * y < TOUCH_SLOP * TOUCH_SLOP * dp_factor_ )
73                {
74                    LOGI( "TapDetector: Tap detected" );
75                    return GESTURE_STATE_ACTION;
76                }
77            }
78        }
79        break;
80    }
81    }
82    return GESTURE_STATE_NONE;
83}
84
85//--------------------------------------------------------------------------------
86// DoubletapDetector
87//--------------------------------------------------------------------------------
88GESTURE_STATE DoubletapDetector::Detect( const AInputEvent* motion_event )
89{
90    if( AMotionEvent_getPointerCount( motion_event ) > 1 )
91    {
92        //Only support single double tap
93        return false;
94    }
95
96    bool tap_detected = tap_detector_.Detect( motion_event );
97
98    int32_t action = AMotionEvent_getAction( motion_event );
99    unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
100    switch( flags )
101    {
102    case AMOTION_EVENT_ACTION_DOWN:
103    {
104        int64_t eventTime = AMotionEvent_getEventTime( motion_event );
105        if( eventTime - last_tap_time_ <= DOUBLE_TAP_TIMEOUT )
106        {
107            float x = AMotionEvent_getX( motion_event, 0 ) - last_tap_x_;
108            float y = AMotionEvent_getY( motion_event, 0 ) - last_tap_y_;
109            if( x * x + y * y < DOUBLE_TAP_SLOP * DOUBLE_TAP_SLOP * dp_factor_ )
110            {
111                LOGI( "DoubletapDetector: Doubletap detected" );
112                return GESTURE_STATE_ACTION;
113            }
114        }
115        break;
116    }
117    case AMOTION_EVENT_ACTION_UP:
118        if( tap_detected )
119        {
120            last_tap_time_ = AMotionEvent_getEventTime( motion_event );
121            last_tap_x_ = AMotionEvent_getX( motion_event, 0 );
122            last_tap_y_ = AMotionEvent_getY( motion_event, 0 );
123        }
124        break;
125    }
126    return GESTURE_STATE_NONE;
127}
128
129void DoubletapDetector::SetConfiguration( AConfiguration* config )
130{
131    dp_factor_ = 160.f / AConfiguration_getDensity( config );
132    tap_detector_.SetConfiguration( config );
133}
134
135//--------------------------------------------------------------------------------
136// PinchDetector
137//--------------------------------------------------------------------------------
138
139int32_t PinchDetector::FindIndex( const AInputEvent* event, int32_t id )
140{
141    int32_t count = AMotionEvent_getPointerCount( event );
142    for( uint32_t i = 0; i < count; ++i )
143    {
144        if( id == AMotionEvent_getPointerId( event, i ) )
145            return i;
146    }
147    return -1;
148}
149
150GESTURE_STATE PinchDetector::Detect( const AInputEvent* event )
151{
152    GESTURE_STATE ret = GESTURE_STATE_NONE;
153    int32_t action = AMotionEvent_getAction( event );
154    uint32_t flags = action & AMOTION_EVENT_ACTION_MASK;
155    event_ = event;
156
157    int32_t count = AMotionEvent_getPointerCount( event );
158    switch( flags )
159    {
160    case AMOTION_EVENT_ACTION_DOWN:
161        vec_pointers_.push_back( AMotionEvent_getPointerId( event, 0 ) );
162        break;
163    case AMOTION_EVENT_ACTION_POINTER_DOWN:
164    {
165        int32_t iIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
166                >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
167        vec_pointers_.push_back( AMotionEvent_getPointerId( event, iIndex ) );
168        if( count == 2 )
169        {
170            //Start new pinch
171            ret = GESTURE_STATE_START;
172        }
173    }
174        break;
175    case AMOTION_EVENT_ACTION_UP:
176        vec_pointers_.pop_back();
177        break;
178    case AMOTION_EVENT_ACTION_POINTER_UP:
179    {
180        int32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
181                >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
182        int32_t released_pointer_id = AMotionEvent_getPointerId( event, index );
183
184        std::vector<int32_t>::iterator it = vec_pointers_.begin();
185        std::vector<int32_t>::iterator it_end = vec_pointers_.end();
186        int32_t i = 0;
187        for( ; it != it_end; ++it, ++i )
188        {
189            if( *it == released_pointer_id )
190            {
191                vec_pointers_.erase( it );
192                break;
193            }
194        }
195
196        if( i <= 1 )
197        {
198            //Reset pinch or drag
199            if( count != 2 )
200            {
201                //Start new pinch
202                ret = GESTURE_STATE_START | GESTURE_STATE_END;
203            }
204        }
205    }
206        break;
207    case AMOTION_EVENT_ACTION_MOVE:
208        switch( count )
209        {
210        case 1:
211            break;
212        default:
213            //Multi touch
214            ret = GESTURE_STATE_MOVE;
215            break;
216        }
217        break;
218    case AMOTION_EVENT_ACTION_CANCEL:
219        break;
220    }
221
222    return ret;
223}
224
225bool PinchDetector::GetPointers( Vec2& v1, Vec2& v2 )
226{
227    if( vec_pointers_.size() < 2 )
228        return false;
229
230    int32_t index = FindIndex( event_, vec_pointers_[0] );
231    if( index == -1 )
232        return false;
233
234    float x = AMotionEvent_getX( event_, index );
235    float y = AMotionEvent_getY( event_, index );
236
237    index = FindIndex( event_, vec_pointers_[1] );
238    if( index == -1 )
239        return false;
240
241    float x2 = AMotionEvent_getX( event_, index );
242    float y2 = AMotionEvent_getY( event_, index );
243
244    v1 = Vec2( x, y );
245    v2 = Vec2( x2, y2 );
246
247    return true;
248}
249
250//--------------------------------------------------------------------------------
251// DragDetector
252//--------------------------------------------------------------------------------
253
254int32_t DragDetector::FindIndex( const AInputEvent* event, int32_t id )
255{
256    int32_t count = AMotionEvent_getPointerCount( event );
257    for( uint32_t i = 0; i < count; ++i )
258    {
259        if( id == AMotionEvent_getPointerId( event, i ) )
260            return i;
261    }
262    return -1;
263}
264
265GESTURE_STATE DragDetector::Detect( const AInputEvent* event )
266{
267    GESTURE_STATE ret = GESTURE_STATE_NONE;
268    int32_t action = AMotionEvent_getAction( event );
269    int32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
270            >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
271    uint32_t flags = action & AMOTION_EVENT_ACTION_MASK;
272    event_ = event;
273
274    int32_t count = AMotionEvent_getPointerCount( event );
275    switch( flags )
276    {
277    case AMOTION_EVENT_ACTION_DOWN:
278        vec_pointers_.push_back( AMotionEvent_getPointerId( event, 0 ) );
279        ret = GESTURE_STATE_START;
280        break;
281    case AMOTION_EVENT_ACTION_POINTER_DOWN:
282        vec_pointers_.push_back( AMotionEvent_getPointerId( event, index ) );
283        break;
284    case AMOTION_EVENT_ACTION_UP:
285        vec_pointers_.pop_back();
286        ret = GESTURE_STATE_END;
287        break;
288    case AMOTION_EVENT_ACTION_POINTER_UP:
289    {
290        int32_t released_pointer_id = AMotionEvent_getPointerId( event, index );
291
292        std::vector<int32_t>::iterator it = vec_pointers_.begin();
293        std::vector<int32_t>::iterator it_end = vec_pointers_.end();
294        int32_t i = 0;
295        for( ; it != it_end; ++it, ++i )
296        {
297            if( *it == released_pointer_id )
298            {
299                vec_pointers_.erase( it );
300                break;
301            }
302        }
303
304        if( i <= 1 )
305        {
306            //Reset pinch or drag
307            if( count == 2 )
308            {
309                ret = GESTURE_STATE_START;
310            }
311        }
312        break;
313    }
314    case AMOTION_EVENT_ACTION_MOVE:
315        switch( count )
316        {
317        case 1:
318            //Drag
319            ret = GESTURE_STATE_MOVE;
320            break;
321        default:
322            break;
323        }
324        break;
325    case AMOTION_EVENT_ACTION_CANCEL:
326        break;
327    }
328
329    return ret;
330}
331
332bool DragDetector::GetPointer( Vec2& v )
333{
334    if( vec_pointers_.size() < 1 )
335        return false;
336
337    int32_t iIndex = FindIndex( event_, vec_pointers_[0] );
338    if( iIndex == -1 )
339        return false;
340
341    float x = AMotionEvent_getX( event_, iIndex );
342    float y = AMotionEvent_getY( event_, iIndex );
343
344    v = Vec2( x, y );
345
346    return true;
347}
348
349}   //namespace ndkHelper
350
351