blob: 19d82124b4ae4332d25487b839c900613e8b9e48 [file] [log] [blame]
/*
* 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