| /* |
| * Copyright 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "gestureDetector.h" |
| |
| //-------------------------------------------------------------------------------- |
| // gestureDetector.cpp |
| //-------------------------------------------------------------------------------- |
| namespace ndk_helper |
| { |
| |
| //-------------------------------------------------------------------------------- |
| // includes |
| //-------------------------------------------------------------------------------- |
| |
| //-------------------------------------------------------------------------------- |
| // GestureDetector |
| //-------------------------------------------------------------------------------- |
| GestureDetector::GestureDetector() |
| { |
| dp_factor_ = 1.f; |
| } |
| |
| void GestureDetector::SetConfiguration( AConfiguration* config ) |
| { |
| dp_factor_ = 160.f / AConfiguration_getDensity( config ); |
| } |
| |
| //-------------------------------------------------------------------------------- |
| // TapDetector |
| //-------------------------------------------------------------------------------- |
| GESTURE_STATE TapDetector::Detect( const AInputEvent* motion_event ) |
| { |
| if( AMotionEvent_getPointerCount( motion_event ) > 1 ) |
| { |
| //Only support single touch |
| return false; |
| } |
| |
| int32_t action = AMotionEvent_getAction( motion_event ); |
| unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; |
| switch( flags ) |
| { |
| case AMOTION_EVENT_ACTION_DOWN: |
| down_pointer_id_ = AMotionEvent_getPointerId( motion_event, 0 ); |
| down_x_ = AMotionEvent_getX( motion_event, 0 ); |
| down_y_ = AMotionEvent_getY( motion_event, 0 ); |
| break; |
| case AMOTION_EVENT_ACTION_UP: |
| { |
| int64_t eventTime = AMotionEvent_getEventTime( motion_event ); |
| int64_t downTime = AMotionEvent_getDownTime( motion_event ); |
| if( eventTime - downTime <= TAP_TIMEOUT ) |
| { |
| if( down_pointer_id_ == AMotionEvent_getPointerId( motion_event, 0 ) ) |
| { |
| float x = AMotionEvent_getX( motion_event, 0 ) - down_x_; |
| float y = AMotionEvent_getY( motion_event, 0 ) - down_y_; |
| if( x * x + y * y < TOUCH_SLOP * TOUCH_SLOP * dp_factor_ ) |
| { |
| LOGI( "TapDetector: Tap detected" ); |
| return GESTURE_STATE_ACTION; |
| } |
| } |
| } |
| break; |
| } |
| } |
| return GESTURE_STATE_NONE; |
| } |
| |
| //-------------------------------------------------------------------------------- |
| // DoubletapDetector |
| //-------------------------------------------------------------------------------- |
| GESTURE_STATE DoubletapDetector::Detect( const AInputEvent* motion_event ) |
| { |
| if( AMotionEvent_getPointerCount( motion_event ) > 1 ) |
| { |
| //Only support single double tap |
| return false; |
| } |
| |
| bool tap_detected = tap_detector_.Detect( motion_event ); |
| |
| int32_t action = AMotionEvent_getAction( motion_event ); |
| unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; |
| switch( flags ) |
| { |
| case AMOTION_EVENT_ACTION_DOWN: |
| { |
| int64_t eventTime = AMotionEvent_getEventTime( motion_event ); |
| if( eventTime - last_tap_time_ <= DOUBLE_TAP_TIMEOUT ) |
| { |
| float x = AMotionEvent_getX( motion_event, 0 ) - last_tap_x_; |
| float y = AMotionEvent_getY( motion_event, 0 ) - last_tap_y_; |
| if( x * x + y * y < DOUBLE_TAP_SLOP * DOUBLE_TAP_SLOP * dp_factor_ ) |
| { |
| LOGI( "DoubletapDetector: Doubletap detected" ); |
| return GESTURE_STATE_ACTION; |
| } |
| } |
| break; |
| } |
| case AMOTION_EVENT_ACTION_UP: |
| if( tap_detected ) |
| { |
| last_tap_time_ = AMotionEvent_getEventTime( motion_event ); |
| last_tap_x_ = AMotionEvent_getX( motion_event, 0 ); |
| last_tap_y_ = AMotionEvent_getY( motion_event, 0 ); |
| } |
| break; |
| } |
| return GESTURE_STATE_NONE; |
| } |
| |
| void DoubletapDetector::SetConfiguration( AConfiguration* config ) |
| { |
| dp_factor_ = 160.f / AConfiguration_getDensity( config ); |
| tap_detector_.SetConfiguration( config ); |
| } |
| |
| //-------------------------------------------------------------------------------- |
| // PinchDetector |
| //-------------------------------------------------------------------------------- |
| |
| int32_t PinchDetector::FindIndex( const AInputEvent* event, int32_t id ) |
| { |
| int32_t count = AMotionEvent_getPointerCount( event ); |
| for( uint32_t i = 0; i < count; ++i ) |
| { |
| if( id == AMotionEvent_getPointerId( event, i ) ) |
| return i; |
| } |
| return -1; |
| } |
| |
| GESTURE_STATE PinchDetector::Detect( const AInputEvent* event ) |
| { |
| GESTURE_STATE ret = GESTURE_STATE_NONE; |
| int32_t action = AMotionEvent_getAction( event ); |
| uint32_t flags = action & AMOTION_EVENT_ACTION_MASK; |
| event_ = event; |
| |
| int32_t count = AMotionEvent_getPointerCount( event ); |
| switch( flags ) |
| { |
| case AMOTION_EVENT_ACTION_DOWN: |
| vec_pointers_.push_back( AMotionEvent_getPointerId( event, 0 ) ); |
| break; |
| case AMOTION_EVENT_ACTION_POINTER_DOWN: |
| { |
| int32_t iIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) |
| >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; |
| vec_pointers_.push_back( AMotionEvent_getPointerId( event, iIndex ) ); |
| if( count == 2 ) |
| { |
| //Start new pinch |
| ret = GESTURE_STATE_START; |
| } |
| } |
| break; |
| case AMOTION_EVENT_ACTION_UP: |
| vec_pointers_.pop_back(); |
| break; |
| case AMOTION_EVENT_ACTION_POINTER_UP: |
| { |
| int32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) |
| >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; |
| int32_t released_pointer_id = AMotionEvent_getPointerId( event, index ); |
| |
| std::vector<int32_t>::iterator it = vec_pointers_.begin(); |
| std::vector<int32_t>::iterator it_end = vec_pointers_.end(); |
| int32_t i = 0; |
| for( ; it != it_end; ++it, ++i ) |
| { |
| if( *it == released_pointer_id ) |
| { |
| vec_pointers_.erase( it ); |
| break; |
| } |
| } |
| |
| if( i <= 1 ) |
| { |
| //Reset pinch or drag |
| if( count != 2 ) |
| { |
| //Start new pinch |
| ret = GESTURE_STATE_START | GESTURE_STATE_END; |
| } |
| } |
| } |
| break; |
| case AMOTION_EVENT_ACTION_MOVE: |
| switch( count ) |
| { |
| case 1: |
| break; |
| default: |
| //Multi touch |
| ret = GESTURE_STATE_MOVE; |
| break; |
| } |
| break; |
| case AMOTION_EVENT_ACTION_CANCEL: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| bool PinchDetector::GetPointers( Vec2& v1, Vec2& v2 ) |
| { |
| if( vec_pointers_.size() < 2 ) |
| return false; |
| |
| int32_t index = FindIndex( event_, vec_pointers_[0] ); |
| if( index == -1 ) |
| return false; |
| |
| float x = AMotionEvent_getX( event_, index ); |
| float y = AMotionEvent_getY( event_, index ); |
| |
| index = FindIndex( event_, vec_pointers_[1] ); |
| if( index == -1 ) |
| return false; |
| |
| float x2 = AMotionEvent_getX( event_, index ); |
| float y2 = AMotionEvent_getY( event_, index ); |
| |
| v1 = Vec2( x, y ); |
| v2 = Vec2( x2, y2 ); |
| |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------------- |
| // DragDetector |
| //-------------------------------------------------------------------------------- |
| |
| int32_t DragDetector::FindIndex( const AInputEvent* event, int32_t id ) |
| { |
| int32_t count = AMotionEvent_getPointerCount( event ); |
| for( uint32_t i = 0; i < count; ++i ) |
| { |
| if( id == AMotionEvent_getPointerId( event, i ) ) |
| return i; |
| } |
| return -1; |
| } |
| |
| GESTURE_STATE DragDetector::Detect( const AInputEvent* event ) |
| { |
| GESTURE_STATE ret = GESTURE_STATE_NONE; |
| int32_t action = AMotionEvent_getAction( event ); |
| int32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) |
| >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; |
| uint32_t flags = action & AMOTION_EVENT_ACTION_MASK; |
| event_ = event; |
| |
| int32_t count = AMotionEvent_getPointerCount( event ); |
| switch( flags ) |
| { |
| case AMOTION_EVENT_ACTION_DOWN: |
| vec_pointers_.push_back( AMotionEvent_getPointerId( event, 0 ) ); |
| ret = GESTURE_STATE_START; |
| break; |
| case AMOTION_EVENT_ACTION_POINTER_DOWN: |
| vec_pointers_.push_back( AMotionEvent_getPointerId( event, index ) ); |
| break; |
| case AMOTION_EVENT_ACTION_UP: |
| vec_pointers_.pop_back(); |
| ret = GESTURE_STATE_END; |
| break; |
| case AMOTION_EVENT_ACTION_POINTER_UP: |
| { |
| int32_t released_pointer_id = AMotionEvent_getPointerId( event, index ); |
| |
| std::vector<int32_t>::iterator it = vec_pointers_.begin(); |
| std::vector<int32_t>::iterator it_end = vec_pointers_.end(); |
| int32_t i = 0; |
| for( ; it != it_end; ++it, ++i ) |
| { |
| if( *it == released_pointer_id ) |
| { |
| vec_pointers_.erase( it ); |
| break; |
| } |
| } |
| |
| if( i <= 1 ) |
| { |
| //Reset pinch or drag |
| if( count == 2 ) |
| { |
| ret = GESTURE_STATE_START; |
| } |
| } |
| break; |
| } |
| case AMOTION_EVENT_ACTION_MOVE: |
| switch( count ) |
| { |
| case 1: |
| //Drag |
| ret = GESTURE_STATE_MOVE; |
| break; |
| default: |
| break; |
| } |
| break; |
| case AMOTION_EVENT_ACTION_CANCEL: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| bool DragDetector::GetPointer( Vec2& v ) |
| { |
| if( vec_pointers_.size() < 1 ) |
| return false; |
| |
| int32_t iIndex = FindIndex( event_, vec_pointers_[0] ); |
| if( iIndex == -1 ) |
| return false; |
| |
| float x = AMotionEvent_getX( event_, iIndex ); |
| float y = AMotionEvent_getY( event_, iIndex ); |
| |
| v = Vec2( x, y ); |
| |
| return true; |
| } |
| |
| } //namespace ndkHelper |
| |