dali-adaptor internal folder refactoring
[platform/core/uifw/dali-adaptor.git] / dali / internal / input / common / long-press-gesture-detector.cpp
1 /*
2  * Copyright (c) 2018 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/input/common/long-press-gesture-detector.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/integration-api/events/gesture-requests.h>
28 #include <dali/integration-api/events/long-press-gesture-event.h>
29 #include <dali/integration-api/events/touch-event-integ.h>
30
31 #include <dali/internal/system/common/system-settings.h>
32
33 // INTERNAL INCLUDES
34 #include <dali/internal/system/common/core-event-interface.h>
35
36 namespace Dali
37 {
38
39 namespace Internal
40 {
41
42 namespace Adaptor
43 {
44
45 namespace
46 {
47 // TODO: Set these according to DPI
48 const float MAXIMUM_MOTION_ALLOWED = 60.0f;
49 // TODO: Set this time according to system setting (vconf)
50 const unsigned long LONG_PRESS_TIME = 500u;
51 } // unnamed namespace
52
53 LongPressGestureDetector::LongPressGestureDetector(CoreEventInterface& coreEventInterface, Vector2 screenSize, const Integration::LongPressGestureRequest& request)
54 : GestureDetector(screenSize, Gesture::LongPress),
55   mCoreEventInterface(coreEventInterface),
56   mState(Clear),
57   mMinimumTouchesRequired(request.minTouches),
58   mMaximumTouchesRequired(request.maxTouches),
59   mTouchTime(0),
60   mTimerSlot( this )
61 {
62   mTimer = Dali::Timer::New(GetSystemValue());
63   mTimer.TickSignal().Connect( mTimerSlot, &LongPressGestureDetector::TimerCallback );
64 }
65
66 LongPressGestureDetector::~LongPressGestureDetector()
67 {
68 }
69
70 void LongPressGestureDetector::SendEvent(const Integration::TouchEvent& event)
71 {
72   unsigned int pointCount( event.GetPointCount() );
73
74   switch (mState)
75   {
76     // Clear: Wait till one point touches the screen before starting timer.
77     case Clear:
78     {
79       const Integration::Point& point = event.points[0];
80
81       if ( point.GetState() == PointState::DOWN )
82       {
83         mTouchPositions.clear();
84         mTouchPositions[point.GetDeviceId()] = point.GetScreenPosition();
85
86         mTouchTime = event.time;
87
88         mTimer.SetInterval(GetSystemValue());
89         mTimer.Start();
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         mTimer.Stop();
108         mState = Failed;
109         break;
110       }
111
112       bool endLoop(false);
113
114       for ( Integration::PointContainerConstIterator iter = event.points.begin(), endIter = event.points.end();
115            iter != endIter && !endLoop; ++iter)
116       {
117         switch( iter->GetState() )
118         {
119           // add point.
120           case PointState::DOWN:
121           {
122             mTouchPositions[iter->GetDeviceId()] = iter->GetScreenPosition();
123             break;
124           }
125
126           // remove point.
127           case PointState::UP:
128           case PointState::INTERRUPTED:
129           {
130             // System has interrupted us, long press is not possible, inform Core
131             EmitGesture( Gesture::Cancelled );
132             mTouchPositions.clear();
133             mTimer.Stop();
134             mState = ( pointCount == 1 ) ? Clear : Failed; // Change state to Clear if only one point, Failed otherwise.
135             endLoop = true;
136             break;
137           }
138
139           case PointState::MOTION:
140           {
141             const Vector2 touchPosition( mTouchPositions[iter->GetDeviceId()] - iter->GetScreenPosition() );
142             float distanceSquared = touchPosition.LengthSquared();
143
144             if (distanceSquared > ( MAXIMUM_MOTION_ALLOWED * MAXIMUM_MOTION_ALLOWED ) )
145             {
146               // We have moved more than the allowable motion for a long press gesture. Inform Core and change state to Failed.
147               EmitGesture( Gesture::Cancelled );
148               mTimer.Stop();
149               mState = Failed;
150               endLoop = true;
151             }
152             break;
153           }
154
155           case PointState::STATIONARY:
156           case PointState::LEAVE:
157           {
158             break;
159           }
160         }
161       }
162       break;
163     }
164
165     // Failed/Finished: Monitor the touches, waiting for all touches to be released.
166     case Failed:
167     case Finished:
168     {
169       // eventually the final touch point will be removed, marking the end of this gesture.
170       if ( pointCount == 1 )
171       {
172         PointState::Type primaryPointState = event.points[0].GetState();
173
174         if ( (primaryPointState == PointState::UP) || (primaryPointState == PointState::INTERRUPTED) )
175         {
176           if(mState == Finished)
177           {
178             // When the last touch point is lifted, we should inform the Core that the Long press has finished.
179             EmitGesture(Gesture::Finished);
180           }
181           mTouchPositions.clear();
182           mState = Clear; // Reset state to clear when last touch point is lifted.
183         }
184       }
185       break;
186     }
187   }
188 }
189
190 void LongPressGestureDetector::Update(const Integration::GestureRequest& request)
191 {
192   const Integration::LongPressGestureRequest& longPress = static_cast<const Integration::LongPressGestureRequest&>(request);
193
194   mMinimumTouchesRequired = longPress.minTouches;
195   mMaximumTouchesRequired = longPress.maxTouches;
196 }
197
198 bool LongPressGestureDetector::TimerCallback()
199 {
200   EmitGesture(Gesture::Started);
201
202   mState = Finished;
203
204   // There is no touch event at this time, so ProcessEvents must be called directly
205   mCoreEventInterface.ProcessCoreEvents();
206
207   return false;
208 }
209
210 void LongPressGestureDetector::EmitGesture(Gesture::State state)
211 {
212   unsigned int touchPoints ( mTouchPositions.size() );
213
214   // We should tell Core about the Possible and Cancelled states regardless of whether we have satisfied long press requirements.
215   if ( (state == Gesture::Possible) ||
216        (state == Gesture::Cancelled) ||
217        (touchPoints >= mMinimumTouchesRequired) )
218   {
219     Integration::LongPressGestureEvent longPress( state );
220     longPress.numberOfTouches = touchPoints;
221
222     for (std::map<int, Vector2>::iterator iter = mTouchPositions.begin(), endIter = mTouchPositions.end();
223          iter != endIter; ++iter)
224     {
225       longPress.point += iter->second;
226     }
227     longPress.point /= touchPoints;
228
229     longPress.time = mTouchTime;
230     if ( state != Gesture::Possible )
231     {
232       longPress.time += GetSystemValue();
233     }
234
235     mCoreEventInterface.QueueCoreEvent(longPress);
236   }
237 }
238
239 int LongPressGestureDetector::GetSystemValue()
240 {
241   return GetLongPressTime( LONG_PRESS_TIME );
242 }
243
244 } // namespace Adaptor
245
246 } // namespace Internal
247
248 } // namespace Dali