[Tizen](ATSPI) Fix Native TC fails
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / common / accessibility-gesture-detector.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/accessibility/common/accessibility-gesture-detector.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 #include <dali/public-api/events/touch-point.h>
25
26 #include <dali/integration-api/events/touch-event-integ.h>
27
28
29 namespace Dali
30 {
31
32 namespace Internal
33 {
34
35 namespace Adaptor
36 {
37
38 namespace
39 {
40   const float MINIMUM_MOTION_DISTANCE_BEFORE_PAN( 15.0f );
41   const float MINIMUM_MOTION_DISTANCE_BEFORE_PAN_SQUARED( MINIMUM_MOTION_DISTANCE_BEFORE_PAN * MINIMUM_MOTION_DISTANCE_BEFORE_PAN );
42   const float MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO( 2.0f / 3.0f );
43   const unsigned long MAXIMUM_TIME_DIFF_ALLOWED( 500 );
44   const unsigned long MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS( 100 );
45   const unsigned int MINIMUM_MOTION_EVENTS_BEFORE_PAN(2);
46   const unsigned int MINIMUM_TOUCHES_BEFORE_PAN(1);
47   const unsigned int MAXIMUM_TOUCHES_BEFORE_PAN(1);
48 } // unnamed namespace
49
50
51 AccessibilityGestureDetector::AccessibilityGestureDetector()
52 : mState( Clear ),
53   mScene(nullptr),
54   mGestureHandler(nullptr),
55   mPanning(false),
56   mThresholdAdjustmentsRemaining( 0 ),
57   mThresholdTotalAdjustments( MINIMUM_MOTION_DISTANCE_BEFORE_PAN * MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO ),
58   mPrimaryTouchDownTime( 0 ),
59   mMinimumTouchesRequired( MINIMUM_TOUCHES_BEFORE_PAN ),
60   mMaximumTouchesRequired( MAXIMUM_TOUCHES_BEFORE_PAN ),
61   mMinimumDistanceSquared( MINIMUM_MOTION_DISTANCE_BEFORE_PAN_SQUARED ),
62   mMinimumMotionEvents( MINIMUM_MOTION_EVENTS_BEFORE_PAN ),
63   mMotionEvents( 0 )
64 {
65 }
66
67 AccessibilityGestureDetector::~AccessibilityGestureDetector()
68 {
69 }
70
71 void AccessibilityGestureDetector::SetGestureHandler(AccessibilityGestureHandler& handler)
72 {
73   mGestureHandler = &handler;
74 }
75
76 void AccessibilityGestureDetector::EmitPan(const AccessibilityGestureEvent gesture)
77 {
78   if( mGestureHandler )
79   {
80     if(gesture.state == AccessibilityGestureEvent::Started)
81     {
82       mPanning = true;
83     }
84
85     if( mPanning )
86     {
87       mGestureHandler->HandlePanGesture(gesture);
88
89       if( (gesture.state == AccessibilityGestureEvent::Finished) ||
90           (gesture.state == AccessibilityGestureEvent::Cancelled) )
91       {
92         mPanning = false;
93       }
94     }
95   }
96 }
97
98 void AccessibilityGestureDetector::SendEvent(const Integration::TouchEvent& event)
99 {
100   PointState::Type primaryPointState(event.points[0].GetState());
101
102   if (primaryPointState == PointState::INTERRUPTED)
103   {
104     if ( ( mState == Started ) || ( mState == Possible ) )
105     {
106       // If our pan had started and we are interrupted, then tell Core that pan is cancelled.
107       mTouchEvents.push_back(event);
108       SendPan(AccessibilityGestureEvent::Cancelled, event);
109     }
110     mState = Clear; // We should change our state to Clear.
111     mTouchEvents.clear();
112   }
113   else
114   {
115     switch (mState)
116     {
117       case Clear:
118       {
119         if ( ( primaryPointState == PointState::DOWN ) || ( primaryPointState == PointState::STATIONARY ) )
120         {
121           mPrimaryTouchDownLocation = event.points[0].GetScreenPosition();
122           mPrimaryTouchDownTime = event.time;
123           mMotionEvents = 0;
124           if (event.GetPointCount() == mMinimumTouchesRequired)
125           {
126             // We have satisfied the minimum touches required for a pan, tell core that a gesture may be possible and change our state accordingly.
127             mState = Possible;
128             SendPan(AccessibilityGestureEvent::Possible, event);
129           }
130
131           mTouchEvents.push_back(event);
132         }
133         break;
134       }
135
136       case Possible:
137       {
138         unsigned int pointCount(event.GetPointCount());
139         if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
140         {
141           if (primaryPointState == PointState::MOTION)
142           {
143             mTouchEvents.push_back(event);
144             mMotionEvents++;
145
146             Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
147
148             if ( ( mMotionEvents >= mMinimumMotionEvents ) &&
149                  ( delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) ) )
150             {
151               // If the touch point(s) have moved enough distance to be considered a pan, then tell Core that the pan gesture has started and change our state accordingly.
152               mState = Started;
153               SendPan(AccessibilityGestureEvent::Started, event);
154             }
155           }
156           else if (primaryPointState == PointState::UP)
157           {
158             Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
159             if (delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) )
160             {
161               SendPan(AccessibilityGestureEvent::Started, event);
162               mTouchEvents.push_back(event);
163               SendPan(AccessibilityGestureEvent::Finished, event);
164             }
165             else
166             {
167               // If we have lifted the primary touch point then tell core the pan is cancelled and change our state to Clear.
168               SendPan(AccessibilityGestureEvent::Cancelled, event);
169             }
170             mState = Clear;
171             mTouchEvents.clear();
172           }
173         }
174         else
175         {
176           // We do not satisfy pan conditions, tell Core our Gesture has been cancelled.
177           SendPan(AccessibilityGestureEvent::Cancelled, event);
178
179           if (pointCount == 1 && primaryPointState == PointState::UP)
180           {
181             // If we have lifted the primary touch point, then change our state to Clear...
182             mState = Clear;
183             mTouchEvents.clear();
184           }
185           else
186           {
187             // ...otherwise change it to Failed.
188             mState = Failed;
189           }
190         }
191         break;
192       }
193
194       case Started:
195       {
196         mTouchEvents.push_back(event);
197
198         unsigned int pointCount(event.GetPointCount());
199         if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
200         {
201           switch (primaryPointState)
202           {
203             case PointState::MOTION:
204               // Pan is continuing, tell Core.
205               SendPan(AccessibilityGestureEvent::Continuing, event);
206               break;
207
208             case PointState::UP:
209               // Pan is finally finished when our primary point is lifted, tell Core and change our state to Clear.
210               SendPan(AccessibilityGestureEvent::Finished, event);
211               mState = Clear;
212               mTouchEvents.clear();
213               break;
214
215             case PointState::STATIONARY:
216               if (pointCount == mMinimumTouchesRequired)
217               {
218                 Integration::PointContainerConstIterator iter = event.points.begin() + 1; // We already know the state of the first point
219                 for(; iter != event.points.end(); ++iter)
220                 {
221                   if(iter->GetState() == PointState::UP)
222                   {
223                     // The number of touch points will be less than the minimum required.  Inform core and change our state to Finished.
224                     SendPan(AccessibilityGestureEvent::Finished, event);
225                     mState = Finished;
226                     break;
227                   }
228                 }
229               }
230               break;
231
232             default:
233               break;
234           }
235         }
236         else
237         {
238           // We have gone outside of the pan requirements, inform Core that the gesture is finished.
239           SendPan(AccessibilityGestureEvent::Finished, event);
240
241           if (pointCount == 1 && primaryPointState == PointState::UP)
242           {
243             // If this was the primary point being released, then we change our state back to Clear...
244             mState = Clear;
245             mTouchEvents.clear();
246           }
247           else
248           {
249             // ...otherwise we change it to Finished.
250             mState = Finished;
251           }
252         }
253         break;
254       }
255
256       case Finished:
257       case Failed:
258       {
259         if (primaryPointState == PointState::UP)
260         {
261           // Change our state back to clear when the primary touch point is released.
262           mState = Clear;
263           mTouchEvents.clear();
264         }
265         break;
266       }
267     }
268   }
269 }
270
271 void AccessibilityGestureDetector::SendPan(AccessibilityGestureEvent::State state, const Integration::TouchEvent& currentEvent)
272 {
273   AccessibilityGestureEvent gesture(state);
274   gesture.currentPosition = currentEvent.points[0].GetScreenPosition();
275   gesture.numberOfTouches = currentEvent.GetPointCount();
276
277   if ( mTouchEvents.size() > 1 )
278   {
279     // Get the second last event in the queue, the last one is the current event
280     const Integration::TouchEvent& previousEvent( *( mTouchEvents.rbegin() + 1 ) );
281
282     Vector2 previousPosition( mPreviousPosition );
283     uint32_t previousTime( previousEvent.time );
284
285     // If we've just started then we want to remove the threshold from Core calculations.
286     if ( state == AccessibilityGestureEvent::Started )
287     {
288       previousPosition = mPrimaryTouchDownLocation;
289       previousTime = mPrimaryTouchDownTime;
290
291       // If it's a slow pan, we do not want to phase in the threshold over the first few pan-events
292       // A slow pan is defined as one that starts the specified number of milliseconds after the down-event
293       if ( ( currentEvent.time - previousTime ) > MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS )
294       {
295         mThresholdAdjustmentsRemaining = mThresholdTotalAdjustments;
296         mThresholdAdjustmentPerFrame = ( gesture.currentPosition - previousPosition ) / static_cast<float>( mThresholdTotalAdjustments );
297       }
298       else
299       {
300         mThresholdAdjustmentsRemaining = 0;
301         mThresholdAdjustmentPerFrame = Vector2::ZERO;
302       }
303     }
304
305     gesture.previousPosition = previousPosition;
306     gesture.timeDelta = currentEvent.time - previousTime;
307
308     // Apply the threshold with a phased approach
309     if ( mThresholdAdjustmentsRemaining > 0 )
310     {
311       --mThresholdAdjustmentsRemaining;
312       gesture.currentPosition -= mThresholdAdjustmentPerFrame * static_cast<float>( mThresholdAdjustmentsRemaining );
313     }
314
315     mPreviousPosition = gesture.currentPosition;
316   }
317   else
318   {
319     gesture.previousPosition = gesture.currentPosition;
320     gesture.timeDelta = 0;
321   }
322
323   gesture.time = currentEvent.time;
324
325   EmitPan(gesture);
326 }
327
328 } // namespace Adaptor
329
330 } // namespace Internal
331
332 } // namespace Dali