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
33 #region Constructor, Destructor, Dispose
35 /// LottieAnimationView constructor
37 /// <param name="scale">The factor of scaling image, default : 1.0f</param>
38 /// <param name="shown">false : not displayed (hidden), true : displayed (shown), default : true</param>
40 /// If the shown parameter is false, the animation is not visible even if the LottieAnimationView instance is created.
44 /// LottieAnimationView myLottie = new LottieAnimationView();
45 /// LottieAnimationView myLottie2 = new LottieAnimationView(2.0f);
46 /// LottieAnimationView myLottie3 = new LottieAnimationView(1.0f, false);
49 /// <since_tizen> 7 </since_tizen>
50 public LottieAnimationView(float scale = 1.0f, bool shown = true) : base()
52 NUILog.Debug($"< constructor GetId={GetId()} >");
53 currentStates.url = "";
54 currentStates.loopCount = 1;
55 currentStates.loopMode = LoopingModeType.Restart;
56 currentStates.stopEndAction = StopBehaviorType.CurrentFrame;
57 currentStates.framePlayRangeMin = -1;
58 currentStates.framePlayRangeMax = -1;
59 currentStates.totalFrame = -1;
60 currentStates.scale = scale;
61 currentStates.redrawInScalingDown = true;
62 currentStates.desiredWidth = 0;
63 currentStates.desiredHeight = 0;
65 // Set changed flag as true when initalized state.
66 // After some properties change, LottieAnimationView.UpdateImage will apply these inital values.
67 currentStates.changed = true;
72 /// Dispose(DisposeTypes type)
74 /// <param name="type"></param>
75 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
76 [EditorBrowsable(EditorBrowsableState.Never)]
77 protected override void Dispose(DisposeTypes type)
84 CleanCallbackDictionaries();
86 //Release your own unmanaged resources here.
87 //You should not access any managed member here except static instance.
88 //because the execution order of Finalizes is non-deterministic.
90 //disconnect event signal
91 if (finishedEventHandler != null && visualEventSignalCallback != null)
93 using VisualEventSignal visualEvent = VisualEventSignal();
94 visualEvent.Disconnect(visualEventSignalCallback);
95 finishedEventHandler = null;
96 NUILog.Debug($"disconnect event signal");
102 // This is used for internal purpose. hidden API.
103 [EditorBrowsable(EditorBrowsableState.Never)]
104 protected override void Dispose(bool disposing)
106 CleanCallbackDictionaries();
107 base.Dispose(disposing);
109 #endregion Constructor, Destructor, Dispose
114 /// Set or Get resource URL of Lottie file.
116 /// <since_tizen> 7 </since_tizen>
121 return GetValue(URLProperty) as string;
125 SetValue(URLProperty, value);
126 NotifyPropertyChanged();
131 /// Set or Get resource URL of Lottie file.
133 [EditorBrowsable(EditorBrowsableState.Never)]
134 public new string ResourceUrl
146 private string InternalURL
150 // Reset cached infomations.
151 currentStates.contentInfo = null;
152 currentStates.mark1 = null;
153 currentStates.mark2 = null;
154 currentStates.framePlayRangeMin = -1;
155 currentStates.framePlayRangeMax = -1;
156 currentStates.totalFrame = -1;
158 string ret = (value == null ? "" : value);
159 currentStates.url = ret;
161 NUILog.Debug($"<[{GetId()}]SET url={currentStates.url}");
163 // TODO : Could create new Image without additional creation?
164 using PropertyMap map = new PropertyMap();
165 using PropertyValue type = new PropertyValue((int)Visual.Type.AnimatedVectorImage);
166 using PropertyValue url = new PropertyValue(currentStates.url);
167 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
168 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
169 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
170 using PropertyValue redrawInScalingDown = new PropertyValue(currentStates.redrawInScalingDown);
172 map.Add(Visual.Property.Type, type)
173 .Add(ImageVisualProperty.URL, url)
174 .Add(ImageVisualProperty.LoopCount, loopCnt)
175 .Add(ImageVisualProperty.StopBehavior, stopAction)
176 .Add(ImageVisualProperty.LoopingMode, loopMode)
177 .Add(ImageVisualProperty.RedrawInScalingDown, redrawInScalingDown);
179 if (currentStates.desiredWidth > 0)
181 using PropertyValue desiredWidth = new PropertyValue((int)currentStates.desiredWidth);
182 map.Add(ImageVisualProperty.DesiredWidth, desiredWidth);
184 if (currentStates.desiredHeight > 0)
186 using PropertyValue desiredHeight = new PropertyValue((int)currentStates.desiredHeight);
187 map.Add(ImageVisualProperty.DesiredHeight, desiredHeight);
192 // All states applied well.
193 currentStates.changed = false;
195 if (currentStates.scale != 1.0f)
197 Scale = new Vector3(currentStates.scale, currentStates.scale, currentStates.scale);
199 NUILog.Debug($"<[{GetId()}]>");
203 string ret = currentStates.url;
204 NUILog.Debug($"<[{GetId()}] GET");
205 NUILog.Debug($"gotten url={ret} >");
211 /// Gets or sets the desired image width for LottieAnimationView<br />
213 [EditorBrowsable(EditorBrowsableState.Never)]
214 public new int DesiredWidth
218 return currentStates.desiredWidth;
222 currentStates.desiredWidth = value;
223 base.DesiredWidth = currentStates.desiredWidth;
228 /// Gets or sets the desired image height for LottieAnimationView<br />
230 [EditorBrowsable(EditorBrowsableState.Never)]
231 public new int DesiredHeight
235 return currentStates.desiredHeight;
239 currentStates.desiredHeight = value;
240 base.DesiredHeight = currentStates.desiredHeight;
245 /// Gets the playing state
247 /// <since_tizen> 7 </since_tizen>
248 public PlayStateType PlayState
252 NUILog.Debug($"< Get!");
255 Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayState, out ret);
257 currentStates.playState = (PlayStateType)ret;
258 NUILog.Debug($"gotten play state={currentStates.playState} >");
260 return currentStates.playState;
265 /// Get the number of total frames
267 /// <since_tizen> 7 </since_tizen>
268 public int TotalFrame
272 int ret = currentStates.totalFrame;
275 Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.TotalFrameNumber, out ret);
277 currentStates.totalFrame = ret;
278 NUILog.Debug($"TotalFrameNumber get! ret={ret}");
285 /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
288 /// 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.
291 /// We assume that the animation in myLottie.json file has 100 frames originally. If so, its frame index will be 0 - 99.
293 /// LottieAnimationView myLottie = new LottieAnimationView();
294 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
295 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
296 /// myLottie.CurrentFrame = 200; //display 99 frame
297 /// myLottie.SetMinMaxFrame(10, 20);
298 /// myLottie.CurrentFrame = 15; //display 15 frame
299 /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
302 /// <since_tizen> 7 </since_tizen>
303 public int CurrentFrame
307 return (int)GetValue(CurrentFrameProperty);
311 SetValue(CurrentFrameProperty, value);
312 NotifyPropertyChanged();
316 private int InternalCurrentFrame
320 NUILog.Debug($"<[{GetId()}]SET frame={value}>");
322 Interop.View.DoActionWithSingleIntAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionJumpTo, value);
328 Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.CurrentFrameNumber, out ret);
330 NUILog.Debug($"CurrentFrameNumber get! val={ret}");
336 /// Sets or gets the looping mode of Lottie animation.
338 /// <since_tizen> 7 </since_tizen>
339 public LoopingModeType LoopingMode
343 return (LoopingModeType)GetValue(LoopingModeProperty);
347 SetValue(LoopingModeProperty, value);
348 NotifyPropertyChanged();
352 private LoopingModeType InternalLoopingMode
356 if (currentStates.loopMode != (LoopingModeType)value)
358 currentStates.changed = true;
359 currentStates.loopMode = (LoopingModeType)value;
361 NUILog.Debug($"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
363 Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.LoopingMode, (int)currentStates.loopMode);
368 NUILog.Debug($"LoopMode get! {currentStates.loopMode}");
369 return currentStates.loopMode;
374 /// Sets or gets the loop count.
377 /// The minus value means the infinite loop count.
381 /// LottieAnimationView myLottie = new LottieAnimationView();
382 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
383 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
384 /// myLottie.LoopCount = -1; //infinite loop
386 /// myLottie.Stop(); //it plays continuously unless Stop() is called
387 /// myLottie.LoopCount = 2;
388 /// myLottie.Play(); //it plays only 2 times and stops automatically
391 /// <since_tizen> 7 </since_tizen>
396 return (int)GetValue(LoopCountProperty);
400 SetValue(LoopCountProperty, value);
401 NotifyPropertyChanged();
405 private int InternalLoopCount
409 if (currentStates.loopCount != value)
411 currentStates.changed = true;
412 currentStates.loopCount = value;
414 NUILog.Debug($"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
416 Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.LoopCount, currentStates.loopCount);
421 NUILog.Debug($"LoopCount get! {currentStates.loopCount}");
422 return currentStates.loopCount;
427 /// Sets or gets the stop behavior.
429 /// <since_tizen> 7 </since_tizen>
430 public StopBehaviorType StopBehavior
434 return (StopBehaviorType)GetValue(StopBehaviorProperty);
438 SetValue(StopBehaviorProperty, value);
439 NotifyPropertyChanged();
443 private StopBehaviorType InternalStopBehavior
447 if (currentStates.stopEndAction != (StopBehaviorType)value)
449 currentStates.changed = true;
450 currentStates.stopEndAction = (StopBehaviorType)value;
452 NUILog.Debug($"<[{GetId()}]SET val={currentStates.stopEndAction}>");
454 Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.StopBehavior, (int)currentStates.stopEndAction);
459 NUILog.Debug($"StopBehavior get! {currentStates.stopEndAction}");
460 return currentStates.stopEndAction;
465 /// Whether to redraw the image when the visual is scaled down.
469 /// It is used in the AnimatedVectorImageVisual.The default is true.
471 [EditorBrowsable(EditorBrowsableState.Never)]
472 public bool RedrawInScalingDown
476 return (bool)GetValue(RedrawInScalingDownProperty);
480 SetValue(RedrawInScalingDownProperty, value);
481 NotifyPropertyChanged();
485 private bool InternalRedrawInScalingDown
489 if (currentStates.redrawInScalingDown != value)
491 currentStates.changed = true;
492 currentStates.redrawInScalingDown = value;
494 NUILog.Debug($"<[{GetId()}]SET currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
496 Interop.View.InternalUpdateVisualPropertyBool(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.RedrawInScalingDown, currentStates.redrawInScalingDown);
501 NUILog.Debug($"RedrawInScalingDown get! {currentStates.redrawInScalingDown}");
502 return currentStates.redrawInScalingDown;
510 /// Set the minimum and the maximum frame.
512 /// <param name="minFrame">minimum frame</param>
513 /// <param name="maxFrame">maximum frame</param>
514 /// <since_tizen> 7 </since_tizen>
515 public void SetMinMaxFrame(int minFrame, int maxFrame)
517 if (currentStates.framePlayRangeMin != minFrame || currentStates.framePlayRangeMax != maxFrame)
519 NUILog.Debug($"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
520 currentStates.changed = true;
521 currentStates.framePlayRangeMin = minFrame;
522 currentStates.framePlayRangeMax = maxFrame;
524 Interop.View.InternalUpdateVisualPropertyIntPair(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.framePlayRangeMin, currentStates.framePlayRangeMax);
526 NUILog.Debug($" [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
533 /// <since_tizen> 7 </since_tizen>
534 public new void Play()
536 NUILog.Debug($"<[{GetId()}] Play()");
539 NUILog.Debug($"[{GetId()}]>");
545 /// <since_tizen> 7 </since_tizen>
546 public new void Pause()
548 NUILog.Debug($"<[{GetId()}] Pause()>");
551 NUILog.Debug($"[{GetId()}]>");
557 /// <since_tizen> 7 </since_tizen>
558 public new void Stop()
560 NUILog.Debug($"<[{GetId()}] Stop()");
563 NUILog.Debug($"[{GetId()}]>");
567 /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
569 /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
570 /// <since_tizen> 7 </since_tizen>
571 public List<Tuple<string, int, int>> GetContentInfo()
573 if (currentStates.contentInfo != null)
575 return currentStates.contentInfo;
580 PropertyMap imageMap = base.Image;
581 if (imageMap != null)
583 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
584 PropertyMap contentMap = new PropertyMap();
585 if (val?.Get(ref contentMap) == true)
587 currentStates.contentInfo = new List<Tuple<string, int, int>>();
588 for (uint i = 0; i < contentMap.Count(); i++)
590 using PropertyKey propertyKey = contentMap.GetKeyAt(i);
591 string key = propertyKey.StringKey;
593 using PropertyValue arrVal = contentMap.GetValue(i);
594 using PropertyArray arr = new PropertyArray();
598 using PropertyValue start = arr.GetElementAt(0);
599 start?.Get(out startFrame);
602 using PropertyValue end = arr.GetElementAt(1);
603 end?.Get(out endFrame);
605 NUILog.Debug($"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
607 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
609 currentStates.contentInfo?.Add(item);
613 contentMap.Dispose();
618 return currentStates.contentInfo;
622 /// A marker has its start frame and end frame.
623 /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
624 /// 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. *
626 /// <param name="marker1">First marker</param>
627 /// <param name="marker2">Second marker</param>
628 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
629 [EditorBrowsable(EditorBrowsableState.Never)]
630 public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
632 if (currentStates.mark1 != marker1 || currentStates.mark2 != marker2)
634 NUILog.Debug($"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
636 currentStates.changed = true;
637 currentStates.mark1 = marker1;
638 currentStates.mark2 = marker2;
640 if (string.IsNullOrEmpty(currentStates.mark2))
642 Interop.View.InternalUpdateVisualPropertyString(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.mark1);
646 Interop.View.InternalUpdateVisualPropertyStringPair(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.mark1, currentStates.mark2);
649 NUILog.Debug($" [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
656 /// <returns>Tuple of Min and Max frames</returns>
657 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
658 [EditorBrowsable(EditorBrowsableState.Never)]
659 public Tuple<int, int> GetMinMaxFrame()
661 NUILog.Debug($"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
663 using PropertyMap map = Image;
666 using PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
669 using PropertyArray array = new PropertyArray();
672 uint cnt = array.Count();
673 int item1 = -1, item2 = -1;
674 for (uint i = 0; i < cnt; i++)
676 using PropertyValue v = array.GetElementAt(i);
678 if (v.Get(out intRet))
680 NUILog.Debug($"Got play range of string [{i}]: {intRet}");
692 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
695 NUILog.Debug($" [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
696 return new Tuple<int, int>(item1, item2);
700 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
701 return new Tuple<int, int>(-1, -1);
704 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
705 [EditorBrowsable(EditorBrowsableState.Never)]
706 public void DoActionExtension(LottieAnimationViewDynamicProperty info)
708 dynamicPropertyCallbackId++;
710 weakReferencesOfLottie?.Add(dynamicPropertyCallbackId, new WeakReference<LottieAnimationView>(this));
711 InternalSavedDynamicPropertyCallbacks?.Add(dynamicPropertyCallbackId, info.Callback);
713 Interop.View.DoActionExtension(SwigCPtr, ImageView.Property.IMAGE, ActionSetDynamicProperty, dynamicPropertyCallbackId, info.KeyPath, (int)info.Property, Marshal.GetFunctionPointerForDelegate<System.Delegate>(rootCallback));
715 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
718 private void CleanCallbackDictionaries()
720 if (weakReferencesOfLottie?.Count > 0 && InternalSavedDynamicPropertyCallbacks != null)
722 foreach (var key in InternalSavedDynamicPropertyCallbacks?.Keys)
724 if (weakReferencesOfLottie.ContainsKey(key))
726 weakReferencesOfLottie.Remove(key);
730 InternalSavedDynamicPropertyCallbacks?.Clear();
731 InternalSavedDynamicPropertyCallbacks = null;
735 /// Update lottie-image-relative properties synchronously.
736 /// After call this API, All image properties updated.
738 [EditorBrowsable(EditorBrowsableState.Never)]
739 protected override void UpdateImage()
741 if (!imagePropertyUpdatedFlag) return;
743 // Update currentStates properties to cachedImagePropertyMap
744 if(currentStates.changed)
746 UpdateImage(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount), false);
747 UpdateImage(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction), false);
748 UpdateImage(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode), false);
749 UpdateImage(ImageVisualProperty.RedrawInScalingDown, new PropertyValue(currentStates.redrawInScalingDown), false);
751 // Do not cache PlayRange and TotalFrameNumber into cachedImagePropertyMap.
752 // (To keep legacy implements behaviour)
753 currentStates.changed = false;
760 /// Update NUI cached animated image visual property map by inputed property map.
761 /// And call base.MergeCachedImageVisualProperty()
764 /// For performance issue, we will collect only "cachedLottieAnimationPropertyKeyList" hold in this class.
766 [EditorBrowsable(EditorBrowsableState.Never)]
767 protected override void MergeCachedImageVisualProperty(PropertyMap map)
769 if (map == null) return;
770 if (cachedImagePropertyMap == null)
772 cachedImagePropertyMap = new PropertyMap();
774 foreach (var key in cachedLottieAnimationPropertyKeyList)
776 PropertyValue value = map.Find(key);
779 // Update-or-Insert new value
780 cachedImagePropertyMap[key] = value;
783 base.MergeCachedImageVisualProperty(map);
788 #region Event, Enum, Struct, ETC
790 /// Animation finished event.
792 /// <since_tizen> 7 </since_tizen>
793 public event EventHandler Finished
797 if (finishedEventHandler == null)
799 NUILog.Debug($"<[{GetId()}] Finished eventhandler added>");
800 visualEventSignalCallback = onVisualEventSignal;
801 using VisualEventSignal visualEvent = VisualEventSignal();
802 visualEvent.Connect(visualEventSignalCallback);
804 finishedEventHandler += value;
808 NUILog.Debug($"<[{GetId()}] Finished eventhandler removed>");
809 finishedEventHandler -= value;
810 if (finishedEventHandler == null && visualEventSignalCallback != null)
812 using VisualEventSignal visualEvent = VisualEventSignal();
813 visualEvent.Disconnect(visualEventSignalCallback);
814 if (visualEvent?.Empty() == true)
816 visualEventSignalCallback = null;
823 /// Enumeration for what state the vector animation is in
825 /// <since_tizen> 7 </since_tizen>
826 public enum PlayStateType
831 /// <since_tizen> 7 </since_tizen>
834 /// Vector Animation has stopped
836 /// <since_tizen> 7 </since_tizen>
839 /// The vector animation is playing
841 /// <since_tizen> 7 </since_tizen>
844 /// The vector animation is paused
846 /// <since_tizen> 7 </since_tizen>
851 /// Enumeration for what to do when the animation is stopped.
853 /// <since_tizen> 7 </since_tizen>
854 public enum StopBehaviorType
857 /// When the animation is stopped, the current frame is shown.
859 /// <since_tizen> 7 </since_tizen>
862 /// When the animation is stopped, the min frame (first frame) is shown.
864 /// <since_tizen> 7 </since_tizen>
867 /// When the animation is stopped, the max frame (last frame) is shown.
869 /// <since_tizen> 7 </since_tizen>
874 /// Enumeration for what looping mode is in.
876 /// <since_tizen> 7 </since_tizen>
877 public enum LoopingModeType
880 /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
882 /// <since_tizen> 7 </since_tizen>
885 /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
887 /// <since_tizen> 7 </since_tizen>
894 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
895 [EditorBrowsable(EditorBrowsableState.Never)]
896 public enum VectorProperty
899 /// Fill color of the object, Type of <see cref="Vector3"/>
901 [EditorBrowsable(EditorBrowsableState.Never)]
905 /// Fill opacity of the object, Type of float
907 [EditorBrowsable(EditorBrowsableState.Never)]
911 /// Stroke color of the object, Type of <see cref="Vector3"/>
913 [EditorBrowsable(EditorBrowsableState.Never)]
917 /// Stroke opacity of the object, Type of float
919 [EditorBrowsable(EditorBrowsableState.Never)]
923 /// Stroke width of the object, Type of float
925 [EditorBrowsable(EditorBrowsableState.Never)]
929 /// Transform anchor of the Layer and Group object, Type of <see cref="Vector2"/>
931 [EditorBrowsable(EditorBrowsableState.Never)]
935 /// Transform position of the Layer and Group object, Type of <see cref="Vector2"/>
937 [EditorBrowsable(EditorBrowsableState.Never)]
941 /// Transform scale of the Layer and Group object, Type of <see cref="Vector2"/>, Value range of [0..100]
943 [EditorBrowsable(EditorBrowsableState.Never)]
947 /// Transform rotation of the Layer and Group object, Type of float, Value range of [0..360] in degrees
949 [EditorBrowsable(EditorBrowsableState.Never)]
953 /// Transform opacity of the Layer and Group object, Type of float
955 [EditorBrowsable(EditorBrowsableState.Never)]
959 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
960 [EditorBrowsable(EditorBrowsableState.Never)]
961 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
962 public delegate PropertyValue DynamicPropertyCallbackType(int returnType, uint frameNumber);
963 #endregion Event, Enum, Struct, ETC
968 /// Actions property value to Jump to the specified frame.
970 internal static readonly int ActionJumpTo = Interop.LottieAnimationView.AnimatedVectorImageVisualActionJumpToGet();
972 // This is used for internal purpose.
973 internal static readonly int ActionSetDynamicProperty = Interop.LottieAnimationView.AnimatedVectorImageVisualActionSetDynamicPropertyGet();
974 internal static readonly int ActionFlush = Interop.LottieAnimationView.AnimatedVectorImageVisualActionFlushGet();
976 internal class VisualEventSignalArgs : EventArgs
978 public int VisualIndex
990 internal event EventHandler<VisualEventSignalArgs> VisualEvent
994 if (visualEventSignalHandler == null)
996 visualEventSignalCallback = onVisualEventSignal;
997 using VisualEventSignal visualEvent = VisualEventSignal();
998 visualEvent?.Connect(visualEventSignalCallback);
1000 visualEventSignalHandler += value;
1004 visualEventSignalHandler -= value;
1005 if (visualEventSignalHandler == null && visualEventSignalCallback != null)
1007 using VisualEventSignal visualEvent = VisualEventSignal();
1008 visualEvent?.Disconnect(visualEventSignalCallback);
1009 if (visualEvent?.Empty() == true)
1011 visualEventSignalCallback = null;
1017 internal void EmitVisualEventSignal(int visualIndex, int signalId)
1019 using VisualEventSignal visualEvent = VisualEventSignal();
1020 visualEvent?.Emit(this, visualIndex, signalId);
1023 internal VisualEventSignal VisualEventSignal()
1025 VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
1026 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
1030 internal Dictionary<int, DynamicPropertyCallbackType> InternalSavedDynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1032 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1033 internal delegate void RootCallbackType(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3);
1035 internal RootCallbackType rootCallback = RootCallback;
1037 static internal void RootCallback(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3)
1039 WeakReference<LottieAnimationView> current = null;
1040 LottieAnimationView currentView = null;
1041 DynamicPropertyCallbackType currentCallback = null;
1042 PropertyValue ret = null;
1044 if (weakReferencesOfLottie.TryGetValue(id, out current))
1046 if (current.TryGetTarget(out currentView))
1048 if (currentView != null && currentView.InternalSavedDynamicPropertyCallbacks != null &&
1049 currentView.InternalSavedDynamicPropertyCallbacks.TryGetValue(id, out currentCallback))
1051 ret = currentCallback?.Invoke(returnType, frameNumber);
1055 Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1061 Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1067 Tizen.Log.Error("NUI", "can't find LottieAnimationView by id, just return here!");
1073 case (int)(VectorProperty.FillColor):
1074 case (int)(VectorProperty.StrokeColor):
1075 Vector3 tmpVector3 = new Vector3(-1, -1, -1);
1076 if ((ret != null) && ret.Get(tmpVector3))
1078 val1 = tmpVector3.X;
1079 val2 = tmpVector3.Y;
1080 val3 = tmpVector3.Z;
1082 tmpVector3.Dispose();
1085 case (int)(VectorProperty.TransformAnchor):
1086 case (int)(VectorProperty.TransformPosition):
1087 case (int)(VectorProperty.TransformScale):
1088 Vector2 tmpVector2 = new Vector2(-1, -1);
1089 if ((ret != null) && ret.Get(tmpVector2))
1091 val1 = tmpVector2.X;
1092 val2 = tmpVector2.Y;
1094 tmpVector2.Dispose();
1097 case (int)(VectorProperty.FillOpacity):
1098 case (int)(VectorProperty.StrokeOpacity):
1099 case (int)(VectorProperty.StrokeWidth):
1100 case (int)(VectorProperty.TransformRotation):
1101 case (int)(VectorProperty.TransformOpacity):
1102 float tmpFloat = -1;
1103 if ((ret != null) && ret.Get(out tmpFloat))
1115 internal void FlushLottieMessages()
1117 NUILog.Debug($"<[{GetId()}]FLUSH>");
1119 Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionFlush);
1126 // Collection of lottie-image-sensitive properties.
1127 private static readonly List<int> cachedLottieAnimationPropertyKeyList = new List<int> {
1128 ImageVisualProperty.LoopCount,
1129 ImageVisualProperty.StopBehavior,
1130 ImageVisualProperty.LoopingMode,
1131 ImageVisualProperty.RedrawInScalingDown,
1134 private struct states
1136 internal string url;
1137 internal int loopCount;
1138 internal LoopingModeType loopMode;
1139 internal StopBehaviorType stopEndAction;
1140 internal int framePlayRangeMin;
1141 internal int framePlayRangeMax;
1142 internal int totalFrame;
1143 internal float scale;
1144 internal PlayStateType playState;
1145 internal List<Tuple<string, int, int>> contentInfo;
1146 internal string mark1, mark2;
1147 internal bool redrawInScalingDown;
1148 internal int desiredWidth, desiredHeight;
1149 internal bool changed;
1151 private states currentStates;
1153 private const string tag = "NUITEST";
1154 private event EventHandler finishedEventHandler;
1156 private void OnFinished()
1158 NUILog.Debug($"<[{GetId()}] OnFinished()>");
1159 finishedEventHandler?.Invoke(this, null);
1162 private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
1166 if (targetView != IntPtr.Zero)
1168 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
1171 NUILog.Debug($"targetView is not null! name={v.Name}");
1175 NUILog.Debug($"target is something created from dali");
1178 VisualEventSignalArgs e = new VisualEventSignalArgs();
1179 e.VisualIndex = visualIndex;
1180 e.SignalId = signalId;
1181 visualEventSignalHandler?.Invoke(this, e);
1183 NUILog.Debug($"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
1186 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1187 private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
1189 private VisualEventSignalCallbackType visualEventSignalCallback;
1190 private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
1192 static private int dynamicPropertyCallbackId = 0;
1193 //static private Dictionary<int, DynamicPropertyCallbackType> dynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1194 static private Dictionary<int, WeakReference<LottieAnimationView>> weakReferencesOfLottie = new Dictionary<int, WeakReference<LottieAnimationView>>();
1196 private void debugPrint()
1198 NUILog.Debug($"===================================");
1199 NUILog.Debug($"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, \nframePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
1200 NUILog.Debug($" get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState}");
1201 NUILog.Debug($" RedrawInScalingDown={RedrawInScalingDown} >");
1202 NUILog.Debug($"===================================");
1209 /// A class containing frame informations for a LottieAnimationView.
1211 [EditorBrowsable(EditorBrowsableState.Never)]
1212 public class LottieFrameInfo : ICloneable
1215 /// Creates a new instance with a playing range.
1217 [EditorBrowsable(EditorBrowsableState.Never)]
1218 public LottieFrameInfo(int startFrame, int endFrame)
1220 StartFrame = startFrame;
1221 EndFrame = endFrame;
1225 /// Creates a new instance with a still image frame.
1227 [EditorBrowsable(EditorBrowsableState.Never)]
1228 public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
1233 /// Create a new instance from a pair notation.
1235 [EditorBrowsable(EditorBrowsableState.Never)]
1236 public static implicit operator LottieFrameInfo((int, int) pair)
1238 return new LottieFrameInfo(pair.Item1, pair.Item2);
1242 /// Create a new instance from an int value.
1244 [EditorBrowsable(EditorBrowsableState.Never)]
1245 public static implicit operator LottieFrameInfo(int stillImageFrame)
1247 return new LottieFrameInfo(stillImageFrame);
1251 /// Create a new instance from string.
1252 /// Possible input : "0, 10", "10"
1254 [EditorBrowsable(EditorBrowsableState.Never)]
1255 public static implicit operator LottieFrameInfo(string pair)
1262 string[] parts = pair.Split(',');
1263 if (parts.Length == 1)
1265 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture));
1267 else if (parts.Length == 2)
1269 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), Int32.Parse(parts[1].Trim(), CultureInfo.InvariantCulture));
1272 Tizen.Log.Error("NUI", $"Can not convert string {pair} to LottieFrameInfo");
1277 /// The start frame of the lottie animation.
1279 [EditorBrowsable(EditorBrowsableState.Never)]
1280 public int StartFrame { get; }
1283 /// The end frame of the lottie animation.
1285 [EditorBrowsable(EditorBrowsableState.Never)]
1286 public int EndFrame { get; }
1289 /// Create LottieFrameInfo struct with animation range information
1291 [EditorBrowsable(EditorBrowsableState.Never)]
1292 public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
1294 return new LottieFrameInfo(startFrame, endFrame);
1298 /// Create LottieFrameInfo struct with still image information
1300 [EditorBrowsable(EditorBrowsableState.Never)]
1301 public static LottieFrameInfo CreateStillImage(int stillImageFrame)
1303 return new LottieFrameInfo(stillImageFrame, stillImageFrame);
1308 /// Whether this LottieFrameInfo represents one frame or more.
1310 [EditorBrowsable(EditorBrowsableState.Never)]
1311 public bool IsStillImage()
1313 return StartFrame == EndFrame;
1318 /// Play specified LottieAnimationView with this frame information.
1320 /// <param name="lottieView">The target LottieAnimationView to play.</param>
1321 /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
1322 [EditorBrowsable(EditorBrowsableState.Never)]
1323 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062: Validate arguments of public methods", Justification = "The null checking is done by BeReadyToShow()")]
1324 public void Show(LottieAnimationView lottieView, bool noPlay = false)
1326 if (!BeReadyToShow(lottieView))
1331 lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
1333 if (IsStillImage() || noPlay)
1335 lottieView.CurrentFrame = EndFrame;
1339 lottieView.CurrentFrame = StartFrame;
1345 [EditorBrowsable(EditorBrowsableState.Never)]
1346 public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
1348 private bool BeReadyToShow(LottieAnimationView lottieView)
1350 // Validate input lottieView
1351 if (null == lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
1356 // Stop if it was playing
1357 if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)
1366 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
1367 [EditorBrowsable(EditorBrowsableState.Never)]
1368 public struct LottieAnimationViewDynamicProperty : IEquatable<LottieAnimationViewDynamicProperty>
1370 [EditorBrowsable(EditorBrowsableState.Never)]
1371 public string KeyPath { get; set; }
1373 [EditorBrowsable(EditorBrowsableState.Never)]
1374 public LottieAnimationView.VectorProperty Property { get; set; }
1376 [EditorBrowsable(EditorBrowsableState.Never)]
1377 public LottieAnimationView.DynamicPropertyCallbackType Callback { get; set; }
1379 public override bool Equals(object obj)
1381 if (obj is LottieAnimationViewDynamicProperty target)
1383 if (KeyPath == target.KeyPath && Property == target.Property && Callback == target.Callback)
1391 public override int GetHashCode()
1393 return base.GetHashCode();
1396 public static bool operator ==(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1398 return left.Equals(right);
1401 public static bool operator !=(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1403 return !(left == right);
1406 public bool Equals(LottieAnimationViewDynamicProperty other)
1408 if (KeyPath == other.KeyPath && Property == other.Property && Callback == other.Callback)