[Tizen](ATSPI) Fix Native TC fails
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / common / accessibility-gesture-detector.cpp
old mode 100644 (file)
new mode 100755 (executable)
index 8010348..b872077
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali/internal/accessibility/common/accessibility-gesture-detector.h>
 
 // EXTERNAL INCLUDES
-#include <dali/integration-api/events/gesture-requests.h>
+#include <cmath>
+
+#include <dali/public-api/events/touch-point.h>
+
+#include <dali/integration-api/events/touch-event-integ.h>
+
 
 namespace Dali
 {
@@ -30,10 +35,32 @@ namespace Internal
 namespace Adaptor
 {
 
+namespace
+{
+  const float MINIMUM_MOTION_DISTANCE_BEFORE_PAN( 15.0f );
+  const float MINIMUM_MOTION_DISTANCE_BEFORE_PAN_SQUARED( MINIMUM_MOTION_DISTANCE_BEFORE_PAN * MINIMUM_MOTION_DISTANCE_BEFORE_PAN );
+  const float MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO( 2.0f / 3.0f );
+  const unsigned long MAXIMUM_TIME_DIFF_ALLOWED( 500 );
+  const unsigned long MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS( 100 );
+  const unsigned int MINIMUM_MOTION_EVENTS_BEFORE_PAN(2);
+  const unsigned int MINIMUM_TOUCHES_BEFORE_PAN(1);
+  const unsigned int MAXIMUM_TOUCHES_BEFORE_PAN(1);
+} // unnamed namespace
+
+
 AccessibilityGestureDetector::AccessibilityGestureDetector()
-: PanGestureDetectorBase(Vector2::ZERO, Integration::PanGestureRequest(), NULL),
-  mGestureHandler(NULL),
-  mPanning(false)
+: mState( Clear ),
+  mScene(nullptr),
+  mGestureHandler(nullptr),
+  mPanning(false),
+  mThresholdAdjustmentsRemaining( 0 ),
+  mThresholdTotalAdjustments( MINIMUM_MOTION_DISTANCE_BEFORE_PAN * MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO ),
+  mPrimaryTouchDownTime( 0 ),
+  mMinimumTouchesRequired( MINIMUM_TOUCHES_BEFORE_PAN ),
+  mMaximumTouchesRequired( MAXIMUM_TOUCHES_BEFORE_PAN ),
+  mMinimumDistanceSquared( MINIMUM_MOTION_DISTANCE_BEFORE_PAN_SQUARED ),
+  mMinimumMotionEvents( MINIMUM_MOTION_EVENTS_BEFORE_PAN ),
+  mMotionEvents( 0 )
 {
 }
 
@@ -46,11 +73,11 @@ void AccessibilityGestureDetector::SetGestureHandler(AccessibilityGestureHandler
   mGestureHandler = &handler;
 }
 
-void AccessibilityGestureDetector::EmitPan(const Integration::PanGestureEvent gesture)
+void AccessibilityGestureDetector::EmitPan(const AccessibilityGestureEvent gesture)
 {
   if( mGestureHandler )
   {
-    if(gesture.state == Gesture::Started)
+    if(gesture.state == AccessibilityGestureEvent::Started)
     {
       mPanning = true;
     }
@@ -59,8 +86,8 @@ void AccessibilityGestureDetector::EmitPan(const Integration::PanGestureEvent ge
     {
       mGestureHandler->HandlePanGesture(gesture);
 
-      if( (gesture.state == Gesture::Finished) ||
-          (gesture.state == Gesture::Cancelled) )
+      if( (gesture.state == AccessibilityGestureEvent::Finished) ||
+          (gesture.state == AccessibilityGestureEvent::Cancelled) )
       {
         mPanning = false;
       }
@@ -68,6 +95,236 @@ void AccessibilityGestureDetector::EmitPan(const Integration::PanGestureEvent ge
   }
 }
 
+void AccessibilityGestureDetector::SendEvent(const Integration::TouchEvent& event)
+{
+  PointState::Type primaryPointState(event.points[0].GetState());
+
+  if (primaryPointState == PointState::INTERRUPTED)
+  {
+    if ( ( mState == Started ) || ( mState == Possible ) )
+    {
+      // If our pan had started and we are interrupted, then tell Core that pan is cancelled.
+      mTouchEvents.push_back(event);
+      SendPan(AccessibilityGestureEvent::Cancelled, event);
+    }
+    mState = Clear; // We should change our state to Clear.
+    mTouchEvents.clear();
+  }
+  else
+  {
+    switch (mState)
+    {
+      case Clear:
+      {
+        if ( ( primaryPointState == PointState::DOWN ) || ( primaryPointState == PointState::STATIONARY ) )
+        {
+          mPrimaryTouchDownLocation = event.points[0].GetScreenPosition();
+          mPrimaryTouchDownTime = event.time;
+          mMotionEvents = 0;
+          if (event.GetPointCount() == mMinimumTouchesRequired)
+          {
+            // We have satisfied the minimum touches required for a pan, tell core that a gesture may be possible and change our state accordingly.
+            mState = Possible;
+            SendPan(AccessibilityGestureEvent::Possible, event);
+          }
+
+          mTouchEvents.push_back(event);
+        }
+        break;
+      }
+
+      case Possible:
+      {
+        unsigned int pointCount(event.GetPointCount());
+        if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
+        {
+          if (primaryPointState == PointState::MOTION)
+          {
+            mTouchEvents.push_back(event);
+            mMotionEvents++;
+
+            Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
+
+            if ( ( mMotionEvents >= mMinimumMotionEvents ) &&
+                 ( delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) ) )
+            {
+              // 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.
+              mState = Started;
+              SendPan(AccessibilityGestureEvent::Started, event);
+            }
+          }
+          else if (primaryPointState == PointState::UP)
+          {
+            Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
+            if (delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) )
+            {
+              SendPan(AccessibilityGestureEvent::Started, event);
+              mTouchEvents.push_back(event);
+              SendPan(AccessibilityGestureEvent::Finished, event);
+            }
+            else
+            {
+              // If we have lifted the primary touch point then tell core the pan is cancelled and change our state to Clear.
+              SendPan(AccessibilityGestureEvent::Cancelled, event);
+            }
+            mState = Clear;
+            mTouchEvents.clear();
+          }
+        }
+        else
+        {
+          // We do not satisfy pan conditions, tell Core our Gesture has been cancelled.
+          SendPan(AccessibilityGestureEvent::Cancelled, event);
+
+          if (pointCount == 1 && primaryPointState == PointState::UP)
+          {
+            // If we have lifted the primary touch point, then change our state to Clear...
+            mState = Clear;
+            mTouchEvents.clear();
+          }
+          else
+          {
+            // ...otherwise change it to Failed.
+            mState = Failed;
+          }
+        }
+        break;
+      }
+
+      case Started:
+      {
+        mTouchEvents.push_back(event);
+
+        unsigned int pointCount(event.GetPointCount());
+        if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
+        {
+          switch (primaryPointState)
+          {
+            case PointState::MOTION:
+              // Pan is continuing, tell Core.
+              SendPan(AccessibilityGestureEvent::Continuing, event);
+              break;
+
+            case PointState::UP:
+              // Pan is finally finished when our primary point is lifted, tell Core and change our state to Clear.
+              SendPan(AccessibilityGestureEvent::Finished, event);
+              mState = Clear;
+              mTouchEvents.clear();
+              break;
+
+            case PointState::STATIONARY:
+              if (pointCount == mMinimumTouchesRequired)
+              {
+                Integration::PointContainerConstIterator iter = event.points.begin() + 1; // We already know the state of the first point
+                for(; iter != event.points.end(); ++iter)
+                {
+                  if(iter->GetState() == PointState::UP)
+                  {
+                    // The number of touch points will be less than the minimum required.  Inform core and change our state to Finished.
+                    SendPan(AccessibilityGestureEvent::Finished, event);
+                    mState = Finished;
+                    break;
+                  }
+                }
+              }
+              break;
+
+            default:
+              break;
+          }
+        }
+        else
+        {
+          // We have gone outside of the pan requirements, inform Core that the gesture is finished.
+          SendPan(AccessibilityGestureEvent::Finished, event);
+
+          if (pointCount == 1 && primaryPointState == PointState::UP)
+          {
+            // If this was the primary point being released, then we change our state back to Clear...
+            mState = Clear;
+            mTouchEvents.clear();
+          }
+          else
+          {
+            // ...otherwise we change it to Finished.
+            mState = Finished;
+          }
+        }
+        break;
+      }
+
+      case Finished:
+      case Failed:
+      {
+        if (primaryPointState == PointState::UP)
+        {
+          // Change our state back to clear when the primary touch point is released.
+          mState = Clear;
+          mTouchEvents.clear();
+        }
+        break;
+      }
+    }
+  }
+}
+
+void AccessibilityGestureDetector::SendPan(AccessibilityGestureEvent::State state, const Integration::TouchEvent& currentEvent)
+{
+  AccessibilityGestureEvent gesture(state);
+  gesture.currentPosition = currentEvent.points[0].GetScreenPosition();
+  gesture.numberOfTouches = currentEvent.GetPointCount();
+
+  if ( mTouchEvents.size() > 1 )
+  {
+    // Get the second last event in the queue, the last one is the current event
+    const Integration::TouchEvent& previousEvent( *( mTouchEvents.rbegin() + 1 ) );
+
+    Vector2 previousPosition( mPreviousPosition );
+    uint32_t previousTime( previousEvent.time );
+
+    // If we've just started then we want to remove the threshold from Core calculations.
+    if ( state == AccessibilityGestureEvent::Started )
+    {
+      previousPosition = mPrimaryTouchDownLocation;
+      previousTime = mPrimaryTouchDownTime;
+
+      // If it's a slow pan, we do not want to phase in the threshold over the first few pan-events
+      // A slow pan is defined as one that starts the specified number of milliseconds after the down-event
+      if ( ( currentEvent.time - previousTime ) > MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS )
+      {
+        mThresholdAdjustmentsRemaining = mThresholdTotalAdjustments;
+        mThresholdAdjustmentPerFrame = ( gesture.currentPosition - previousPosition ) / static_cast<float>( mThresholdTotalAdjustments );
+      }
+      else
+      {
+        mThresholdAdjustmentsRemaining = 0;
+        mThresholdAdjustmentPerFrame = Vector2::ZERO;
+      }
+    }
+
+    gesture.previousPosition = previousPosition;
+    gesture.timeDelta = currentEvent.time - previousTime;
+
+    // Apply the threshold with a phased approach
+    if ( mThresholdAdjustmentsRemaining > 0 )
+    {
+      --mThresholdAdjustmentsRemaining;
+      gesture.currentPosition -= mThresholdAdjustmentPerFrame * static_cast<float>( mThresholdAdjustmentsRemaining );
+    }
+
+    mPreviousPosition = gesture.currentPosition;
+  }
+  else
+  {
+    gesture.previousPosition = gesture.currentPosition;
+    gesture.timeDelta = 0;
+  }
+
+  gesture.time = currentEvent.time;
+
+  EmitPan(gesture);
+}
+
 } // namespace Adaptor
 
 } // namespace Internal