2 * Copyright(c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 using global::System.Runtime.InteropServices;
20 using System.ComponentModel;
21 using System.Collections.Generic;
22 using System.Globalization;
24 namespace Tizen.NUI.BaseComponents
27 /// LottieAnimationView renders an animated vector image (Lottie file).
29 /// <since_tizen> 7 </since_tizen>
30 public partial class LottieAnimationView : ImageView
32 #region Constructor, Destructor, Dispose
34 /// LottieAnimationView constructor
36 /// <param name="scale">The factor of scaling image, default : 1.0f</param>
37 /// <param name="shown">false : not displayed (hidden), true : displayed (shown), default : true</param>
39 /// If the shown parameter is false, the animation is not visible even if the LottieAnimationView instance is created.
43 /// LottieAnimationView myLottie = new LottieAnimationView();
44 /// LottieAnimationView myLottie2 = new LottieAnimationView(2.0f);
45 /// LottieAnimationView myLottie3 = new LottieAnimationView(1.0f, false);
48 /// <since_tizen> 7 </since_tizen>
49 public LottieAnimationView(float scale = 1.0f, bool shown = true) : base()
51 ActionPlay = Interop.LottieAnimationView.AnimatedVectorImageVisualActionPlayGet();
52 ActionPause = Interop.LottieAnimationView.AnimatedVectorImageVisualActionPauseGet();
53 ActionStop = Interop.LottieAnimationView.AnimatedVectorImageVisualActionStopGet();
55 NUILog.Debug($"< constructor GetId={GetId()} >");
56 currentStates.url = "";
57 currentStates.frame = -1;
58 currentStates.loopCount = 1;
59 currentStates.loopMode = LoopingModeType.Restart;
60 currentStates.stopEndAction = StopBehaviorType.CurrentFrame;
61 currentStates.framePlayRangeMin = -1;
62 currentStates.framePlayRangeMax = -1;
63 currentStates.totalFrame = -1;
64 currentStates.scale = scale;
65 currentStates.redrawInScalingDown = true;
67 // Set changed flag as true when initalized state.
68 // After some properties change, LottieAnimationView.UpdateImage will apply these inital values.
69 currentStates.changed = true;
74 /// Dispose(DisposeTypes type)
76 /// <param name="type"></param>
77 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
78 [EditorBrowsable(EditorBrowsableState.Never)]
79 protected override void Dispose(DisposeTypes type)
86 CleanCallbackDictionaries();
88 //Release your own unmanaged resources here.
89 //You should not access any managed member here except static instance.
90 //because the execution order of Finalizes is non-deterministic.
92 //disconnect event signal
93 if (finishedEventHandler != null && visualEventSignalCallback != null)
95 using VisualEventSignal visualEvent = VisualEventSignal();
96 visualEvent.Disconnect(visualEventSignalCallback);
97 finishedEventHandler = null;
98 NUILog.Debug($"disconnect event signal");
104 // This is used for internal purpose. hidden API.
105 [EditorBrowsable(EditorBrowsableState.Never)]
106 protected override void Dispose(bool disposing)
108 CleanCallbackDictionaries();
109 base.Dispose(disposing);
111 #endregion Constructor, Destructor, Dispose
116 /// Set or Get resource URL of Lottie file.
118 /// <since_tizen> 7 </since_tizen>
123 return GetValue(URLProperty) as string;
127 SetValue(URLProperty, value);
128 NotifyPropertyChanged();
132 private string InternalURL
136 string ret = (value == null ? "" : value);
137 currentStates.url = ret;
139 NUILog.Debug($"<[{GetId()}]SET url={currentStates.url}");
141 using PropertyMap map = new PropertyMap();
142 using PropertyValue type = new PropertyValue((int)Visual.Type.AnimatedVectorImage);
143 using PropertyValue url = new PropertyValue(currentStates.url);
144 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
145 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
146 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
147 using PropertyValue redrawInScalingDown = new PropertyValue(currentStates.redrawInScalingDown);
149 map.Add(Visual.Property.Type, type)
150 .Add(ImageVisualProperty.URL, url)
151 .Add(ImageVisualProperty.LoopCount, loopCnt)
152 .Add(ImageVisualProperty.StopBehavior, stopAction)
153 .Add(ImageVisualProperty.LoopingMode, loopMode)
154 .Add(ImageVisualProperty.RedrawInScalingDown, redrawInScalingDown);
157 // All states applied well.
158 currentStates.changed = false;
160 currentStates.contentInfo = null;
162 if (currentStates.scale != 1.0f)
164 Scale = new Vector3(currentStates.scale, currentStates.scale, 0.0f);
166 NUILog.Debug($"<[{GetId()}]>");
170 string ret = currentStates.url;
171 NUILog.Debug($"<[{GetId()}] GET");
173 using PropertyMap map = base.Image;
176 using PropertyValue val = map.Find(ImageVisualProperty.URL);
179 if (val.Get(out ret))
181 NUILog.Debug($"gotten url={ret} >");
186 Tizen.Log.Error(tag, $" [ERROR][{GetId()}](LottieAnimationView) Fail to get URL from dali >");
192 /// Gets the playing state
194 /// <since_tizen> 7 </since_tizen>
195 public PlayStateType PlayState
199 NUILog.Debug($"< Get!");
200 using PropertyMap map = base.Image;
204 using PropertyValue val = map.Find(ImageVisualProperty.PlayState);
207 if (val.Get(out ret))
209 currentStates.playState = (PlayStateType)ret;
210 NUILog.Debug($"gotten play state={ret} >");
216 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}]Fail to get PlayState from dali currentStates.playState={currentStates.playState}>");
218 return currentStates.playState;
223 /// Get the number of total frames
225 /// <since_tizen> 7 </since_tizen>
226 public int TotalFrame
231 using PropertyMap map = base.Image;
234 using PropertyValue val = map.Find(ImageVisualProperty.TotalFrameNumber);
237 if (val.Get(out ret))
239 NUILog.Debug($"TotalFrameNumber get! ret={ret}");
240 currentStates.totalFrame = ret;
245 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get TotalFrameNumber from dali>");
251 /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
254 /// Gets the value set by a user. If the setting value is out-ranged, it is reset as a minimum frame or a maximum frame.
257 /// We assume that the animation in myLottie.json file has 100 frames originally. If so, its frame index will be 0 - 99.
259 /// LottieAnimationView myLottie = new LottieAnimationView();
260 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
261 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
262 /// myLottie.CurrentFrame = 200; //display 99 frame
263 /// myLottie.SetMinMaxFrame(10, 20);
264 /// myLottie.CurrentFrame = 15; //display 15 frame
265 /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
268 /// <since_tizen> 7 </since_tizen>
269 public int CurrentFrame
273 return (int)GetValue(CurrentFrameProperty);
277 SetValue(CurrentFrameProperty, value);
278 NotifyPropertyChanged();
282 private int InternalCurrentFrame
286 currentStates.frame = value;
287 NUILog.Debug($"<[{GetId()}]SET frame={currentStates.frame}>");
288 using PropertyValue attribute = new PropertyValue(currentStates.frame);
289 DoAction(ImageView.Property.IMAGE, ActionJumpTo, attribute);
294 using PropertyMap map = base.Image;
297 using PropertyValue val = map.Find(ImageVisualProperty.CurrentFrameNumber);
300 if (val.Get(out ret))
302 NUILog.Debug($"CurrentFrameNumber get! val={ret}");
307 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get CurrentFrameNumber from dali!! ret={ret}>");
313 /// Sets or gets the looping mode of Lottie animation.
315 /// <since_tizen> 7 </since_tizen>
316 public LoopingModeType LoopingMode
320 return (LoopingModeType)GetValue(LoopingModeProperty);
324 SetValue(LoopingModeProperty, value);
325 NotifyPropertyChanged();
329 private LoopingModeType InternalLoopingMode
333 currentStates.loopMode = (LoopingModeType)value;
334 currentStates.changed = true;
336 NUILog.Debug($"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
337 using PropertyMap map = new PropertyMap();
338 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
339 map.Add(ImageVisualProperty.LoopingMode, loopMode);
340 using PropertyValue attribute = new PropertyValue(map);
341 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
345 NUILog.Debug($"LoopMode get!");
346 using PropertyMap map = base.Image;
350 using PropertyValue val = map.Find(ImageVisualProperty.LoopingMode);
353 if (val.Get(out ret))
355 NUILog.Debug($"gotten LoopMode={ret}");
356 if (ret != (int)currentStates.loopMode && ret > 0)
358 NUILog.Debug($" [ERROR][{GetId()}](LottieAnimationView) different LoopMode! gotten={ret}, loopMode={currentStates.loopMode}");
360 currentStates.loopMode = (LoopingModeType)ret;
361 return (LoopingModeType)ret;
365 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get loopMode from dali>");
366 return currentStates.loopMode;
371 /// Sets or gets the loop count.
374 /// The minus value means the infinite loop count.
378 /// LottieAnimationView myLottie = new LottieAnimationView();
379 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
380 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
381 /// myLottie.LoopCount = -1; //infinite loop
383 /// myLottie.Stop(); //it plays continuously unless Stop() is called
384 /// myLottie.LoopCount = 2;
385 /// myLottie.Play(); //it plays only 2 times and stops automatically
388 /// <since_tizen> 7 </since_tizen>
393 return (int)GetValue(LoopCountProperty);
397 SetValue(LoopCountProperty, value);
398 NotifyPropertyChanged();
402 private int InternalLoopCount
406 currentStates.changed = true;
407 currentStates.loopCount = value;
408 NUILog.Debug($"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
409 using PropertyMap map = new PropertyMap();
410 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
411 map.Add(ImageVisualProperty.LoopCount, loopCnt);
412 using PropertyValue attribute = new PropertyValue(map);
413 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
417 NUILog.Debug($"LoopCount get!");
418 using PropertyMap map = base.Image;
422 using PropertyValue val = map.Find(ImageVisualProperty.LoopCount);
425 if (val.Get(out ret))
427 NUILog.Debug($"gotten loop count={ret}");
428 if (ret != currentStates.loopCount && ret > 0)
430 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different loop count! gotten={ret}, loopCount={currentStates.loopCount}>");
432 currentStates.loopCount = ret;
433 return currentStates.loopCount;
437 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get LoopCount from dali currentStates.loopCount={currentStates.loopCount}>");
438 return currentStates.loopCount;
443 /// Sets or gets the stop behavior.
445 /// <since_tizen> 7 </since_tizen>
446 public StopBehaviorType StopBehavior
450 return (StopBehaviorType)GetValue(StopBehaviorProperty);
454 SetValue(StopBehaviorProperty, value);
455 NotifyPropertyChanged();
459 private StopBehaviorType InternalStopBehavior
463 currentStates.stopEndAction = (StopBehaviorType)value;
464 currentStates.changed = true;
466 NUILog.Debug($"<[{GetId()}]SET val={currentStates.stopEndAction}>");
467 using PropertyMap map = new PropertyMap();
468 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
469 map.Add(ImageVisualProperty.StopBehavior, stopAction);
470 using PropertyValue attribute = new PropertyValue(map);
471 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
475 NUILog.Debug($"StopBehavior get!");
476 using PropertyMap map = base.Image;
480 using PropertyValue val = map.Find(ImageVisualProperty.StopBehavior);
483 if (val.Get(out ret))
485 NUILog.Debug($"gotten StopBehavior={ret}");
486 if (ret != (int)currentStates.stopEndAction)
488 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different StopBehavior! gotten={ret}, StopBehavior={currentStates.stopEndAction}>");
490 currentStates.stopEndAction = (StopBehaviorType)ret;
491 return (StopBehaviorType)ret;
495 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get StopBehavior from dali>");
496 return currentStates.stopEndAction;
501 /// Whether to redraw the image when the visual is scaled down.
505 /// It is used in the AnimatedVectorImageVisual.The default is true.
507 [EditorBrowsable(EditorBrowsableState.Never)]
508 public bool RedrawInScalingDown
512 return (bool)GetValue(RedrawInScalingDownProperty);
516 SetValue(RedrawInScalingDownProperty, value);
517 NotifyPropertyChanged();
521 private bool InternalRedrawInScalingDown
525 currentStates.changed = true;
526 currentStates.redrawInScalingDown = value;
527 NUILog.Debug($"<[{GetId()}]SET currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
528 using PropertyMap map = new PropertyMap();
529 using PropertyValue redraw = new PropertyValue(currentStates.redrawInScalingDown);
530 map.Add(ImageVisualProperty.RedrawInScalingDown, redraw);
531 using PropertyValue action = new PropertyValue(map);
532 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, action);
536 PropertyMap map = base.Image;
540 using PropertyValue val = map.Find(ImageVisualProperty.RedrawInScalingDown);
543 if (val.Get(out ret))
545 if (ret != currentStates.redrawInScalingDown)
547 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different redrawInScalingDown! gotten={ret}, redrawInScalingDown={currentStates.redrawInScalingDown}>");
549 currentStates.redrawInScalingDown = ret;
550 return currentStates.redrawInScalingDown;
554 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get redrawInScalingDown from dali currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
555 return currentStates.redrawInScalingDown;
561 /// Actions property value to Jump to the specified frame.
562 /// This property can be redefined by child class if it use different value.
564 [EditorBrowsable(EditorBrowsableState.Never)]
565 protected int ActionJumpTo { get; set; } = Interop.LottieAnimationView.AnimatedVectorImageVisualActionJumpToGet();
567 // This is used for internal purpose. hidden API.
568 [EditorBrowsable(EditorBrowsableState.Never)]
569 protected int SetDynamicProperty => ActionJumpTo + 1;
575 /// Set the minimum and the maximum frame.
577 /// <param name="minFrame">minimum frame</param>
578 /// <param name="maxFrame">maximum frame</param>
579 /// <since_tizen> 7 </since_tizen>
580 public void SetMinMaxFrame(int minFrame, int maxFrame)
582 NUILog.Debug($"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
584 currentStates.changed = true;
585 currentStates.framePlayRangeMin = minFrame;
586 currentStates.framePlayRangeMax = maxFrame;
588 using PropertyArray array = new PropertyArray();
589 using PropertyValue min = new PropertyValue(currentStates.framePlayRangeMin);
590 using PropertyValue max = new PropertyValue(currentStates.framePlayRangeMax);
594 using PropertyMap map = new PropertyMap();
595 using PropertyValue range = new PropertyValue(array);
596 map.Add(ImageVisualProperty.PlayRange, range);
597 using PropertyValue action = new PropertyValue(map);
598 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, action);
599 NUILog.Debug($" [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
605 /// <since_tizen> 7 </since_tizen>
606 public new void Play()
608 NUILog.Debug($"<[{GetId()}] Play()");
611 NUILog.Debug($"[{GetId()}]>");
617 /// <since_tizen> 7 </since_tizen>
618 public new void Pause()
620 NUILog.Debug($"<[{GetId()}] Pause()>");
623 NUILog.Debug($"[{GetId()}]>");
629 /// <since_tizen> 7 </since_tizen>
630 public new void Stop()
632 NUILog.Debug($"<[{GetId()}] Stop()");
635 NUILog.Debug($"[{GetId()}]>");
639 /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
641 /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
642 /// <since_tizen> 7 </since_tizen>
643 public List<Tuple<string, int, int>> GetContentInfo()
647 if (currentStates.contentInfo != null)
649 return currentStates.contentInfo;
652 PropertyMap imageMap = base.Image;
653 if (imageMap != null)
655 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
656 PropertyMap contentMap = new PropertyMap();
657 if (val?.Get(ref contentMap) == true)
659 currentStates.contentInfo = new List<Tuple<string, int, int>>();
660 for (uint i = 0; i < contentMap.Count(); i++)
662 using PropertyKey propertyKey = contentMap.GetKeyAt(i);
663 string key = propertyKey.StringKey;
665 using PropertyValue arrVal = contentMap.GetValue(i);
666 using PropertyArray arr = new PropertyArray();
670 using PropertyValue start = arr.GetElementAt(0);
671 start?.Get(out startFrame);
674 using PropertyValue end = arr.GetElementAt(1);
675 end?.Get(out endFrame);
677 NUILog.Debug($"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
679 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
681 currentStates.contentInfo?.Add(item);
685 contentMap.Dispose();
690 return currentStates.contentInfo;
694 /// A marker has its start frame and end frame.
695 /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
696 /// Or animation will play between the start frame of the first marker and the end frame of the second marker if two markers are specified. *
698 /// <param name="marker1">First marker</param>
699 /// <param name="marker2">Second marker</param>
700 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
701 [EditorBrowsable(EditorBrowsableState.Never)]
702 public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
704 NUILog.Debug($"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
706 currentStates.changed = true;
707 currentStates.mark1 = marker1;
708 currentStates.mark2 = marker2;
710 using PropertyArray array = new PropertyArray();
711 using PropertyValue mark1 = new PropertyValue(currentStates.mark1);
712 array.PushBack(mark1);
713 using PropertyValue mark2 = new PropertyValue(currentStates.mark2);
716 array.PushBack(mark2);
719 using PropertyMap map = new PropertyMap();
720 using PropertyValue range = new PropertyValue(array);
721 map.Add(ImageVisualProperty.PlayRange, range);
722 using PropertyValue actionProperty = new PropertyValue(map);
723 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, actionProperty);
724 NUILog.Debug($" [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
730 /// <returns>Tuple of Min and Max frames</returns>
731 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
732 [EditorBrowsable(EditorBrowsableState.Never)]
733 public Tuple<int, int> GetMinMaxFrame()
735 NUILog.Debug($"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
737 using PropertyMap map = Image;
740 using PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
743 using PropertyArray array = new PropertyArray();
746 uint cnt = array.Count();
747 int item1 = -1, item2 = -1;
748 for (uint i = 0; i < cnt; i++)
750 using PropertyValue v = array.GetElementAt(i);
752 if (v.Get(out intRet))
754 NUILog.Debug($"Got play range of string [{i}]: {intRet}");
766 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
769 NUILog.Debug($" [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
770 return new Tuple<int, int>(item1, item2);
774 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
775 return new Tuple<int, int>(-1, -1);
778 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
779 [EditorBrowsable(EditorBrowsableState.Never)]
780 public void DoActionExtension(LottieAnimationViewDynamicProperty info)
782 dynamicPropertyCallbackId++;
784 weakReferencesOfLottie?.Add(dynamicPropertyCallbackId, new WeakReference<LottieAnimationView>(this));
785 InternalSavedDynamicPropertyCallbacks?.Add(dynamicPropertyCallbackId, info.Callback);
787 Interop.View.DoActionExtension(SwigCPtr, ImageView.Property.IMAGE, SetDynamicProperty, dynamicPropertyCallbackId, info.KeyPath, (int)info.Property, Marshal.GetFunctionPointerForDelegate<System.Delegate>(rootCallback));
789 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
792 private void CleanCallbackDictionaries()
794 if (weakReferencesOfLottie?.Count > 0 && InternalSavedDynamicPropertyCallbacks != null)
796 foreach (var key in InternalSavedDynamicPropertyCallbacks?.Keys)
798 if (weakReferencesOfLottie.ContainsKey(key))
800 weakReferencesOfLottie.Remove(key);
804 InternalSavedDynamicPropertyCallbacks?.Clear();
805 InternalSavedDynamicPropertyCallbacks = null;
809 /// Update lottie-image-relative properties synchronously.
810 /// After call this API, All image properties updated.
812 [EditorBrowsable(EditorBrowsableState.Never)]
813 protected override void UpdateImage()
815 if (!imagePropertyUpdatedFlag) return;
817 // Update currentStates properties to cachedImagePropertyMap
818 if(currentStates.changed)
820 UpdateImage(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount));
821 UpdateImage(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction));
822 UpdateImage(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
823 UpdateImage(ImageVisualProperty.RedrawInScalingDown, new PropertyValue(currentStates.redrawInScalingDown));
824 currentStates.changed = false;
827 using PropertyValue animatiedImage = new PropertyValue((int)Visual.Type.AnimatedVectorImage);
828 UpdateImage(Visual.Property.Type, animatiedImage);
834 /// Update NUI cached animated image visual property map by inputed property map.
835 /// And call base.MergeCachedImageVisualProperty()
838 /// For performance issue, we will collect only "cachedLottieAnimationPropertyKeyList" hold in this class.
840 [EditorBrowsable(EditorBrowsableState.Never)]
841 protected override void MergeCachedImageVisualProperty(PropertyMap map)
843 if (map == null) return;
844 if (cachedImagePropertyMap == null)
846 cachedImagePropertyMap = new PropertyMap();
848 foreach (var key in cachedLottieAnimationPropertyKeyList)
850 PropertyValue value = map.Find(key);
853 // Update-or-Insert new value
854 cachedImagePropertyMap[key] = value;
857 base.MergeCachedImageVisualProperty(map);
862 #region Event, Enum, Struct, ETC
864 /// Animation finished event.
866 /// <since_tizen> 7 </since_tizen>
867 public event EventHandler Finished
871 if (finishedEventHandler == null)
873 NUILog.Debug($"<[{GetId()}] Finished eventhandler added>");
874 visualEventSignalCallback = onVisualEventSignal;
875 using VisualEventSignal visualEvent = VisualEventSignal();
876 visualEvent.Connect(visualEventSignalCallback);
878 finishedEventHandler += value;
882 NUILog.Debug($"<[{GetId()}] Finished eventhandler removed>");
883 finishedEventHandler -= value;
884 if (finishedEventHandler == null && visualEventSignalCallback != null)
886 using VisualEventSignal visualEvent = VisualEventSignal();
887 visualEvent.Disconnect(visualEventSignalCallback);
888 if (visualEvent?.Empty() == true)
890 visualEventSignalCallback = null;
897 /// Enumeration for what state the vector animation is in
899 /// <since_tizen> 7 </since_tizen>
900 public enum PlayStateType
905 /// <since_tizen> 7 </since_tizen>
908 /// Vector Animation has stopped
910 /// <since_tizen> 7 </since_tizen>
913 /// The vector animation is playing
915 /// <since_tizen> 7 </since_tizen>
918 /// The vector animation is paused
920 /// <since_tizen> 7 </since_tizen>
925 /// Enumeration for what to do when the animation is stopped.
927 /// <since_tizen> 7 </since_tizen>
928 public enum StopBehaviorType
931 /// When the animation is stopped, the current frame is shown.
933 /// <since_tizen> 7 </since_tizen>
936 /// When the animation is stopped, the min frame (first frame) is shown.
938 /// <since_tizen> 7 </since_tizen>
941 /// When the animation is stopped, the max frame (last frame) is shown.
943 /// <since_tizen> 7 </since_tizen>
948 /// Enumeration for what looping mode is in.
950 /// <since_tizen> 7 </since_tizen>
951 public enum LoopingModeType
954 /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
956 /// <since_tizen> 7 </since_tizen>
959 /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
961 /// <since_tizen> 7 </since_tizen>
968 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
969 [EditorBrowsable(EditorBrowsableState.Never)]
970 public enum VectorProperty
973 /// Fill color of the object, Type of <see cref="Vector3"/>
975 [EditorBrowsable(EditorBrowsableState.Never)]
979 /// Fill opacity of the object, Type of float
981 [EditorBrowsable(EditorBrowsableState.Never)]
985 /// Stroke color of the object, Type of <see cref="Vector3"/>
987 [EditorBrowsable(EditorBrowsableState.Never)]
991 /// Stroke opacity of the object, Type of float
993 [EditorBrowsable(EditorBrowsableState.Never)]
997 /// Stroke width of the object, Type of float
999 [EditorBrowsable(EditorBrowsableState.Never)]
1003 /// Transform anchor of the Layer and Group object, Type of <see cref="Vector2"/>
1005 [EditorBrowsable(EditorBrowsableState.Never)]
1009 /// Transform position of the Layer and Group object, Type of <see cref="Vector2"/>
1011 [EditorBrowsable(EditorBrowsableState.Never)]
1015 /// Transform scale of the Layer and Group object, Type of <see cref="Vector2"/>, Value range of [0..100]
1017 [EditorBrowsable(EditorBrowsableState.Never)]
1021 /// Transform rotation of the Layer and Group object, Type of float, Value range of [0..360] in degrees
1023 [EditorBrowsable(EditorBrowsableState.Never)]
1027 /// Transform opacity of the Layer and Group object, Type of float
1029 [EditorBrowsable(EditorBrowsableState.Never)]
1033 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
1034 [EditorBrowsable(EditorBrowsableState.Never)]
1035 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1036 public delegate PropertyValue DynamicPropertyCallbackType(int returnType, uint frameNumber);
1037 #endregion Event, Enum, Struct, ETC
1041 internal class VisualEventSignalArgs : EventArgs
1043 public int VisualIndex
1055 internal event EventHandler<VisualEventSignalArgs> VisualEvent
1059 if (visualEventSignalHandler == null)
1061 visualEventSignalCallback = onVisualEventSignal;
1062 using VisualEventSignal visualEvent = VisualEventSignal();
1063 visualEvent?.Connect(visualEventSignalCallback);
1065 visualEventSignalHandler += value;
1069 visualEventSignalHandler -= value;
1070 if (visualEventSignalHandler == null && visualEventSignalCallback != null)
1072 using VisualEventSignal visualEvent = VisualEventSignal();
1073 visualEvent?.Disconnect(visualEventSignalCallback);
1074 if (visualEvent?.Empty() == true)
1076 visualEventSignalCallback = null;
1082 internal void EmitVisualEventSignal(int visualIndex, int signalId)
1084 using VisualEventSignal visualEvent = VisualEventSignal();
1085 visualEvent?.Emit(this, visualIndex, signalId);
1088 internal VisualEventSignal VisualEventSignal()
1090 VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
1091 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
1095 internal Dictionary<int, DynamicPropertyCallbackType> InternalSavedDynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1097 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1098 internal delegate void RootCallbackType(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3);
1100 internal RootCallbackType rootCallback = RootCallback;
1102 static internal void RootCallback(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3)
1104 WeakReference<LottieAnimationView> current = null;
1105 LottieAnimationView currentView = null;
1106 DynamicPropertyCallbackType currentCallback = null;
1107 PropertyValue ret = null;
1109 if (weakReferencesOfLottie.TryGetValue(id, out current))
1111 if (current.TryGetTarget(out currentView))
1113 if (currentView != null && currentView.InternalSavedDynamicPropertyCallbacks != null &&
1114 currentView.InternalSavedDynamicPropertyCallbacks.TryGetValue(id, out currentCallback))
1116 ret = currentCallback?.Invoke(returnType, frameNumber);
1120 Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1126 Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1132 Tizen.Log.Error("NUI", "can't find LottieAnimationView by id, just return here!");
1138 case (int)(VectorProperty.FillColor):
1139 case (int)(VectorProperty.StrokeColor):
1140 Vector3 tmpVector3 = new Vector3(-1, -1, -1);
1141 if ((ret != null) && ret.Get(tmpVector3))
1143 val1 = tmpVector3.X;
1144 val2 = tmpVector3.Y;
1145 val3 = tmpVector3.Z;
1147 tmpVector3.Dispose();
1150 case (int)(VectorProperty.TransformAnchor):
1151 case (int)(VectorProperty.TransformPosition):
1152 case (int)(VectorProperty.TransformScale):
1153 Vector2 tmpVector2 = new Vector2(-1, -1);
1154 if ((ret != null) && ret.Get(tmpVector2))
1156 val1 = tmpVector2.X;
1157 val2 = tmpVector2.Y;
1159 tmpVector2.Dispose();
1162 case (int)(VectorProperty.FillOpacity):
1163 case (int)(VectorProperty.StrokeOpacity):
1164 case (int)(VectorProperty.StrokeWidth):
1165 case (int)(VectorProperty.TransformRotation):
1166 case (int)(VectorProperty.TransformOpacity):
1167 float tmpFloat = -1;
1168 if ((ret != null) && ret.Get(out tmpFloat))
1184 // Collection of lottie-image-sensitive properties.
1185 private static readonly List<int> cachedLottieAnimationPropertyKeyList = new List<int> {
1186 ImageVisualProperty.LoopCount,
1187 ImageVisualProperty.StopBehavior,
1188 ImageVisualProperty.LoopingMode,
1189 ImageVisualProperty.RedrawInScalingDown,
1192 private struct states
1194 internal string url;
1196 internal int loopCount;
1197 internal LoopingModeType loopMode;
1198 internal StopBehaviorType stopEndAction;
1199 internal int framePlayRangeMin;
1200 internal int framePlayRangeMax;
1201 internal int totalFrame;
1202 internal float scale;
1203 internal PlayStateType playState;
1204 internal List<Tuple<string, int, int>> contentInfo;
1205 internal string mark1, mark2;
1206 internal bool redrawInScalingDown;
1207 internal bool changed;
1209 private states currentStates;
1211 private const string tag = "NUITEST";
1212 private event EventHandler finishedEventHandler;
1214 private void OnFinished()
1216 NUILog.Debug($"<[{GetId()}] OnFinished()>");
1217 finishedEventHandler?.Invoke(this, null);
1220 private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
1224 if (targetView != IntPtr.Zero)
1226 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
1229 NUILog.Debug($"targetView is not null! name={v.Name}");
1233 NUILog.Debug($"target is something created from dali");
1236 VisualEventSignalArgs e = new VisualEventSignalArgs();
1237 e.VisualIndex = visualIndex;
1238 e.SignalId = signalId;
1239 visualEventSignalHandler?.Invoke(this, e);
1241 NUILog.Debug($"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
1244 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
1245 private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
1247 private VisualEventSignalCallbackType visualEventSignalCallback;
1248 private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
1250 static private int dynamicPropertyCallbackId = 0;
1251 //static private Dictionary<int, DynamicPropertyCallbackType> dynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1252 static private Dictionary<int, WeakReference<LottieAnimationView>> weakReferencesOfLottie = new Dictionary<int, WeakReference<LottieAnimationView>>();
1254 private void debugPrint()
1256 NUILog.Debug($"===================================");
1257 NUILog.Debug($"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, \nframePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
1258 NUILog.Debug($" get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState}");
1259 NUILog.Debug($" RedrawInScalingDown={RedrawInScalingDown} >");
1260 NUILog.Debug($"===================================");
1267 /// A class containing frame informations for a LottieAnimationView.
1269 [EditorBrowsable(EditorBrowsableState.Never)]
1270 public class LottieFrameInfo : ICloneable
1273 /// Creates a new instance with a playing range.
1275 [EditorBrowsable(EditorBrowsableState.Never)]
1276 public LottieFrameInfo(int startFrame, int endFrame)
1278 StartFrame = startFrame;
1279 EndFrame = endFrame;
1283 /// Creates a new instance with a still image frame.
1285 [EditorBrowsable(EditorBrowsableState.Never)]
1286 public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
1291 /// Create a new instance from a pair notation.
1293 [EditorBrowsable(EditorBrowsableState.Never)]
1294 public static implicit operator LottieFrameInfo((int, int) pair)
1296 return new LottieFrameInfo(pair.Item1, pair.Item2);
1300 /// Create a new instance from an int value.
1302 [EditorBrowsable(EditorBrowsableState.Never)]
1303 public static implicit operator LottieFrameInfo(int stillImageFrame)
1305 return new LottieFrameInfo(stillImageFrame);
1309 /// Create a new instance from string.
1310 /// Possible input : "0, 10", "10"
1312 [EditorBrowsable(EditorBrowsableState.Never)]
1313 public static implicit operator LottieFrameInfo(string pair)
1320 string[] parts = pair.Split(',');
1321 if (parts.Length == 1)
1323 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture));
1325 else if (parts.Length == 2)
1327 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), Int32.Parse(parts[1].Trim(), CultureInfo.InvariantCulture));
1330 Tizen.Log.Error("NUI", $"Can not convert string {pair} to LottieFrameInfo");
1335 /// The start frame of the lottie animation.
1337 [EditorBrowsable(EditorBrowsableState.Never)]
1338 public int StartFrame { get; }
1341 /// The end frame of the lottie animation.
1343 [EditorBrowsable(EditorBrowsableState.Never)]
1344 public int EndFrame { get; }
1347 /// Create LottieFrameInfo struct with animation range information
1349 [EditorBrowsable(EditorBrowsableState.Never)]
1350 public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
1352 return new LottieFrameInfo(startFrame, endFrame);
1356 /// Create LottieFrameInfo struct with still image information
1358 [EditorBrowsable(EditorBrowsableState.Never)]
1359 public static LottieFrameInfo CreateStillImage(int stillImageFrame)
1361 return new LottieFrameInfo(stillImageFrame, stillImageFrame);
1366 /// Whether this LottieFrameInfo represents one frame or more.
1368 [EditorBrowsable(EditorBrowsableState.Never)]
1369 public bool IsStillImage()
1371 return StartFrame == EndFrame;
1376 /// Play specified LottieAnimationView with this frame information.
1378 /// <param name="lottieView">The target LottieAnimationView to play.</param>
1379 /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
1380 [EditorBrowsable(EditorBrowsableState.Never)]
1381 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062: Validate arguments of public methods", Justification = "The null checking is done by BeReadyToShow()")]
1382 public void Show(LottieAnimationView lottieView, bool noPlay = false)
1384 if (!BeReadyToShow(lottieView))
1389 lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
1391 if (IsStillImage() || noPlay)
1393 lottieView.CurrentFrame = EndFrame;
1397 lottieView.CurrentFrame = StartFrame;
1403 [EditorBrowsable(EditorBrowsableState.Never)]
1404 public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
1406 private bool BeReadyToShow(LottieAnimationView lottieView)
1408 // Validate input lottieView
1409 if (null == lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
1414 // Stop if it was playing
1415 if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)
1424 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
1425 [EditorBrowsable(EditorBrowsableState.Never)]
1426 public struct LottieAnimationViewDynamicProperty : IEquatable<LottieAnimationViewDynamicProperty>
1428 [EditorBrowsable(EditorBrowsableState.Never)]
1429 public string KeyPath { get; set; }
1431 [EditorBrowsable(EditorBrowsableState.Never)]
1432 public LottieAnimationView.VectorProperty Property { get; set; }
1434 [EditorBrowsable(EditorBrowsableState.Never)]
1435 public LottieAnimationView.DynamicPropertyCallbackType Callback { get; set; }
1437 public override bool Equals(object obj)
1439 if (obj is LottieAnimationViewDynamicProperty target)
1441 if (KeyPath == target.KeyPath && Property == target.Property && Callback == target.Callback)
1449 public override int GetHashCode()
1451 return base.GetHashCode();
1454 public static bool operator ==(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1456 return left.Equals(right);
1459 public static bool operator !=(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1461 return !(left == right);
1464 public bool Equals(LottieAnimationViewDynamicProperty other)
1468 if (KeyPath == other.KeyPath && Property == other.Property && Callback == other.Callback)