Support multiple window rendering
[platform/core/uifw/dali-core.git] / dali / internal / event / animation / animation-impl.cpp
index 0d70028..876ec29 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2018 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.
@@ -30,6 +30,7 @@
 #include <dali/public-api/math/radian.h>
 #include <dali/internal/event/animation/animation-playlist.h>
 #include <dali/internal/event/animation/animator-connector.h>
+#include <dali/internal/event/animation/path-impl.h>
 #include <dali/internal/event/common/notification-manager.h>
 #include <dali/internal/event/common/property-helper.h>
 #include <dali/internal/event/common/stage-impl.h>
@@ -81,34 +82,77 @@ const Dali::Animation::EndAction DEFAULT_DISCONNECT_ACTION( Dali::Animation::Bak
 const Dali::Animation::Interpolation DEFAULT_INTERPOLATION( Dali::Animation::Linear );
 const Dali::AlphaFunction DEFAULT_ALPHA_FUNCTION( Dali::AlphaFunction::DEFAULT );
 
-} // anon namespace
-
-
-AnimationPtr Animation::New(float durationSeconds)
+/**
+ * Helper to tell if a property is animatable (if we have animators for it)
+ *
+ * @param type type to check
+ * @return true if animatable
+ */
+inline bool IsAnimatable( Property::Type type )
 {
-  Stage* stage = Stage::GetCurrent();
-
-  if( stage )
+  bool animatable = false;
+  switch( type )
   {
-    AnimationPlaylist& playlist = stage->GetAnimationPlaylist();
-
-    if( durationSeconds < 0.0f )
+    case Property::BOOLEAN :
+    case Property::FLOAT :
+    case Property::INTEGER :
+    case Property::VECTOR2 :
+    case Property::VECTOR3 :
+    case Property::VECTOR4 :
+    case Property::ROTATION :
+    {
+      animatable = true;
+      break;
+    }
+    case Property::MATRIX : // matrix is allowed as a scene graph property but there's no animators for it
+    case Property::MATRIX3 : // matrix3 is allowed as a scene graph property but there's no animators for it
+    case Property::NONE :
+    case Property::RECTANGLE :
+    case Property::STRING :
+    case Property::ARRAY :
+    case Property::MAP :
+    case Property::EXTENTS :
     {
-      DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
-      durationSeconds = 0.0f;
+      break;
     }
+  }
+  return animatable;
+}
+
+/**
+ * Helper to validate animation input values
+ *
+ * @param propertyType type of the property that is being animated
+ * @param destinationType type of the target
+ * @param period time period of the animation
+ */
+void ValidateParameters( Property::Type propertyType, Property::Type destinationType, const TimePeriod& period )
+{
+  // destination value has to be animatable
+  DALI_ASSERT_ALWAYS( IsAnimatable( propertyType ) && "Property type is not animatable" );
+  DALI_ASSERT_ALWAYS( IsAnimatable( destinationType ) && "Target value is not animatable" );
+  DALI_ASSERT_ALWAYS( propertyType == destinationType && "Property and target types don't match" );
+  DALI_ASSERT_ALWAYS( period.durationSeconds >= 0 && "Duration must be >=0" );
+}
 
-    AnimationPtr animation = new Animation( *stage, playlist, durationSeconds, DEFAULT_END_ACTION, DEFAULT_DISCONNECT_ACTION, DEFAULT_ALPHA_FUNCTION );
+} // anonymous namespace
 
-    // Second-phase construction
-    animation->Initialize();
 
-    return animation;
-  }
-  else
+AnimationPtr Animation::New(float durationSeconds)
+{
+  if( durationSeconds < 0.0f )
   {
-    return NULL;
+    DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
+    durationSeconds = 0.0f;
   }
+
+  ThreadLocalStorage& tls = ThreadLocalStorage::Get();
+  AnimationPtr animation = new Animation( tls.GetEventThreadServices(), tls.GetAnimationPlaylist(), durationSeconds, DEFAULT_END_ACTION, DEFAULT_DISCONNECT_ACTION, DEFAULT_ALPHA_FUNCTION );
+
+  // Second-phase construction
+  animation->Initialize();
+
+  return animation;
 }
 
 Animation::Animation( EventThreadServices& eventThreadServices, AnimationPlaylist& playlist, float durationSeconds, EndAction endAction, EndAction disconnectAction, AlphaFunction defaultAlpha )
@@ -127,7 +171,10 @@ Animation::Animation( EventThreadServices& eventThreadServices, AnimationPlaylis
   mEndAction( endAction ),
   mDisconnectAction( disconnectAction ),
   mDefaultAlpha( defaultAlpha ),
-  mState(Dali::Animation::STOPPED)
+  mState(Dali::Animation::STOPPED),
+  mProgressReachedMarker( 0.0f ),
+  mDelaySeconds( 0.0f ),
+  mAutoReverseEnabled( false )
 {
 }
 
@@ -159,14 +206,10 @@ void Animation::CreateSceneObject()
 {
   DALI_ASSERT_DEBUG( mAnimation == NULL );
 
-  // Create a new animation, temporarily owned
-  SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mPlayRange, mLoopCount, mEndAction, mDisconnectAction );
-
-  // Keep a const pointer to the animation.
-  mAnimation = animation;
-
-  // Transfer animation ownership to the update manager through a message
-  AddAnimationMessage( mEventThreadServices.GetUpdateManager(), animation );
+  // Create a new animation, Keep a const pointer to the animation.
+  mAnimation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mPlayRange, mLoopCount, mEndAction, mDisconnectAction );
+  OwnerPointer< SceneGraph::Animation > transferOwnership( const_cast< SceneGraph::Animation* >( mAnimation ) );
+  AddAnimationMessage( mEventThreadServices.GetUpdateManager(), transferOwnership );
 }
 
 void Animation::DestroySceneObject()
@@ -187,13 +230,23 @@ void Animation::SetDuration(float seconds)
     seconds = 0.0f;
   }
 
-  // Cache for public getters
   mDurationSeconds = seconds;
 
   // mAnimation is being used in a separate thread; queue a message to set the value
   SetDurationMessage( mEventThreadServices, *mAnimation, seconds );
 }
 
+void Animation::SetProgressNotification( float progress )
+{
+  // mAnimation is being used in a separate thread; queue a message to set the value
+  mProgressReachedMarker = progress;
+}
+
+float Animation::GetProgressNotification()
+{
+  return mProgressReachedMarker;
+}
+
 float Animation::GetDuration() const
 {
   // This is not animatable; the cached value is up-to-date.
@@ -205,7 +258,7 @@ void Animation::SetLooping(bool on)
   SetLoopCount( on ? 0 : 1 );
 }
 
-void Animation::SetLoopCount(int count)
+void Animation::SetLoopCount(int32_t count)
 {
   // Cache for public getters
   mLoopCount = count;
@@ -214,12 +267,12 @@ void Animation::SetLoopCount(int count)
   SetLoopingMessage( mEventThreadServices, *mAnimation, mLoopCount );
 }
 
-int Animation::GetLoopCount()
+int32_t Animation::GetLoopCount()
 {
   return mLoopCount;
 }
 
-int Animation::GetCurrentLoop()
+int32_t Animation::GetCurrentLoop()
 {
   return mCurrentLoop;
 }
@@ -266,38 +319,9 @@ void Animation::Play()
 
   mState = Dali::Animation::PLAYING;
 
-  if( mEndAction != EndAction::Discard ) // If the animation is discarded, then we do not want to change the target values
-  {
-    unsigned int connectorTargetValuesIndex( 0 );
-    unsigned int numberOfConnectorTargetValues = mConnectorTargetValues.size();
-
-    /*
-     * Loop through all Animator connectors, if connector index matches the current index stored in mConnectorTargetValues container then
-     * should apply target values for this index to the object.
-     */
-    for ( unsigned int connectorIndex = 0; connectorIndex < mConnectors.Count(); connectorIndex ++)
-    {
-      // Use index to check if the current connector is next in the mConnectorTargetValues container, meaning targetValues have been pushed in AnimateXXFunction
-      if ( connectorTargetValuesIndex < numberOfConnectorTargetValues )
-      {
-        ConnectorTargetValues& connectorPair = mConnectorTargetValues[ connectorTargetValuesIndex ];
-
-        if ( connectorPair.connectorIndex == connectorIndex )
-        {
-          // Current connector index matches next in the stored connectors with target values so apply target value.
-          connectorTargetValuesIndex++; // Found a match for connector so increment index to next one
+  NotifyObjects();
 
-          AnimatorConnectorBase* connector = mConnectors[ connectorIndex ];
-
-          Object* object = connector->GetObject();
-          if( object )
-          {
-            object->NotifyPropertyAnimation( *this, connector->GetPropertyIndex(), connectorPair.targetValue );
-          }
-        }
-      }
-    }
-  }
+  SendFinalProgressNotificationMessage();
 
   // mAnimation is being used in a separate thread; queue a Play message
   PlayAnimationMessage( mEventThreadServices, *mAnimation );
@@ -312,11 +336,35 @@ void Animation::PlayFrom( float progress )
 
     mState = Dali::Animation::PLAYING;
 
+    NotifyObjects();
+
+    SendFinalProgressNotificationMessage();
+
     // mAnimation is being used in a separate thread; queue a Play message
     PlayAnimationFromMessage( mEventThreadServices, *mAnimation, progress );
   }
 }
 
+void Animation::PlayAfter( float delaySeconds )
+{
+  // The negative delay means play immediately.
+  delaySeconds = std::max( 0.f, delaySeconds );
+
+  mDelaySeconds = delaySeconds;
+
+  // Update the current playlist
+  mPlaylist.OnPlay( *this );
+
+  mState = Dali::Animation::PLAYING;
+
+  NotifyObjects();
+
+  SendFinalProgressNotificationMessage();
+
+  // mAnimation is being used in a separate thread; queue a message to set the value
+  PlayAfterMessage( mEventThreadServices, *mAnimation, delaySeconds );
+}
+
 void Animation::Pause()
 {
   mState = Dali::Animation::PAUSED;
@@ -359,31 +407,43 @@ void Animation::Clear()
   mPlaylist.OnClear( *this );
 }
 
-void Animation::AnimateBy(Property& target, Property::Value& relativeValue)
+void Animation::AnimateBy( Property& target, Property::Value& relativeValue )
 {
-  AnimateBy(target, relativeValue, mDefaultAlpha, TimePeriod(mDurationSeconds));
+  AnimateBy( target, relativeValue, mDefaultAlpha, TimePeriod(mDurationSeconds) );
 }
 
-void Animation::AnimateBy(Property& target, Property::Value& relativeValue, AlphaFunction alpha)
+void Animation::AnimateBy( Property& target, Property::Value& relativeValue, AlphaFunction alpha )
 {
-  AnimateBy(target, relativeValue, alpha, TimePeriod(mDurationSeconds));
+  AnimateBy( target, relativeValue, alpha, TimePeriod(mDurationSeconds) );
 }
 
-void Animation::AnimateBy(Property& target, Property::Value& relativeValue, TimePeriod period)
+void Animation::AnimateBy( Property& target, Property::Value& relativeValue, TimePeriod period )
 {
-  AnimateBy(target, relativeValue, mDefaultAlpha, period);
+  AnimateBy( target, relativeValue, mDefaultAlpha, period );
 }
 
-void Animation::AnimateBy(Property& target, Property::Value& relativeValue, AlphaFunction alpha, TimePeriod period)
+void Animation::AnimateBy( Property& target, Property::Value& relativeValue, AlphaFunction alpha, TimePeriod period )
 {
-  Object& object = GetImplementation( target.object );
-  const Property::Type targetType = object.GetPropertyType( target.propertyIndex );
+  Object& object = GetImplementation(target.object);
+  const Property::Type propertyType = object.GetPropertyType( target.propertyIndex );
   const Property::Type destinationType = relativeValue.GetType();
-  DALI_ASSERT_ALWAYS( targetType == destinationType && "Animated value and Property type don't match" );
 
-  ExtendDuration( period );
+  // validate animation parameters, if component index is set then use float as checked type
+  ValidateParameters( (target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
+                      destinationType, period );
+
+  ExtendDuration(period);
+
+  // Store data to later notify the object that its property is being animated
+  ConnectorTargetValues connectorPair;
+  connectorPair.targetValue = relativeValue;
+  connectorPair.connectorIndex = mConnectors.Count();
+  connectorPair.timePeriod = period;
+  connectorPair.animatorType = Animation::BY;
+  mConnectorTargetValues.push_back( connectorPair );
 
-  switch ( targetType )
+  // using destination type so component animation gets correct type
+  switch ( destinationType )
   {
     case Property::BOOLEAN:
     {
@@ -398,12 +458,12 @@ void Animation::AnimateBy(Property& target, Property::Value& relativeValue, Alph
 
     case Property::INTEGER:
     {
-      AddAnimatorConnector( AnimatorConnector<int>::New( object,
-                                                         target.propertyIndex,
-                                                         target.componentIndex,
-                                                         new AnimateByInteger(relativeValue.Get<int>()),
-                                                         alpha,
-                                                         period ) );
+      AddAnimatorConnector( AnimatorConnector<int32_t>::New( object,
+                                                             target.propertyIndex,
+                                                             target.componentIndex,
+                                                             new AnimateByInteger(relativeValue.Get<int32_t>()),
+                                                             alpha,
+                                                             period ) );
       break;
     }
 
@@ -471,42 +531,30 @@ void Animation::AnimateBy(Property& target, Property::Value& relativeValue, Alph
   }
 }
 
-void Animation::AnimateTo(Property& target, Property::Value& destinationValue)
+void Animation::AnimateTo( Property& target, Property::Value& destinationValue )
 {
-  AnimateTo(target, destinationValue, mDefaultAlpha, TimePeriod(mDurationSeconds));
+  AnimateTo( target, destinationValue, mDefaultAlpha, TimePeriod(mDurationSeconds) );
 }
 
-void Animation::AnimateTo(Property& target, Property::Value& destinationValue, AlphaFunction alpha)
+void Animation::AnimateTo( Property& target, Property::Value& destinationValue, AlphaFunction alpha )
 {
-  AnimateTo(target, destinationValue, alpha, TimePeriod(mDurationSeconds));
+  AnimateTo( target, destinationValue, alpha, TimePeriod(mDurationSeconds));
 }
 
-void Animation::AnimateTo(Property& target, Property::Value& destinationValue, TimePeriod period)
+void Animation::AnimateTo( Property& target, Property::Value& destinationValue, TimePeriod period )
 {
-  AnimateTo(target, destinationValue, mDefaultAlpha, period);
+  AnimateTo( target, destinationValue, mDefaultAlpha, period );
 }
 
-void Animation::AnimateTo(Property& target, Property::Value& destinationValue, AlphaFunction alpha, TimePeriod period)
+void Animation::AnimateTo( Property& target, Property::Value& destinationValue, AlphaFunction alpha, TimePeriod period )
 {
-  Object& object = GetImplementation(target.object);
-
-  AnimateTo( object, target.propertyIndex, target.componentIndex, destinationValue, alpha, period );
-}
-
-void Animation::AnimateTo(Object& targetObject, Property::Index targetPropertyIndex, int componentIndex, Property::Value& destinationValue, AlphaFunction alpha, TimePeriod period)
-{
-  Property::Type targetType = targetObject.GetPropertyType(targetPropertyIndex);
-  if( componentIndex != Property::INVALID_COMPONENT_INDEX )
-  {
-    if( ( targetType == Property::VECTOR2 ) ||
-        ( targetType == Property::VECTOR3 ) ||
-        ( targetType == Property::VECTOR4 ) )
-    {
-      targetType = Property::FLOAT;
-    }
-  }
+  Object& object = GetImplementation( target.object );
+  const Property::Type propertyType = object.GetPropertyType( target.propertyIndex );
   const Property::Type destinationType = destinationValue.GetType();
-  DALI_ASSERT_ALWAYS( targetType == destinationType && "Animated value and Property type don't match" );
+
+  // validate animation parameters, if component index is set then use float as checked type
+  ValidateParameters( (target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
+                      destinationType, period );
 
   ExtendDuration( period );
 
@@ -514,15 +562,18 @@ void Animation::AnimateTo(Object& targetObject, Property::Index targetPropertyIn
   ConnectorTargetValues connectorPair;
   connectorPair.targetValue = destinationValue;
   connectorPair.connectorIndex = mConnectors.Count();
+  connectorPair.timePeriod = period;
+  connectorPair.animatorType = Animation::TO;
   mConnectorTargetValues.push_back( connectorPair );
 
+  // using destination type so component animation gets correct type
   switch ( destinationType )
   {
     case Property::BOOLEAN:
     {
-      AddAnimatorConnector( AnimatorConnector<bool>::New( targetObject,
-                                                          targetPropertyIndex,
-                                                          componentIndex,
+      AddAnimatorConnector( AnimatorConnector<bool>::New( object,
+                                                          target.propertyIndex,
+                                                          target.componentIndex,
                                                           new AnimateToBoolean( destinationValue.Get<bool>() ),
                                                           alpha,
                                                           period ) );
@@ -531,20 +582,20 @@ void Animation::AnimateTo(Object& targetObject, Property::Index targetPropertyIn
 
     case Property::INTEGER:
     {
-      AddAnimatorConnector( AnimatorConnector<int>::New( targetObject,
-                                                         targetPropertyIndex,
-                                                         componentIndex,
-                                                         new AnimateToInteger( destinationValue.Get<int>() ),
-                                                         alpha,
-                                                         period ) );
+      AddAnimatorConnector( AnimatorConnector<int32_t>::New( object,
+                                                             target.propertyIndex,
+                                                             target.componentIndex,
+                                                             new AnimateToInteger( destinationValue.Get<int32_t>() ),
+                                                             alpha,
+                                                             period ) );
       break;
     }
 
     case Property::FLOAT:
     {
-      AddAnimatorConnector( AnimatorConnector<float>::New( targetObject,
-                                                           targetPropertyIndex,
-                                                           componentIndex,
+      AddAnimatorConnector( AnimatorConnector<float>::New( object,
+                                                           target.propertyIndex,
+                                                           target.componentIndex,
                                                            new AnimateToFloat( destinationValue.Get<float>() ),
                                                            alpha,
                                                            period ) );
@@ -553,9 +604,9 @@ void Animation::AnimateTo(Object& targetObject, Property::Index targetPropertyIn
 
     case Property::VECTOR2:
     {
-      AddAnimatorConnector( AnimatorConnector<Vector2>::New( targetObject,
-                                                             targetPropertyIndex,
-                                                             componentIndex,
+      AddAnimatorConnector( AnimatorConnector<Vector2>::New( object,
+                                                             target.propertyIndex,
+                                                             target.componentIndex,
                                                              new AnimateToVector2( destinationValue.Get<Vector2>() ),
                                                              alpha,
                                                              period ) );
@@ -564,9 +615,9 @@ void Animation::AnimateTo(Object& targetObject, Property::Index targetPropertyIn
 
     case Property::VECTOR3:
     {
-      AddAnimatorConnector( AnimatorConnector<Vector3>::New( targetObject,
-                                                             targetPropertyIndex,
-                                                             componentIndex,
+      AddAnimatorConnector( AnimatorConnector<Vector3>::New( object,
+                                                             target.propertyIndex,
+                                                             target.componentIndex,
                                                              new AnimateToVector3( destinationValue.Get<Vector3>() ),
                                                              alpha,
                                                              period ) );
@@ -575,9 +626,9 @@ void Animation::AnimateTo(Object& targetObject, Property::Index targetPropertyIn
 
     case Property::VECTOR4:
     {
-      AddAnimatorConnector( AnimatorConnector<Vector4>::New( targetObject,
-                                                             targetPropertyIndex,
-                                                             componentIndex,
+      AddAnimatorConnector( AnimatorConnector<Vector4>::New( object,
+                                                             target.propertyIndex,
+                                                             target.componentIndex,
                                                              new AnimateToVector4( destinationValue.Get<Vector4>() ),
                                                              alpha,
                                                              period ) );
@@ -586,9 +637,9 @@ void Animation::AnimateTo(Object& targetObject, Property::Index targetPropertyIn
 
     case Property::ROTATION:
     {
-      AddAnimatorConnector( AnimatorConnector<Quaternion>::New( targetObject,
-                                                                targetPropertyIndex,
-                                                                componentIndex,
+      AddAnimatorConnector( AnimatorConnector<Quaternion>::New( object,
+                                                                target.propertyIndex,
+                                                                target.componentIndex,
                                                                 new RotateToQuaternion( destinationValue.Get<Quaternion>() ),
                                                                 alpha,
                                                                 period ) );
@@ -602,48 +653,63 @@ void Animation::AnimateTo(Object& targetObject, Property::Index targetPropertyIn
   }
 }
 
-void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames)
+void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames )
 {
-  AnimateBetween(target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION );
+  AnimateBetween( target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION );
 }
 
-void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Interpolation interpolation )
+void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, Interpolation interpolation )
 {
-  AnimateBetween(target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), interpolation );
+  AnimateBetween( target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), interpolation );
 }
 
-void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, TimePeriod period)
+void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, TimePeriod period )
 {
-  AnimateBetween(target, keyFrames, mDefaultAlpha, period, DEFAULT_INTERPOLATION);
+  AnimateBetween( target, keyFrames, mDefaultAlpha, period, DEFAULT_INTERPOLATION );
 }
 
-void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation)
+void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation )
 {
-  AnimateBetween(target, keyFrames, mDefaultAlpha, period, interpolation);
+  AnimateBetween( target, keyFrames, mDefaultAlpha, period, interpolation );
 }
 
-void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha)
+void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, AlphaFunction alpha )
 {
-  AnimateBetween(target, keyFrames, alpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION);
+  AnimateBetween( target, keyFrames, alpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION );
 }
 
-void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation)
+void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation )
 {
-  AnimateBetween(target, keyFrames, alpha, TimePeriod(mDurationSeconds), interpolation);
+  AnimateBetween( target, keyFrames, alpha, TimePeriod(mDurationSeconds), interpolation );
 }
 
-void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period)
+void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period )
 {
-  AnimateBetween(target, keyFrames, alpha, period, DEFAULT_INTERPOLATION);
+  AnimateBetween( target, keyFrames, alpha, period, DEFAULT_INTERPOLATION );
 }
 
-void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation)
+void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation )
 {
   Object& object = GetImplementation( target.object );
+  const Property::Type propertyType = object.GetPropertyType( target.propertyIndex );
+  const Property::Type destinationType = keyFrames.GetType();
+
+  // validate animation parameters, if component index is set then use float as checked type
+  ValidateParameters( (target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
+                      destinationType, period );
 
   ExtendDuration( period );
 
-  switch(keyFrames.GetType())
+  // Store data to later notify the object that its property is being animated
+  ConnectorTargetValues connectorPair;
+  connectorPair.targetValue = keyFrames.GetLastKeyFrameValue();
+  connectorPair.connectorIndex = mConnectors.Count();
+  connectorPair.timePeriod = period;
+  connectorPair.animatorType = BETWEEN;
+  mConnectorTargetValues.push_back( connectorPair );
+
+  // using destination type so component animation gets correct type
+  switch( destinationType )
   {
     case Dali::Property::BOOLEAN:
     {
@@ -664,7 +730,7 @@ void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Alph
       const KeyFrameInteger* kf;
       GetSpecialization(keyFrames, kf);
       KeyFrameIntegerPtr kfCopy = KeyFrameInteger::Clone(*kf);
-      AddAnimatorConnector( AnimatorConnector<int>::New( object,
+      AddAnimatorConnector( AnimatorConnector<int32_t>::New( object,
                                                          target.propertyIndex,
                                                          target.componentIndex,
                                                          new KeyFrameIntegerFunctor(kfCopy,interpolation),
@@ -753,7 +819,7 @@ void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Alph
 bool Animation::HasFinished()
 {
   bool hasFinished(false);
-  const int playedCount(mAnimation->GetPlayedCount());
+  const int32_t playedCount(mAnimation->GetPlayedCount());
 
   // If the play count has been incremented, then another notification is required
   mCurrentLoop = mAnimation->GetCurrentLoop();
@@ -776,6 +842,11 @@ Dali::Animation::AnimationSignalType& Animation::FinishedSignal()
   return mFinishedSignal;
 }
 
+Dali::Animation::AnimationSignalType& Animation::ProgressReachedSignal()
+{
+  return mProgressReachedSignal;
+}
+
 void Animation::EmitSignalFinish()
 {
   if ( !mFinishedSignal.Empty() )
@@ -785,19 +856,24 @@ void Animation::EmitSignalFinish()
   }
 }
 
+void Animation::EmitSignalProgressReached()
+{
+  if ( !mProgressReachedSignal.Empty() )
+  {
+    Dali::Animation handle( this );
+    mProgressReachedSignal.Emit( handle );
+  }
+}
+
 bool Animation::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
 {
-  bool connected( true );
+  bool connected( false );
   Animation* animation = static_cast< Animation* >(object); // TypeRegistry guarantees that this is the correct type.
 
   if( 0 == signalName.compare( SIGNAL_FINISHED ) )
   {
     animation->FinishedSignal().Connect( tracker, functor );
-  }
-  else
-  {
-    // signalName does not match any signal
-    connected = false;
+    connected = true;
   }
 
   return connected;
@@ -921,12 +997,13 @@ void Animation::SetCurrentProgress(float progress)
 
 float Animation::GetCurrentProgress()
 {
-  if( mAnimation )
+  float progress = 0.f;
+  if( mAnimation ) // always exists in practice
   {
-    return mAnimation->GetCurrentProgress();
+    progress = mAnimation->GetCurrentProgress();
   }
 
-  return 0.0f;
+  return progress;
 }
 
 void Animation::ExtendDuration( const TimePeriod& timePeriod )
@@ -978,6 +1055,56 @@ Vector2 Animation::GetPlayRange() const
   return mPlayRange;
 }
 
+void Animation::SetLoopingMode( Dali::Animation::LoopingMode loopingMode )
+{
+  mAutoReverseEnabled = ( loopingMode == Dali::Animation::LoopingMode::AUTO_REVERSE );
+
+  // mAnimation is being used in a separate thread; queue a message to set play range
+  SetLoopingModeMessage( mEventThreadServices, *mAnimation, mAutoReverseEnabled );
+}
+
+Dali::Animation::LoopingMode Animation::GetLoopingMode() const
+{
+  return mAutoReverseEnabled ? Dali::Animation::AUTO_REVERSE : Dali::Animation::RESTART;
+}
+
+bool Animation::CompareConnectorEndTimes( const Animation::ConnectorTargetValues& lhs, const Animation::ConnectorTargetValues& rhs )
+{
+  return ( ( lhs.timePeriod.delaySeconds + lhs.timePeriod.durationSeconds ) < ( rhs.timePeriod.delaySeconds + rhs.timePeriod.durationSeconds ) );
+}
+
+void Animation::NotifyObjects()
+{
+  if( mEndAction != EndAction::Discard ) // If the animation is discarded, then we do not want to change the target values
+  {
+    // Sort according to end time with earlier end times coming first, if the end time is the same, then the connectors are not moved
+    std::stable_sort( mConnectorTargetValues.begin(), mConnectorTargetValues.end(), CompareConnectorEndTimes );
+
+    // Loop through all connector target values sorted by increasing end time
+    ConnectorTargetValuesContainer::const_iterator iter = mConnectorTargetValues.begin();
+    const ConnectorTargetValuesContainer::const_iterator endIter = mConnectorTargetValues.end();
+    for( ; iter != endIter; ++iter )
+    {
+      AnimatorConnectorBase* connector = mConnectors[ iter->connectorIndex ];
+
+      Object* object = connector->GetObject();
+      if( object )
+      {
+        object->NotifyPropertyAnimation( *this, connector->GetPropertyIndex(), iter->targetValue, iter->animatorType );
+      }
+    }
+  }
+}
+
+
+void Animation::SendFinalProgressNotificationMessage()
+{
+  if ( mProgressReachedMarker > 0.0f )
+  {
+    float progressMarkerSeconds = mDurationSeconds * mProgressReachedMarker;
+    SetProgressNotificationMessage( mEventThreadServices, *mAnimation, progressMarkerSeconds );
+  }
+}
 
 } // namespace Internal