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.changed = false;
64 currentStates.totalFrame = -1;
65 currentStates.scale = scale;
66 currentStates.redrawInScalingDown = true;
71 /// Dispose(DisposeTypes type)
73 /// <param name="type"></param>
74 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
75 [EditorBrowsable(EditorBrowsableState.Never)]
76 protected override void Dispose(DisposeTypes type)
83 CleanCallbackDictionaries();
85 //Release your own unmanaged resources here.
86 //You should not access any managed member here except static instance.
87 //because the execution order of Finalizes is non-deterministic.
89 //disconnect event signal
90 if (finishedEventHandler != null && visualEventSignalCallback != null)
92 using VisualEventSignal visualEvent = VisualEventSignal();
93 visualEvent.Disconnect(visualEventSignalCallback);
94 finishedEventHandler = null;
95 NUILog.Debug($"disconnect event signal");
101 // This is used for internal purpose. hidden API.
102 [EditorBrowsable(EditorBrowsableState.Never)]
103 protected override void Dispose(bool disposing)
105 CleanCallbackDictionaries();
106 base.Dispose(disposing);
108 #endregion Constructor, Destructor, Dispose
113 /// Set or Get resource URL of Lottie file.
115 /// <since_tizen> 7 </since_tizen>
120 return GetValue(URLProperty) as string;
124 SetValue(URLProperty, value);
125 NotifyPropertyChanged();
129 private string InternalURL
133 string ret = (value == null ? "" : value);
134 currentStates.url = ret;
135 currentStates.changed = true;
137 NUILog.Debug($"<[{GetId()}]SET url={currentStates.url}");
139 using PropertyMap map = new PropertyMap();
140 using PropertyValue type = new PropertyValue((int)DevelVisual.Type.AnimatedVectorImage);
141 using PropertyValue url = new PropertyValue(currentStates.url);
142 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
143 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
144 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
146 map.Add(Visual.Property.Type, type)
147 .Add(ImageVisualProperty.URL, url)
148 .Add(ImageVisualProperty.LoopCount, loopCnt)
149 .Add(ImageVisualProperty.StopBehavior, stopAction)
150 .Add(ImageVisualProperty.LoopingMode, loopMode);
153 currentStates.contentInfo = null;
155 if (currentStates.scale != 1.0f)
157 Scale = new Vector3(currentStates.scale, currentStates.scale, 0.0f);
159 NUILog.Debug($"<[{GetId()}]>");
163 string ret = currentStates.url;
164 NUILog.Debug($"<[{GetId()}] GET");
166 using PropertyMap map = base.Image;
169 using PropertyValue val = map.Find(ImageVisualProperty.URL);
172 if (val.Get(out ret))
174 NUILog.Debug($"gotten url={ret} >");
179 Tizen.Log.Error(tag, $" [ERROR][{GetId()}](LottieAnimationView) Fail to get URL from dali >");
185 /// Gets the playing state
187 /// <since_tizen> 7 </since_tizen>
188 public PlayStateType PlayState
192 NUILog.Debug($"< Get!");
193 using PropertyMap map = base.Image;
197 using PropertyValue val = map.Find(ImageVisualProperty.PlayState);
200 if (val.Get(out ret))
202 currentStates.playState = (PlayStateType)ret;
203 NUILog.Debug($"gotten play state={ret} >");
209 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}]Fail to get PlayState from dali currentStates.playState={currentStates.playState}>");
211 return currentStates.playState;
216 /// Get the number of total frames
218 /// <since_tizen> 7 </since_tizen>
219 public int TotalFrame
224 using PropertyMap map = base.Image;
227 using PropertyValue val = map.Find(ImageVisualProperty.TotalFrameNumber);
230 if (val.Get(out ret))
232 NUILog.Debug($"TotalFrameNumber get! ret={ret}");
233 currentStates.totalFrame = ret;
238 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get TotalFrameNumber from dali>");
244 /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
247 /// 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.
250 /// We assume that the animation in myLottie.json file has 100 frames originally. If so, its frame index will be 0 - 99.
252 /// LottieAnimationView myLottie = new LottieAnimationView();
253 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
254 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
255 /// myLottie.CurrentFrame = 200; //display 99 frame
256 /// myLottie.SetMinMaxFrame(10, 20);
257 /// myLottie.CurrentFrame = 15; //display 15 frame
258 /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
261 /// <since_tizen> 7 </since_tizen>
262 public int CurrentFrame
266 return (int)GetValue(CurrentFrameProperty);
270 SetValue(CurrentFrameProperty, value);
271 NotifyPropertyChanged();
275 private int InternalCurrentFrame
279 currentStates.frame = value;
280 NUILog.Debug($"<[{GetId()}]SET frame={currentStates.frame}>");
281 using PropertyValue attribute = new PropertyValue(currentStates.frame);
282 DoAction(ImageView.Property.IMAGE, ActionJumpTo, attribute);
287 using PropertyMap map = base.Image;
290 using PropertyValue val = map.Find(ImageVisualProperty.CurrentFrameNumber);
293 if (val.Get(out ret))
295 NUILog.Debug($"CurrentFrameNumber get! val={ret}");
300 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get CurrentFrameNumber from dali!! ret={ret}>");
306 /// Sets or gets the looping mode of Lottie animation.
308 /// <since_tizen> 7 </since_tizen>
309 public LoopingModeType LoopingMode
313 return (LoopingModeType)GetValue(LoopingModeProperty);
317 SetValue(LoopingModeProperty, value);
318 NotifyPropertyChanged();
322 private LoopingModeType InternalLoopingMode
326 currentStates.loopMode = (LoopingModeType)value;
327 currentStates.changed = true;
329 NUILog.Debug($"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
330 using PropertyMap map = new PropertyMap();
331 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
332 map.Add(ImageVisualProperty.LoopingMode, loopMode);
333 using PropertyValue attribute = new PropertyValue(map);
334 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
338 NUILog.Debug($"LoopMode get!");
339 using PropertyMap map = base.Image;
343 using PropertyValue val = map.Find(ImageVisualProperty.LoopingMode);
346 if (val.Get(out ret))
348 NUILog.Debug($"gotten LoopMode={ret}");
349 if (ret != (int)currentStates.loopMode && ret > 0)
351 NUILog.Debug($" [ERROR][{GetId()}](LottieAnimationView) different LoopMode! gotten={ret}, loopMode={currentStates.loopMode}");
353 currentStates.loopMode = (LoopingModeType)ret;
354 return (LoopingModeType)ret;
358 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get loopMode from dali>");
359 return currentStates.loopMode;
364 /// Sets or gets the loop count.
367 /// The minus value means the infinite loop count.
371 /// LottieAnimationView myLottie = new LottieAnimationView();
372 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
373 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
374 /// myLottie.LoopCount = -1; //infinite loop
376 /// myLottie.Stop(); //it plays continuously unless Stop() is called
377 /// myLottie.LoopCount = 2;
378 /// myLottie.Play(); //it plays only 2 times and stops automatically
381 /// <since_tizen> 7 </since_tizen>
386 return (int)GetValue(LoopCountProperty);
390 SetValue(LoopCountProperty, value);
391 NotifyPropertyChanged();
395 private int InternalLoopCount
399 currentStates.changed = true;
400 currentStates.loopCount = value;
401 NUILog.Debug($"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
402 using PropertyMap map = new PropertyMap();
403 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
404 map.Add(ImageVisualProperty.LoopCount, loopCnt);
405 using PropertyValue attribute = new PropertyValue(map);
406 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
410 NUILog.Debug($"LoopCount get!");
411 using PropertyMap map = base.Image;
415 using PropertyValue val = map.Find(ImageVisualProperty.LoopCount);
418 if (val.Get(out ret))
420 NUILog.Debug($"gotten loop count={ret}");
421 if (ret != currentStates.loopCount && ret > 0)
423 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different loop count! gotten={ret}, loopCount={currentStates.loopCount}>");
425 currentStates.loopCount = ret;
426 return currentStates.loopCount;
430 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get LoopCount from dali currentStates.loopCount={currentStates.loopCount}>");
431 return currentStates.loopCount;
436 /// Sets or gets the stop behavior.
438 /// <since_tizen> 7 </since_tizen>
439 public StopBehaviorType StopBehavior
443 return (StopBehaviorType)GetValue(StopBehaviorProperty);
447 SetValue(StopBehaviorProperty, value);
448 NotifyPropertyChanged();
452 private StopBehaviorType InternalStopBehavior
456 currentStates.stopEndAction = (StopBehaviorType)value;
457 currentStates.changed = true;
459 NUILog.Debug($"<[{GetId()}]SET val={currentStates.stopEndAction}>");
460 using PropertyMap map = new PropertyMap();
461 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
462 map.Add(ImageVisualProperty.StopBehavior, stopAction);
463 using PropertyValue attribute = new PropertyValue(map);
464 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
468 NUILog.Debug($"StopBehavior get!");
469 using PropertyMap map = base.Image;
473 using PropertyValue val = map.Find(ImageVisualProperty.StopBehavior);
476 if (val.Get(out ret))
478 NUILog.Debug($"gotten StopBehavior={ret}");
479 if (ret != (int)currentStates.stopEndAction)
481 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different StopBehavior! gotten={ret}, StopBehavior={currentStates.stopEndAction}>");
483 currentStates.stopEndAction = (StopBehaviorType)ret;
484 return (StopBehaviorType)ret;
488 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get StopBehavior from dali>");
489 return currentStates.stopEndAction;
494 /// Whether to redraw the image when the visual is scaled down.
498 /// It is used in the AnimatedVectorImageVisual.The default is true.
500 [EditorBrowsable(EditorBrowsableState.Never)]
501 public bool RedrawInScalingDown
505 return (bool)GetValue(RedrawInScalingDownProperty);
509 SetValue(RedrawInScalingDownProperty, value);
510 NotifyPropertyChanged();
514 private bool InternalRedrawInScalingDown
518 currentStates.changed = true;
519 currentStates.redrawInScalingDown = value;
520 NUILog.Debug($"<[{GetId()}]SET currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
521 using PropertyMap map = new PropertyMap();
522 using PropertyValue redraw = new PropertyValue(currentStates.redrawInScalingDown);
523 map.Add(ImageVisualProperty.RedrawInScalingDown, redraw);
524 using PropertyValue action = new PropertyValue(map);
525 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, action);
529 PropertyMap map = base.Image;
533 using PropertyValue val = map.Find(ImageVisualProperty.RedrawInScalingDown);
536 if (val.Get(out ret))
538 if (ret != currentStates.redrawInScalingDown)
540 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different redrawInScalingDown! gotten={ret}, redrawInScalingDown={currentStates.redrawInScalingDown}>");
542 currentStates.redrawInScalingDown = ret;
543 return currentStates.redrawInScalingDown;
547 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get redrawInScalingDown from dali currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
548 return currentStates.redrawInScalingDown;
554 /// Actions property value to Jump to the specified frame.
555 /// This property can be redefined by child class if it use different value.
557 [EditorBrowsable(EditorBrowsableState.Never)]
558 protected int ActionJumpTo { get; set; } = Interop.LottieAnimationView.AnimatedVectorImageVisualActionJumpToGet();
560 // This is used for internal purpose. hidden API.
561 [EditorBrowsable(EditorBrowsableState.Never)]
562 protected int SetDynamicProperty => ActionJumpTo + 1;
568 /// Set the minimum and the maximum frame.
570 /// <param name="minFrame">minimum frame</param>
571 /// <param name="maxFrame">maximum frame</param>
572 /// <since_tizen> 7 </since_tizen>
573 public void SetMinMaxFrame(int minFrame, int maxFrame)
575 NUILog.Debug($"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
577 currentStates.changed = true;
578 currentStates.framePlayRangeMin = minFrame;
579 currentStates.framePlayRangeMax = maxFrame;
581 using PropertyArray array = new PropertyArray();
582 using PropertyValue min = new PropertyValue(currentStates.framePlayRangeMin);
583 using PropertyValue max = new PropertyValue(currentStates.framePlayRangeMax);
587 using PropertyMap map = new PropertyMap();
588 using PropertyValue range = new PropertyValue(array);
589 map.Add(ImageVisualProperty.PlayRange, range);
590 using PropertyValue action = new PropertyValue(map);
591 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, action);
592 NUILog.Debug($" [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
598 /// <since_tizen> 7 </since_tizen>
599 public new void Play()
601 NUILog.Debug($"<[{GetId()}] Play()");
604 NUILog.Debug($"[{GetId()}]>");
610 /// <since_tizen> 7 </since_tizen>
611 public new void Pause()
613 NUILog.Debug($"<[{GetId()}] Pause()>");
616 NUILog.Debug($"[{GetId()}]>");
622 /// <since_tizen> 7 </since_tizen>
623 public new void Stop()
625 NUILog.Debug($"<[{GetId()}] Stop()");
628 NUILog.Debug($"[{GetId()}]>");
632 /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
634 /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
635 /// <since_tizen> 7 </since_tizen>
636 public List<Tuple<string, int, int>> GetContentInfo()
640 if (currentStates.contentInfo != null)
642 return currentStates.contentInfo;
645 PropertyMap imageMap = base.Image;
646 if (imageMap != null)
648 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
649 PropertyMap contentMap = new PropertyMap();
650 if (val?.Get(ref contentMap) == true)
652 currentStates.contentInfo = new List<Tuple<string, int, int>>();
653 for (uint i = 0; i < contentMap.Count(); i++)
655 using PropertyKey propertyKey = contentMap.GetKeyAt(i);
656 string key = propertyKey.StringKey;
658 using PropertyValue arrVal = contentMap.GetValue(i);
659 using PropertyArray arr = new PropertyArray();
663 using PropertyValue start = arr.GetElementAt(0);
664 start?.Get(out startFrame);
667 using PropertyValue end = arr.GetElementAt(1);
668 end?.Get(out endFrame);
670 NUILog.Debug($"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
672 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
674 currentStates.contentInfo?.Add(item);
678 contentMap.Dispose();
683 return currentStates.contentInfo;
687 /// A marker has its start frame and end frame.
688 /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
689 /// 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. *
691 /// <param name="marker1">First marker</param>
692 /// <param name="marker2">Second marker</param>
693 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
694 [EditorBrowsable(EditorBrowsableState.Never)]
695 public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
697 NUILog.Debug($"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
699 currentStates.changed = true;
700 currentStates.mark1 = marker1;
701 currentStates.mark2 = marker2;
703 using PropertyArray array = new PropertyArray();
704 using PropertyValue mark1 = new PropertyValue(currentStates.mark1);
705 array.PushBack(mark1);
706 using PropertyValue mark2 = new PropertyValue(currentStates.mark2);
709 array.PushBack(mark2);
712 using PropertyMap map = new PropertyMap();
713 using PropertyValue range = new PropertyValue(array);
714 map.Add(ImageVisualProperty.PlayRange, range);
715 using PropertyValue actionProperty = new PropertyValue(map);
716 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, actionProperty);
717 NUILog.Debug($" [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
723 /// <returns>Tuple of Min and Max frames</returns>
724 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
725 [EditorBrowsable(EditorBrowsableState.Never)]
726 public Tuple<int, int> GetMinMaxFrame()
728 NUILog.Debug($"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
730 using PropertyMap map = Image;
733 using PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
736 using PropertyArray array = new PropertyArray();
739 uint cnt = array.Count();
740 int item1 = -1, item2 = -1;
741 for (uint i = 0; i < cnt; i++)
743 using PropertyValue v = array.GetElementAt(i);
745 if (v.Get(out intRet))
747 NUILog.Debug($"Got play range of string [{i}]: {intRet}");
759 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
762 NUILog.Debug($" [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
763 return new Tuple<int, int>(item1, item2);
767 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
768 return new Tuple<int, int>(-1, -1);
771 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
772 [EditorBrowsable(EditorBrowsableState.Never)]
773 public void DoActionExtension(LottieAnimationViewDynamicProperty info)
775 dynamicPropertyCallbackId++;
777 weakReferencesOfLottie?.Add(dynamicPropertyCallbackId, new WeakReference<LottieAnimationView>(this));
778 InternalSavedDynamicPropertyCallbacks?.Add(dynamicPropertyCallbackId, info.Callback);
780 Interop.View.DoActionExtension(SwigCPtr, ImageView.Property.IMAGE, SetDynamicProperty, dynamicPropertyCallbackId, info.KeyPath, (int)info.Property, Marshal.GetFunctionPointerForDelegate<System.Delegate>(rootCallback));
782 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
785 private void CleanCallbackDictionaries()
787 if (weakReferencesOfLottie?.Count > 0 && InternalSavedDynamicPropertyCallbacks != null)
789 foreach (var key in InternalSavedDynamicPropertyCallbacks?.Keys)
791 if (weakReferencesOfLottie.ContainsKey(key))
793 weakReferencesOfLottie.Remove(key);
797 InternalSavedDynamicPropertyCallbacks?.Clear();
798 InternalSavedDynamicPropertyCallbacks = null;
803 #region Event, Enum, Struct, ETC
805 /// Animation finished event.
807 /// <since_tizen> 7 </since_tizen>
808 public event EventHandler Finished
812 if (finishedEventHandler == null)
814 NUILog.Debug($"<[{GetId()}] Finished eventhandler added>");
815 visualEventSignalCallback = onVisualEventSignal;
816 using VisualEventSignal visualEvent = VisualEventSignal();
817 visualEvent.Connect(visualEventSignalCallback);
819 finishedEventHandler += value;
823 NUILog.Debug($"<[{GetId()}] Finished eventhandler removed>");
824 finishedEventHandler -= value;
825 if (finishedEventHandler == null && visualEventSignalCallback != null)
827 using VisualEventSignal visualEvent = VisualEventSignal();
828 visualEvent.Disconnect(visualEventSignalCallback);
829 if (visualEvent?.Empty() == true)
831 visualEventSignalCallback = null;
838 /// Enumeration for what state the vector animation is in
840 /// <since_tizen> 7 </since_tizen>
841 public enum PlayStateType
846 /// <since_tizen> 7 </since_tizen>
849 /// Vector Animation has stopped
851 /// <since_tizen> 7 </since_tizen>
854 /// The vector animation is playing
856 /// <since_tizen> 7 </since_tizen>
859 /// The vector animation is paused
861 /// <since_tizen> 7 </since_tizen>
866 /// Enumeration for what to do when the animation is stopped.
868 /// <since_tizen> 7 </since_tizen>
869 public enum StopBehaviorType
872 /// When the animation is stopped, the current frame is shown.
874 /// <since_tizen> 7 </since_tizen>
877 /// When the animation is stopped, the min frame (first frame) is shown.
879 /// <since_tizen> 7 </since_tizen>
882 /// When the animation is stopped, the max frame (last frame) is shown.
884 /// <since_tizen> 7 </since_tizen>
889 /// Enumeration for what looping mode is in.
891 /// <since_tizen> 7 </since_tizen>
892 public enum LoopingModeType
895 /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
897 /// <since_tizen> 7 </since_tizen>
900 /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
902 /// <since_tizen> 7 </since_tizen>
909 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
910 [EditorBrowsable(EditorBrowsableState.Never)]
911 public enum VectorProperty
914 /// Fill color of the object, Type of <see cref="Vector3"/>
916 [EditorBrowsable(EditorBrowsableState.Never)]
920 /// Fill opacity of the object, Type of float
922 [EditorBrowsable(EditorBrowsableState.Never)]
926 /// Stroke color of the object, Type of <see cref="Vector3"/>
928 [EditorBrowsable(EditorBrowsableState.Never)]
932 /// Stroke opacity of the object, Type of float
934 [EditorBrowsable(EditorBrowsableState.Never)]
938 /// Stroke width of the object, Type of float
940 [EditorBrowsable(EditorBrowsableState.Never)]
944 /// Transform anchor of the Layer and Group object, Type of <see cref="Vector2"/>
946 [EditorBrowsable(EditorBrowsableState.Never)]
950 /// Transform position of the Layer and Group object, Type of <see cref="Vector2"/>
952 [EditorBrowsable(EditorBrowsableState.Never)]
956 /// Transform scale of the Layer and Group object, Type of <see cref="Vector2"/>, Value range of [0..100]
958 [EditorBrowsable(EditorBrowsableState.Never)]
962 /// Transform rotation of the Layer and Group object, Type of float, Value range of [0..360] in degrees
964 [EditorBrowsable(EditorBrowsableState.Never)]
968 /// Transform opacity of the Layer and Group object, Type of float
970 [EditorBrowsable(EditorBrowsableState.Never)]
974 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
975 [EditorBrowsable(EditorBrowsableState.Never)]
976 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
977 public delegate PropertyValue DynamicPropertyCallbackType(int returnType, uint frameNumber);
978 #endregion Event, Enum, Struct, ETC
982 internal class VisualEventSignalArgs : EventArgs
984 public int VisualIndex
996 internal event EventHandler<VisualEventSignalArgs> VisualEvent
1000 if (visualEventSignalHandler == null)
1002 visualEventSignalCallback = onVisualEventSignal;
1003 using VisualEventSignal visualEvent = VisualEventSignal();
1004 visualEvent?.Connect(visualEventSignalCallback);
1006 visualEventSignalHandler += value;
1010 visualEventSignalHandler -= value;
1011 if (visualEventSignalHandler == null && visualEventSignalCallback != null)
1013 using VisualEventSignal visualEvent = VisualEventSignal();
1014 visualEvent?.Disconnect(visualEventSignalCallback);
1015 if (visualEvent?.Empty() == true)
1017 visualEventSignalCallback = null;
1023 internal void EmitVisualEventSignal(int visualIndex, int signalId)
1025 using VisualEventSignal visualEvent = VisualEventSignal();
1026 visualEvent?.Emit(this, visualIndex, signalId);
1029 internal VisualEventSignal VisualEventSignal()
1031 VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
1032 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
1036 internal Dictionary<int, DynamicPropertyCallbackType> InternalSavedDynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1038 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1039 internal delegate void RootCallbackType(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3);
1041 internal RootCallbackType rootCallback = RootCallback;
1043 static internal void RootCallback(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3)
1045 WeakReference<LottieAnimationView> current = null;
1046 LottieAnimationView currentView = null;
1047 DynamicPropertyCallbackType currentCallback = null;
1048 PropertyValue ret = null;
1050 if (weakReferencesOfLottie.TryGetValue(id, out current))
1052 if (current.TryGetTarget(out currentView))
1054 if (currentView.InternalSavedDynamicPropertyCallbacks.TryGetValue(id, out currentCallback) && currentView != null
1055 && currentView.InternalSavedDynamicPropertyCallbacks != null)
1057 ret = currentCallback?.Invoke(returnType, frameNumber);
1061 Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1067 Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1073 Tizen.Log.Error("NUI", "can't find LottieAnimationView by id, just return here!");
1079 case (int)(VectorProperty.FillColor):
1080 case (int)(VectorProperty.StrokeColor):
1081 Vector3 tmpVector3 = new Vector3(-1, -1, -1);
1082 if (ret.Get(tmpVector3))
1084 val1 = tmpVector3.X;
1085 val2 = tmpVector3.Y;
1086 val3 = tmpVector3.Z;
1088 tmpVector3.Dispose();
1091 case (int)(VectorProperty.TransformAnchor):
1092 case (int)(VectorProperty.TransformPosition):
1093 case (int)(VectorProperty.TransformScale):
1094 Vector2 tmpVector2 = new Vector2(-1, -1);
1095 if (ret.Get(tmpVector2))
1097 val1 = tmpVector2.X;
1098 val2 = tmpVector2.Y;
1100 tmpVector2.Dispose();
1103 case (int)(VectorProperty.FillOpacity):
1104 case (int)(VectorProperty.StrokeOpacity):
1105 case (int)(VectorProperty.StrokeWidth):
1106 case (int)(VectorProperty.TransformRotation):
1107 case (int)(VectorProperty.TransformOpacity):
1108 float tmpFloat = -1;
1109 if (ret.Get(out tmpFloat))
1124 private struct states
1126 internal string url;
1128 internal int loopCount;
1129 internal LoopingModeType loopMode;
1130 internal StopBehaviorType stopEndAction;
1131 internal int framePlayRangeMin;
1132 internal int framePlayRangeMax;
1133 internal bool changed;
1134 internal int totalFrame;
1135 internal float scale;
1136 internal PlayStateType playState;
1137 internal List<Tuple<string, int, int>> contentInfo;
1138 internal string mark1, mark2;
1139 internal bool redrawInScalingDown;
1141 private states currentStates;
1143 private struct DevelVisual
1147 AnimatedGradient = Visual.Type.AnimatedImage + 1,
1148 AnimatedVectorImage = Visual.Type.AnimatedImage + 2,
1152 private const string tag = "NUITEST";
1153 private event EventHandler finishedEventHandler;
1155 private void OnFinished()
1157 NUILog.Debug($"<[{GetId()}] OnFinished()>");
1158 finishedEventHandler?.Invoke(this, null);
1161 private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
1165 if (targetView != IntPtr.Zero)
1167 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
1170 NUILog.Debug($"targetView is not null! name={v.Name}");
1174 NUILog.Debug($"target is something created from dali");
1177 VisualEventSignalArgs e = new VisualEventSignalArgs();
1178 e.VisualIndex = visualIndex;
1179 e.SignalId = signalId;
1180 visualEventSignalHandler?.Invoke(this, e);
1182 NUILog.Debug($"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
1185 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
1186 private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
1188 private VisualEventSignalCallbackType visualEventSignalCallback;
1189 private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
1191 static private int dynamicPropertyCallbackId = 0;
1192 //static private Dictionary<int, DynamicPropertyCallbackType> dynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1193 static private Dictionary<int, WeakReference<LottieAnimationView>> weakReferencesOfLottie = new Dictionary<int, WeakReference<LottieAnimationView>>();
1195 private void debugPrint()
1197 NUILog.Debug($"===================================");
1198 NUILog.Debug($"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, \nframePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
1199 NUILog.Debug($" get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState}");
1200 NUILog.Debug($" RedrawInScalingDown={RedrawInScalingDown} >");
1201 NUILog.Debug($"===================================");
1208 /// A class containing frame informations for a LottieAnimationView.
1210 [EditorBrowsable(EditorBrowsableState.Never)]
1211 public class LottieFrameInfo : ICloneable
1214 /// Creates a new instance with a playing range.
1216 [EditorBrowsable(EditorBrowsableState.Never)]
1217 public LottieFrameInfo(int startFrame, int endFrame)
1219 StartFrame = startFrame;
1220 EndFrame = endFrame;
1224 /// Creates a new instance with a still image frame.
1226 [EditorBrowsable(EditorBrowsableState.Never)]
1227 public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
1232 /// Create a new instance from a pair notation.
1234 [EditorBrowsable(EditorBrowsableState.Never)]
1235 public static implicit operator LottieFrameInfo((int, int) pair)
1237 return new LottieFrameInfo(pair.Item1, pair.Item2);
1241 /// Create a new instance from an int value.
1243 [EditorBrowsable(EditorBrowsableState.Never)]
1244 public static implicit operator LottieFrameInfo(int stillImageFrame)
1246 return new LottieFrameInfo(stillImageFrame);
1250 /// Create a new instance from string.
1251 /// Possible input : "0, 10", "10"
1253 [EditorBrowsable(EditorBrowsableState.Never)]
1254 public static implicit operator LottieFrameInfo(string pair)
1261 string[] parts = pair.Split(',');
1262 if (parts.Length == 1)
1264 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture));
1266 else if (parts.Length == 2)
1268 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), Int32.Parse(parts[1].Trim(), CultureInfo.InvariantCulture));
1271 Tizen.Log.Error("NUI", $"Can not convert string {pair} to LottieFrameInfo");
1276 /// The start frame of the lottie animation.
1278 [EditorBrowsable(EditorBrowsableState.Never)]
1279 public int StartFrame { get; }
1282 /// The end frame of the lottie animation.
1284 [EditorBrowsable(EditorBrowsableState.Never)]
1285 public int EndFrame { get; }
1288 /// Create LottieFrameInfo struct with animation range information
1290 [EditorBrowsable(EditorBrowsableState.Never)]
1291 public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
1293 return new LottieFrameInfo(startFrame, endFrame);
1297 /// Create LottieFrameInfo struct with still image information
1299 [EditorBrowsable(EditorBrowsableState.Never)]
1300 public static LottieFrameInfo CreateStillImage(int stillImageFrame)
1302 return new LottieFrameInfo(stillImageFrame, stillImageFrame);
1307 /// Whether this LottieFrameInfo represents one frame or more.
1309 [EditorBrowsable(EditorBrowsableState.Never)]
1310 public bool IsStillImage()
1312 return StartFrame == EndFrame;
1317 /// Play specified LottieAnimationView with this frame information.
1319 /// <param name="lottieView">The target LottieAnimationView to play.</param>
1320 /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
1321 [EditorBrowsable(EditorBrowsableState.Never)]
1322 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062: Validate arguments of public methods", Justification = "The null checking is done by BeReadyToShow()")]
1323 public void Show(LottieAnimationView lottieView, bool noPlay = false)
1325 if (!BeReadyToShow(lottieView))
1330 lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
1332 if (IsStillImage() || noPlay)
1334 lottieView.CurrentFrame = EndFrame;
1338 lottieView.CurrentFrame = StartFrame;
1344 [EditorBrowsable(EditorBrowsableState.Never)]
1345 public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
1347 private bool BeReadyToShow(LottieAnimationView lottieView)
1349 // Validate input lottieView
1350 if (null == lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
1355 // Stop if it was playing
1356 if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)
1365 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
1366 [EditorBrowsable(EditorBrowsableState.Never)]
1367 public struct LottieAnimationViewDynamicProperty : IEquatable<LottieAnimationViewDynamicProperty>
1369 [EditorBrowsable(EditorBrowsableState.Never)]
1370 public string KeyPath { get; set; }
1372 [EditorBrowsable(EditorBrowsableState.Never)]
1373 public LottieAnimationView.VectorProperty Property { get; set; }
1375 [EditorBrowsable(EditorBrowsableState.Never)]
1376 public LottieAnimationView.DynamicPropertyCallbackType Callback { get; set; }
1378 public override bool Equals(object obj)
1380 if (obj is LottieAnimationViewDynamicProperty target)
1382 if (KeyPath == target.KeyPath && Property == target.Property && Callback == target.Callback)
1390 public override int GetHashCode()
1392 return base.GetHashCode();
1395 public static bool operator ==(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1397 return left.Equals(right);
1400 public static bool operator !=(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1402 return !(left == right);
1405 public bool Equals(LottieAnimationViewDynamicProperty other)
1409 if (KeyPath == other.KeyPath && Property == other.Property && Callback == other.Callback)