Fix memory leak
[platform/core/uifw/dali-core.git] / dali / internal / event / events / long-press-gesture / long-press-gesture-recognizer.cpp
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
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
18 // CLASS HEADER
19 #include <dali/internal/event/events/long-press-gesture/long-press-gesture-recognizer.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 #include <dali/public-api/events/touch-point.h>
25 #include <dali/public-api/math/vector2.h>
26
27 #include <dali/internal/event/common/thread-local-storage.h>
28 #include <dali/internal/event/events/gesture-requests.h>
29 #include <dali/integration-api/events/touch-event-integ.h>
30 #include <dali/integration-api/platform-abstraction.h>
31
32 namespace Dali
33 {
34
35 namespace Internal
36 {
37
38 namespace
39 {
40 // TODO: Set these according to DPI
41 const float MAXIMUM_MOTION_ALLOWED = 60.0f;
42
43 } // unnamed namespace
44
45 LongPressGestureRecognizer::LongPressGestureRecognizer(Observer& observer, Vector2 screenSize, const LongPressGestureRequest& request, uint32_t minimumHoldingTime )
46 : GestureRecognizer( screenSize, Gesture::LongPress ),
47   mObserver( observer ),
48   mState( Clear ),
49   mMinimumTouchesRequired( request.minTouches ),
50   mMaximumTouchesRequired( request.maxTouches ),
51   mTouchTime( 0 ),
52   mTimerId( 0 ),
53   mMinimumHoldingTime( minimumHoldingTime )
54 {
55 }
56
57 LongPressGestureRecognizer::~LongPressGestureRecognizer()
58 {
59   if( mTimerId != 0 && ThreadLocalStorage::Created() )
60   {
61     Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
62     platformAbstraction.CancelTimer(mTimerId);
63   }
64 }
65
66 void LongPressGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
67 {
68   unsigned int pointCount( event.GetPointCount() );
69   Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
70
71   switch (mState)
72   {
73     // Clear: Wait till one point touches the screen before starting timer.
74     case Clear:
75     {
76       const Integration::Point& point = event.points[0];
77
78       if ( point.GetState() == PointState::DOWN )
79       {
80         mTouchPositions.clear();
81         mTouchPositions[point.GetDeviceId()] = point.GetScreenPosition();
82
83         mTouchTime = event.time;
84
85         if( mTimerId != 0 )
86         {
87           platformAbstraction.CancelTimer(mTimerId);
88         }
89         mTimerId = platformAbstraction.StartTimer( mMinimumHoldingTime, MakeCallback( this, &LongPressGestureRecognizer::TimerCallback ) );
90
91         // A long press gesture may be possible, tell Core about this and change state to Touched.
92         mState = Touched;
93         EmitGesture( Gesture::Possible );
94       }
95
96       break;
97     }
98
99     // Touched: Monitor movement and addition/removal of points.
100     case Touched:
101     {
102       if (pointCount > mMaximumTouchesRequired)
103       {
104         // A long press did not occur, tell Core that it was cancelled and change state to Failed.
105         EmitGesture( Gesture::Cancelled );
106         mTouchPositions.clear();
107         platformAbstraction.CancelTimer(mTimerId);
108         mTimerId = 0;
109         mState = Failed;
110         break;
111       }
112
113       bool endLoop(false);
114
115       for ( Integration::PointContainerConstIterator iter = event.points.begin(), endIter = event.points.end();
116            iter != endIter && !endLoop; ++iter)
117       {
118         switch( iter->GetState() )
119         {
120           // add point.
121           case PointState::DOWN:
122           {
123             mTouchPositions[iter->GetDeviceId()] = iter->GetScreenPosition();
124             break;
125           }
126
127           // remove point.
128           case PointState::UP:
129           case PointState::INTERRUPTED:
130           {
131             // System has interrupted us, long press is not possible, inform Core
132             EmitGesture( Gesture::Cancelled );
133             mTouchPositions.clear();
134             platformAbstraction.CancelTimer(mTimerId);
135             mTimerId = 0;
136             mState = ( pointCount == 1 ) ? Clear : Failed; // Change state to Clear if only one point, Failed otherwise.
137             endLoop = true;
138             break;
139           }
140
141           case PointState::MOTION:
142           {
143             const Vector2 touchPosition( mTouchPositions[iter->GetDeviceId()] - iter->GetScreenPosition() );
144             float distanceSquared = touchPosition.LengthSquared();
145
146             if (distanceSquared > ( MAXIMUM_MOTION_ALLOWED * MAXIMUM_MOTION_ALLOWED ) )
147             {
148               // We have moved more than the allowable motion for a long press gesture. Inform Core and change state to Failed.
149               EmitGesture( Gesture::Cancelled );
150               platformAbstraction.CancelTimer(mTimerId);
151               mTimerId = 0;
152               mState = Failed;
153               endLoop = true;
154             }
155             break;
156           }
157
158           case PointState::STATIONARY:
159           case PointState::LEAVE:
160           {
161             break;
162           }
163         }
164       }
165       break;
166     }
167
168     // Failed/Finished: Monitor the touches, waiting for all touches to be released.
169     case Failed:
170     case Finished:
171     {
172       // eventually the final touch point will be removed, marking the end of this gesture.
173       if ( pointCount == 1 )
174       {
175         PointState::Type primaryPointState = event.points[0].GetState();
176
177         if ( (primaryPointState == PointState::UP) || (primaryPointState == PointState::INTERRUPTED) )
178         {
179           if(mState == Finished)
180           {
181             // When the last touch point is lifted, we should inform the Core that the Long press has finished.
182             EmitGesture(Gesture::Finished);
183           }
184           mTouchPositions.clear();
185           mState = Clear; // Reset state to clear when last touch point is lifted.
186         }
187       }
188       break;
189     }
190   }
191 }
192
193 void LongPressGestureRecognizer::Update(const GestureRequest& request)
194 {
195   const LongPressGestureRequest& longPress = static_cast<const LongPressGestureRequest&>(request);
196
197   mMinimumTouchesRequired = longPress.minTouches;
198   mMaximumTouchesRequired = longPress.maxTouches;
199 }
200
201 void LongPressGestureRecognizer::SetMinimumHoldingTime( uint32_t time )
202 {
203   mMinimumHoldingTime = time;
204 }
205
206
207 bool LongPressGestureRecognizer::TimerCallback()
208 {
209   EmitGesture(Gesture::Started);
210
211   mState = Finished;
212
213   mTimerId = 0;
214
215   return false;
216 }
217
218 void LongPressGestureRecognizer::EmitGesture(Gesture::State state)
219 {
220   unsigned int touchPoints ( static_cast<unsigned int>( mTouchPositions.size() ) );
221
222   // We should tell Core about the Possible and Cancelled states regardless of whether we have satisfied long press requirements.
223   if ( (state == Gesture::Possible) ||
224        (state == Gesture::Cancelled) ||
225        (touchPoints >= mMinimumTouchesRequired) )
226   {
227     LongPressGestureEvent longPress( state );
228     longPress.numberOfTouches = touchPoints;
229
230     for (std::map<int, Vector2>::iterator iter = mTouchPositions.begin(), endIter = mTouchPositions.end();
231          iter != endIter; ++iter)
232     {
233       longPress.point += iter->second;
234     }
235     longPress.point /= static_cast<float>( touchPoints );
236
237     longPress.time = mTouchTime;
238     if ( state != Gesture::Possible )
239     {
240       longPress.time += mMinimumHoldingTime;
241     }
242
243     if( mScene )
244     {
245       // Create another handle so the recognizer cannot be destroyed during process function
246       GestureRecognizerPtr recognizerHandle = this;
247
248       mObserver.Process(*mScene, longPress);
249     }
250   }
251 }
252
253 } // namespace Internal
254
255 } // namespace Dali