diff -r 000000000000 -r 2f259fa3e83a akntouchgesturefw/src/akntouchgesturefwflickrecognizer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/akntouchgesturefw/src/akntouchgesturefwflickrecognizer.cpp Tue Feb 02 01:00:49 2010 +0200 @@ -0,0 +1,565 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Flick touch gesture recognizer. +* +*/ + +#include "akntouchgesturefwdefs.h" +#include "akntouchgesturefwdragtracer.h" +#include "akntouchgesturefwevent.h" +#include "akntouchgesturefwflickrecognizer.h" +#include "akntouchgesturefwsettings.h" + +using namespace AknTouchGestureFw; + + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// Two-phased constructor. +// --------------------------------------------------------------------------- +// +CAknTouchGestureFwFlickRecognizer* CAknTouchGestureFwFlickRecognizer::NewL( + CAknTouchGestureFwRecognitionEngine& aEngine ) + { + CAknTouchGestureFwFlickRecognizer* self = + CAknTouchGestureFwFlickRecognizer::NewLC( aEngine ); + CleanupStack::Pop( self ); + return self; + } + + +// --------------------------------------------------------------------------- +// Two-phased constructor. +// --------------------------------------------------------------------------- +// +CAknTouchGestureFwFlickRecognizer* CAknTouchGestureFwFlickRecognizer::NewLC( + CAknTouchGestureFwRecognitionEngine& aEngine ) + { + CAknTouchGestureFwFlickRecognizer* self + = new ( ELeave ) CAknTouchGestureFwFlickRecognizer( aEngine ); + CleanupStack::PushL( self ); + self->ConstructL(); + return self; + } + + +// --------------------------------------------------------------------------- +// Destructor +// --------------------------------------------------------------------------- +// +CAknTouchGestureFwFlickRecognizer::~CAknTouchGestureFwFlickRecognizer() + { + delete iDragTracer; + iPoints.Close(); + } + + +// --------------------------------------------------------------------------- +// Returns the flick gesture group. +// --------------------------------------------------------------------------- +// +TAknTouchGestureFwGroup CAknTouchGestureFwFlickRecognizer::GestureGroup() const + { + return EAknTouchGestureFwGroupFlick; + } + + +// --------------------------------------------------------------------------- +// Cancels gesture recognition. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::CancelRecognizing() + { + Reset(); + } + + +// --------------------------------------------------------------------------- +// Handles single-touch pointer events. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::HandleSinglePointerEventL( + const TPointerEventData& aPointerData ) + { + switch ( aPointerData.iPointerEvent.iType ) + { + case TPointerEvent::EButton1Down: + { + StartSingleRecognizing( + aPointerData.iPointerEvent.iPosition, + aPointerData.iTimeStamp ); + break; + } + case TPointerEvent::EDrag: + { + if ( iStartOnSingleTouchDrag ) + { + // Start (via Reset) clears iStartOnSingleTouchDrag. + StartSingleRecognizing( + aPointerData.iPointerEvent.iPosition, + aPointerData.iTimeStamp ); + } + else + { + SingleRecognizeL( + aPointerData.iPointerEvent.iPosition, + aPointerData.iTimeStamp ); + } + break; + } + case TPointerEvent::EButton1Up: + { + CompleteSingleRecognizingL( + aPointerData.iPointerEvent.iPosition, + aPointerData.iTimeStamp, + EFalse ); + break; + } + default: + { + break; + } + } + } + + +// --------------------------------------------------------------------------- +// Handles multi-touch pointer events. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::HandleMultiPointerEventL( + const TPointerEventData& aPointerData, + const TPoint& aFirstPointerPosition, + const TPoint& /*aSecondPointerPosition*/ ) + { + switch ( aPointerData.iPointerEvent.iType ) + { + case TPointerEvent::EButton1Down: + { + // Completes single recognizing but does not check if + // flick is detected + CompleteSingleRecognizingL( + aFirstPointerPosition, + aPointerData.iTimeStamp, + ETrue ); + break; + } + case TPointerEvent::EDrag: + { + break; + } + case TPointerEvent::EButton1Up: + { + // Upon next single touch drag, start recognition. + // Current position is not necessarily accurate. + iStartOnSingleTouchDrag = ETrue; + break; + } + default: + { + break; + } + } + } + + +// --------------------------------------------------------------------------- +// C++ constructor. +// --------------------------------------------------------------------------- +// +CAknTouchGestureFwFlickRecognizer::CAknTouchGestureFwFlickRecognizer( + CAknTouchGestureFwRecognitionEngine& aEngine ) + : CAknTouchGestureFwBaseRecognizer( aEngine ) + { + } + + +// --------------------------------------------------------------------------- +// Second-phase constructor. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::ConstructL() + { + iDragTracer = CAknTouchGestureFwDragTracer::NewL(); + } + + +// --------------------------------------------------------------------------- +// Starts recognizing the flick gesture. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::StartSingleRecognizing( + const TPoint& aPointerPos, + const TTime& aTimeStamp ) + { + Reset(); + iDragTracer->Initialize( aPointerPos ); + AddPoint( aPointerPos, aTimeStamp ); + iDragArea.Start( aPointerPos ); + } + + +// --------------------------------------------------------------------------- +// Continues recognizing the flick gesture, called on drag events. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::SingleRecognizeL( + const TPoint& aPointerPos, + const TTime& aTimeStamp ) + { + AddPoint( aPointerPos, aTimeStamp ); + iThresholdExceeded = + iThresholdExceeded || iDragArea.Check( aPointerPos, DragThreshold() ); + } + + +// --------------------------------------------------------------------------- +// Ends the flick gesture recognition. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::CompleteSingleRecognizingL( + const TPoint& aPointerPos, + const TTime& aTimeStamp, + TBool aStartMultiTouch ) + { + // Try to detect flick if multi touch was not started + if ( !aStartMultiTouch ) + { + AddPoint( aPointerPos, aTimeStamp ); + } + + // Try to detect flick only if drag already detected and + // caller wants to detect flick. + if ( iThresholdExceeded && !aStartMultiTouch ) + { + TAknTouchGestureFwType flickGestureType( EAknTouchGestureFwUnknown ); + TPoint flickSpeed( 0, 0 ); + TBool flickDetected = DetermineFlickTypeAndSpeed( flickGestureType, + flickSpeed ); + + // Send flick event if speed threshold is exceeded + if( flickDetected ) + { + TPoint flickPosition( iPoints[ iPoints.Count() - 1 ].iPos ); + iPoints.Reset(); + SendFlickEventL( flickGestureType, flickPosition, flickSpeed ); + iFlickSpeed = TPoint( 0, 0 ); + } + } + + Reset(); + } + + +// --------------------------------------------------------------------------- +// Sends a flick gesture event to the observer. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::SendFlickEventL( + TAknTouchGestureFwType aGestureType, + const TPoint& aPosition, + const TPoint& aSpeed ) + { + TTouchFeedbackType feedbackType( FeedbackType( aGestureType ) ); + if ( feedbackType ) + { + switch ( aGestureType ) + { + case EAknTouchGestureFwFlickLeft: + case EAknTouchGestureFwFlickRight: + case EAknTouchGestureFwFlickUp: + case EAknTouchGestureFwFlickDown: + { + ImmediateFeedback( ETouchFeedbackFlick, feedbackType ); + break; + } + default: + { + break; + } + } + } + + TAknTouchGestureFwFlickEvent flick; + flick.SetType( aGestureType ); + flick.SetPosition( aPosition ); + flick.SetSpeed( aSpeed ); + SendGestureEventL( flick ); + } + + +// --------------------------------------------------------------------------- +// Resets recognizer's state. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::Reset() + { + iStartOnSingleTouchDrag = EFalse; + iPoints.Reset(); + iDragArea.Reset(); + iThresholdExceeded = EFalse; + } + + +// --------------------------------------------------------------------------- +// Adds a point to the sequences of points that forms the gesture. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::AddPoint( const TPoint& aPoint, + const TTime& aTimeStamp ) + { + // Don't add duplicate points, but update time of latest point + if ( iPoints.Count() > 0 && + iPoints[ iPoints.Count() - 1 ].iPos == aPoint ) + { + iPoints[ iPoints.Count() - 1 ].iTime = aTimeStamp; + return; + } + + TInt err = iPoints.Append( TPointEntry( aPoint, aTimeStamp ) ); + if ( err == KErrNone ) + { + // if buffer is full, remove oldest points + if ( iPoints.Count() > MaximumBufferLength() ) + { + iPoints.Remove( 0 ); + } + // Handle changes in direction. + // In practice, removes unnecessary points if direction changes + HandleDirectionChanges( aPoint ); + } + } + + +// --------------------------------------------------------------------------- +// Checks if the point array is empty. +// --------------------------------------------------------------------------- +// +TBool CAknTouchGestureFwFlickRecognizer::IsEmpty() const + { + return iPoints.Count() == 0; + } + + +// --------------------------------------------------------------------------- +// Determines the direction and speed of a flick gesture. +// --------------------------------------------------------------------------- +// +TBool CAknTouchGestureFwFlickRecognizer::DetermineFlickTypeAndSpeed( + TAknTouchGestureFwType& aFlickGestureType, + TPoint& aSpeed ) + { + // Calculate distance and speed based on first and last + // point in the pointer array. + + TPoint distance; + TPoint speed; + if ( !CalculateFlickParameters( distance, speed ) ) + { + return EFalse; + } + + // If speed is over threshold, this movement is assumed to be flick + TInt speedThreshold( FlickSpeedThreshold() ); + if ( Abs( speed.iX ) >= speedThreshold || + Abs ( speed.iY ) >= speedThreshold ) + { + aFlickGestureType = EAknTouchGestureFwUnknown; + + // Flick detected - determine its direction. + if ( Abs( distance.iX ) > Abs( distance. iY ) ) + { + // Right or left + if ( distance.iX > 0 ) + { + aFlickGestureType = EAknTouchGestureFwFlickRight; + } + else + { + aFlickGestureType = EAknTouchGestureFwFlickLeft; + } + } + else + { + // Up or down + if ( distance.iY > 0 ) + { + aFlickGestureType = EAknTouchGestureFwFlickDown; + } + else + { + aFlickGestureType = EAknTouchGestureFwFlickUp; + } + } + + // Set flick speed. + aSpeed = speed; + + return ETrue; + } + + return EFalse; + } + + +// --------------------------------------------------------------------------- +// Calculates the flick distance and speed. +// --------------------------------------------------------------------------- +// +TBool CAknTouchGestureFwFlickRecognizer::CalculateFlickParameters( + TPoint& aDistance, + TPoint& aSpeed ) + { + // Check that there are at least 2 points to calculate parameters. + TInt count = iPoints.Count(); + if ( count < 2 ) + { + return EFalse; + } + + // Flick detection is done using latest points added during last + // n seconds. + // Ignore all points that are older than n seconds + TTime endTime = iPoints[ count - 1 ].iTime; + TInt startIndex = -1; + + for ( TInt i = 0; i < count - 1; i++ ) + { + TInt elapsedTime = Elapsed( iPoints[ i ].iTime, endTime ).Int(); + + if ( elapsedTime < FlickDetectionTime() ) + { + startIndex = i; + break; + } + } + + // If there is no new start point (within n seconds) found, + // it means that time between two latest points have been over this time + // and movement can't be flick + if ( startIndex == -1 ) + { + return EFalse; + } + + // Calculate distance and speed based on new start- and endpoints + aDistance = iPoints[ count - 1 ].iPos - iPoints[ startIndex ].iPos; + + TInt elapsedTime = Elapsed( iPoints[ startIndex ].iTime, + iPoints[ count - 1 ].iTime ).Int(); + + if ( elapsedTime <= 0 ) + { + return EFalse; + } + + aSpeed.iX = KMicroSecondsInSecond * aDistance.iX / elapsedTime; + aSpeed.iY = KMicroSecondsInSecond * aDistance.iY / elapsedTime; + + return ETrue; + } + + +// --------------------------------------------------------------------------- +// Called when drag direction changes to clear the previous points. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::HandleDirectionChanges( + const TPoint& aPosition ) + { + // Sensitivity tells how many points are needed to reversed direction + // to detect change in direction. + TInt sensitivity( DirectionChangeSensitivity() ); + + if ( iDragTracer->IsDirectionChanged( aPosition, sensitivity ) ) + { + // Remove all previous points so that latest points which + // are to reversed direction + point where direction changes are left. + RemovePreviousPoints( sensitivity + 1 ); + } + } + + +// --------------------------------------------------------------------------- +// Calculates the elapsed time from the specified start time and end time. +// static +// --------------------------------------------------------------------------- +// +TTimeIntervalMicroSeconds32 CAknTouchGestureFwFlickRecognizer::Elapsed( + const TTime& aStartTime, + const TTime& aEndTime ) + { + return aEndTime.MicroSecondsFrom( aStartTime ).Int64(); + } + + +// --------------------------------------------------------------------------- +// Removes the specified amount of stored points from the array. +// --------------------------------------------------------------------------- +// +void CAknTouchGestureFwFlickRecognizer::RemovePreviousPoints( + TInt aNumberOfPoints ) + { + TInt count = iPoints.Count(); + if ( count - aNumberOfPoints > 0 ) + { + for ( TInt i = 0; i < count - aNumberOfPoints; i++ ) + { + iPoints.Remove( 0 ); + } + } + } + + +// --------------------------------------------------------------------------- +// CAknTouchGestureFwFlickRecognizer::MaximumBufferLength +// --------------------------------------------------------------------------- +// +TInt CAknTouchGestureFwFlickRecognizer::MaximumBufferLength() const + { + return Settings().FlickBuffer(); + } + + +// --------------------------------------------------------------------------- +// Returns the current value of flick speed threshold setting. +// --------------------------------------------------------------------------- +// +TInt CAknTouchGestureFwFlickRecognizer::FlickSpeedThreshold() const + { + return Settings().FlickSpeedThreshold(); + } + + +// --------------------------------------------------------------------------- +// Returns the current value of flick detection time setting. +// --------------------------------------------------------------------------- +// +TInt CAknTouchGestureFwFlickRecognizer::FlickDetectionTime() const + { + return Settings().FlickDetectionTime() * KMicroSecondsInMilliSecond; + } + + +// --------------------------------------------------------------------------- +// Returns the current value of direction change sensitivity setting. +// --------------------------------------------------------------------------- +// +TInt CAknTouchGestureFwFlickRecognizer::DirectionChangeSensitivity() const + { + return Settings().FlickChangeSensitivity(); + } + +// End of File