[NUI] Support to get Marker list information from lottie
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / BaseComponents / LottieAnimationView.cs
1 /*
2  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 using global::System;
19 using global::System.Runtime.InteropServices;
20 using System.ComponentModel;
21 using System.Collections.Generic;
22 using System.Globalization;
23
24 namespace Tizen.NUI.BaseComponents
25 {
26     /// <summary>
27     /// LottieAnimationView renders an animated vector image (Lottie file).
28     /// </summary>
29     /// <since_tizen> 7 </since_tizen>
30     public partial class LottieAnimationView : ImageView
31     {
32
33         #region Constructor, Destructor, Dispose
34         /// <summary>
35         /// LottieAnimationView constructor
36         /// </summary>
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>
39         /// <remarks>
40         /// If the shown parameter is false, the animation is not visible even if the LottieAnimationView instance is created.
41         /// </remarks>
42         /// <example>
43         /// <code>
44         /// LottieAnimationView myLottie = new LottieAnimationView();
45         /// LottieAnimationView myLottie2 = new LottieAnimationView(2.0f);
46         /// LottieAnimationView myLottie3 = new LottieAnimationView(1.0f, false);
47         /// </code>
48         /// </example>
49         /// <since_tizen> 7 </since_tizen>
50         public LottieAnimationView(float scale = 1.0f, bool shown = true) : base()
51         {
52             ActionPlay = Interop.LottieAnimationView.AnimatedVectorImageVisualActionPlayGet();
53             ActionPause = Interop.LottieAnimationView.AnimatedVectorImageVisualActionPauseGet();
54             ActionStop = Interop.LottieAnimationView.AnimatedVectorImageVisualActionStopGet();
55
56             NUILog.Debug($"< constructor GetId={GetId()} >");
57             currentStates.url = "";
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;
67             SetVisible(shown);
68         }
69
70         /// <summary>
71         /// Dispose(DisposeTypes type)
72         /// </summary>
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)
77         {
78             if (disposed)
79             {
80                 return;
81             }
82
83             CleanCallbackDictionaries();
84
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.
88
89             //disconnect event signal
90             if (finishedEventHandler != null && visualEventSignalCallback != null)
91             {
92                 using VisualEventSignal visualEvent = VisualEventSignal();
93                 visualEvent.Disconnect(visualEventSignalCallback);
94                 finishedEventHandler = null;
95                 NUILog.Debug($"disconnect event signal");
96             }
97
98             base.Dispose(type);
99         }
100
101         // This is used for internal purpose. hidden API.
102         [EditorBrowsable(EditorBrowsableState.Never)]
103         protected override void Dispose(bool disposing)
104         {
105             // Note : We can clean dictionaries even this API called from GC Thread.
106             CleanCallbackDictionaries();
107             base.Dispose(disposing);
108         }
109         #endregion Constructor, Destructor, Dispose
110
111
112         #region Property
113         /// <summary>
114         /// Set or Get resource URL of Lottie file.
115         /// </summary>
116         /// <since_tizen> 7 </since_tizen>
117         public string URL
118         {
119             get
120             {
121                 return GetValue(URLProperty) as string;
122             }
123             set
124             {
125                 SetValue(URLProperty, value);
126                 NotifyPropertyChanged();
127             }
128         }
129
130         private string InternalURL
131         {
132             set
133             {
134                 string ret = (value == null ? "" : value);
135                 currentStates.url = ret;
136                 currentStates.changed = true;
137                 currentStates.totalFrame = -1; // Reset cached totalFrame value;
138                 currentStates.enableFrameCache = false;
139
140                 NUILog.Debug($"<[{GetId()}]SET url={currentStates.url}");
141
142                 // TODO : Could create new Image without additional creation?
143                 using PropertyMap map = new PropertyMap();
144                 using PropertyValue type = new PropertyValue((int)DevelVisual.Type.AnimatedVectorImage);
145                 using PropertyValue url = new PropertyValue(currentStates.url);
146                 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
147                 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
148                 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
149
150                 map.Add(Visual.Property.Type, type)
151                     .Add(ImageVisualProperty.URL, url)
152                     .Add(ImageVisualProperty.LoopCount, loopCnt)
153                     .Add(ImageVisualProperty.StopBehavior, stopAction)
154                     .Add(ImageVisualProperty.LoopingMode, loopMode);
155                 Image = map;
156
157                 currentStates.contentInfo = null;
158
159                 if (currentStates.scale != 1.0f)
160                 {
161                     Scale = new Vector3(currentStates.scale, currentStates.scale, currentStates.scale);
162                 }
163                 NUILog.Debug($"<[{GetId()}]>");
164             }
165             get
166             {
167                 string ret = currentStates.url;
168                 NUILog.Debug($"<[{GetId()}] GET");
169                 NUILog.Debug($"gotten url={ret} >");
170                 return ret;
171             }
172         }
173
174         /// <summary>
175         /// Gets the playing state
176         /// </summary>
177         /// <since_tizen> 7 </since_tizen>
178         public PlayStateType PlayState
179         {
180             get
181             {
182                 NUILog.Debug($"< Get!");
183
184                 int ret = 0;
185                 Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayState, out ret);
186
187                 currentStates.playState = (PlayStateType)ret;
188                 NUILog.Debug($"gotten play state={currentStates.playState} >");
189
190                 return currentStates.playState;
191             }
192         }
193
194         /// <summary>
195         /// Get the number of total frames
196         /// </summary>
197         /// <since_tizen> 7 </since_tizen>
198         public int TotalFrame
199         {
200             get
201             {
202                 int ret = currentStates.totalFrame;
203                 if (ret == -1)
204                 {
205                     Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.TotalFrameNumber, out ret);
206
207                     currentStates.totalFrame = ret;
208                     NUILog.Debug($"TotalFrameNumber get! ret={ret}");
209                 }
210                 return ret;
211             }
212         }
213
214         /// <summary>
215         /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
216         /// </summary>
217         /// <remarks>
218         /// 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.
219         /// </remarks>
220         /// <example>
221         /// We assume that the animation in myLottie.json file has 100 frames originally. If so, its frame index will be 0 - 99.
222         /// <code>
223         /// LottieAnimationView myLottie = new LottieAnimationView();
224         /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
225         /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
226         /// myLottie.CurrentFrame = 200; //display 99 frame
227         /// myLottie.SetMinMaxFrame(10, 20);
228         /// myLottie.CurrentFrame = 15; //display 15 frame
229         /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
230         /// </code>
231         /// </example>
232         /// <since_tizen> 7 </since_tizen>
233         public int CurrentFrame
234         {
235             get
236             {
237                 return (int)GetValue(CurrentFrameProperty);
238             }
239             set
240             {
241                 SetValue(CurrentFrameProperty, value);
242                 NotifyPropertyChanged();
243             }
244         }
245
246         private int InternalCurrentFrame
247         {
248             set
249             {
250                 NUILog.Debug($"<[{GetId()}]SET frame={value}>");
251
252                 Interop.View.DoActionWithSingleIntAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionJumpTo, value);
253             }
254             get
255             {
256                 int ret = 0;
257
258                 Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.CurrentFrameNumber, out ret);
259
260                 NUILog.Debug($"CurrentFrameNumber get! val={ret}");
261                 return ret;
262             }
263         }
264
265         /// <summary>
266         /// Sets or gets the looping mode of Lottie animation.
267         /// </summary>
268         /// <since_tizen> 7 </since_tizen>
269         public LoopingModeType LoopingMode
270         {
271             get
272             {
273                 return (LoopingModeType)GetValue(LoopingModeProperty);
274             }
275             set
276             {
277                 SetValue(LoopingModeProperty, value);
278                 NotifyPropertyChanged();
279             }
280         }
281
282         private LoopingModeType InternalLoopingMode
283         {
284             set
285             {
286                 if (currentStates.loopMode != (LoopingModeType)value)
287                 {
288                     currentStates.changed = true;
289                     currentStates.loopMode = (LoopingModeType)value;
290
291                     NUILog.Debug($"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
292
293                     Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.LoopingMode, (int)currentStates.loopMode);
294                 }
295             }
296             get
297             {
298                 NUILog.Debug($"LoopMode get! {currentStates.loopMode}");
299                 return currentStates.loopMode;
300             }
301         }
302
303         /// <summary>
304         /// Sets or gets the loop count.
305         /// </summary>
306         /// <remarks>
307         /// The minus value means the infinite loop count.
308         /// </remarks>
309         /// <example>
310         /// <code>
311         /// LottieAnimationView myLottie = new LottieAnimationView();
312         /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
313         /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
314         /// myLottie.LoopCount = -1; //infinite loop
315         /// myLottie.Play();
316         /// myLottie.Stop(); //it plays continuously unless Stop() is called
317         /// myLottie.LoopCount = 2;
318         /// myLottie.Play(); //it plays only 2 times and stops automatically
319         /// </code>
320         /// </example>
321         /// <since_tizen> 7 </since_tizen>
322         public int LoopCount
323         {
324             get
325             {
326                 return (int)GetValue(LoopCountProperty);
327             }
328             set
329             {
330                 SetValue(LoopCountProperty, value);
331                 NotifyPropertyChanged();
332             }
333         }
334
335         private int InternalLoopCount
336         {
337             set
338             {
339                 if (currentStates.loopCount != value)
340                 {
341                     currentStates.changed = true;
342                     currentStates.loopCount = value;
343
344                     NUILog.Debug($"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
345
346                     Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.LoopCount, currentStates.loopCount);
347                 }
348             }
349             get
350             {
351                 NUILog.Debug($"LoopCount get! {currentStates.loopCount}");
352                 return currentStates.loopCount;
353             }
354         }
355
356         /// <summary>
357         /// Sets or gets the stop behavior.
358         /// </summary>
359         /// <since_tizen> 7 </since_tizen>
360         public StopBehaviorType StopBehavior
361         {
362             get
363             {
364                 return (StopBehaviorType)GetValue(StopBehaviorProperty);
365             }
366             set
367             {
368                 SetValue(StopBehaviorProperty, value);
369                 NotifyPropertyChanged();
370             }
371         }
372
373         private StopBehaviorType InternalStopBehavior
374         {
375             set
376             {
377                 if (currentStates.stopEndAction != (StopBehaviorType)value)
378                 {
379                     currentStates.changed = true;
380                     currentStates.stopEndAction = (StopBehaviorType)value;
381
382                     NUILog.Debug($"<[{GetId()}]SET val={currentStates.stopEndAction}>");
383
384                     Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.StopBehavior, (int)currentStates.stopEndAction);
385                 }
386             }
387             get
388             {
389                 NUILog.Debug($"StopBehavior get! {currentStates.stopEndAction}");
390                 return currentStates.stopEndAction;
391             }
392         }
393
394         /// <summary>
395         /// Whether to redraw the image when the visual is scaled down.
396         /// </summary>
397         /// <remarks>
398         /// Inhouse API.
399         /// It is used in the AnimatedVectorImageVisual.The default is true.
400         /// </remarks>
401         [EditorBrowsable(EditorBrowsableState.Never)]
402         public bool RedrawInScalingDown
403         {
404             get
405             {
406                 return (bool)GetValue(RedrawInScalingDownProperty);
407             }
408             set
409             {
410                 SetValue(RedrawInScalingDownProperty, value);
411                 NotifyPropertyChanged();
412             }
413         }
414
415         private bool InternalRedrawInScalingDown
416         {
417             set
418             {
419                 if (currentStates.redrawInScalingDown != value)
420                 {
421                     currentStates.changed = true;
422                     currentStates.redrawInScalingDown = value;
423
424                     NUILog.Debug($"<[{GetId()}]SET currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
425
426                     Interop.View.InternalUpdateVisualPropertyBool(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.RedrawInScalingDown, currentStates.redrawInScalingDown);
427                 }
428             }
429             get
430             {
431                 NUILog.Debug($"RedrawInScalingDown get! {currentStates.redrawInScalingDown}");
432                 return currentStates.redrawInScalingDown;
433             }
434         }
435
436
437         /// <summary>
438         /// Actions property value to Jump to the specified frame.
439         /// This property can be redefined by child class if it use different value.
440         /// </summary>
441         [EditorBrowsable(EditorBrowsableState.Never)]
442         protected int ActionJumpTo { get; set; } = Interop.LottieAnimationView.AnimatedVectorImageVisualActionJumpToGet();
443
444         // This is used for internal purpose. hidden API.
445         [EditorBrowsable(EditorBrowsableState.Never)]
446         protected int SetDynamicProperty => ActionJumpTo + 1;
447         [EditorBrowsable(EditorBrowsableState.Never)]
448         public bool EnableFrameCache
449         {
450             get
451             {
452                 return (bool)GetValue(EnableFrameCacheProperty);
453             }
454             set
455             {
456                 SetValue(EnableFrameCacheProperty, value);
457                 NotifyPropertyChanged();
458             }
459         }
460
461         private bool InternalEnableFrameCache
462         {
463             set
464             {
465                 if (currentStates.enableFrameCache != value)
466                 {
467                     currentStates.changed = true;
468                     currentStates.enableFrameCache = value;
469
470                     NUILog.Debug($"<[{GetId()}]SET currentStates.EnableFrameCache={currentStates.enableFrameCache}>");
471
472                     Interop.View.InternalUpdateVisualPropertyBool(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.EnableFrameCache, currentStates.enableFrameCache);
473                 }
474             }
475             get
476             {
477                 NUILog.Debug($"EnableFrameCache get! {currentStates.enableFrameCache}");
478                 return currentStates.enableFrameCache;
479             }
480         }
481         #endregion Property
482
483
484         #region Method
485         /// <summary>
486         /// Set the minimum and the maximum frame.
487         /// </summary>
488         /// <param name="minFrame">minimum frame</param>
489         /// <param name="maxFrame">maximum frame</param>
490         /// <since_tizen> 7 </since_tizen>
491         public void SetMinMaxFrame(int minFrame, int maxFrame)
492         {
493             if (currentStates.framePlayRangeMin != minFrame || currentStates.framePlayRangeMax != maxFrame)
494             {
495                 NUILog.Debug($"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
496                 currentStates.changed = true;
497                 currentStates.framePlayRangeMin = minFrame;
498                 currentStates.framePlayRangeMax = maxFrame;
499
500                 Interop.View.InternalUpdateVisualPropertyIntPair(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.framePlayRangeMin, currentStates.framePlayRangeMax);
501
502                 NUILog.Debug($"  [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
503             }
504         }
505
506         /// <summary>
507         /// Play Animation.
508         /// </summary>
509         /// <since_tizen> 7 </since_tizen>
510         public new void Play()
511         {
512             NUILog.Debug($"<[{GetId()}] Play()");
513             debugPrint();
514             base.Play();
515             NUILog.Debug($"[{GetId()}]>");
516         }
517
518         /// <summary>
519         /// Pause Animation.
520         /// </summary>
521         /// <since_tizen> 7 </since_tizen>
522         public new void Pause()
523         {
524             NUILog.Debug($"<[{GetId()}] Pause()>");
525             debugPrint();
526             base.Pause();
527             NUILog.Debug($"[{GetId()}]>");
528         }
529
530         /// <summary>
531         /// Stop Animation.
532         /// </summary>
533         /// <since_tizen> 7 </since_tizen>
534         public new void Stop()
535         {
536             NUILog.Debug($"<[{GetId()}] Stop()");
537             debugPrint();
538             base.Stop();
539             NUILog.Debug($"[{GetId()}]>");
540         }
541
542         /// <summary>
543         /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
544         /// </summary>
545         /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
546         /// <since_tizen> 7 </since_tizen>
547         public List<Tuple<string, int, int>> GetContentInfo()
548         {
549             if (currentStates.contentInfo != null)
550             {
551                 return currentStates.contentInfo;
552             }
553
554             NUILog.Debug($"<");
555
556             PropertyMap imageMap = base.Image;
557             if (imageMap != null)
558             {
559                 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
560                 PropertyMap contentMap = new PropertyMap();
561                 if (val?.Get(ref contentMap) == true)
562                 {
563                     currentStates.contentInfo = new List<Tuple<string, int, int>>();
564                     for (uint i = 0; i < contentMap.Count(); i++)
565                     {
566                         using PropertyKey propertyKey = contentMap.GetKeyAt(i);
567                         string key = propertyKey.StringKey;
568
569                         using PropertyValue arrVal = contentMap.GetValue(i);
570                         using PropertyArray arr = new PropertyArray();
571                         if (arrVal.Get(arr))
572                         {
573                             int startFrame = -1;
574                             using PropertyValue start = arr.GetElementAt(0);
575                             start?.Get(out startFrame);
576
577                             int endFrame = -1;
578                             using PropertyValue end = arr.GetElementAt(1);
579                             end?.Get(out endFrame);
580
581                             NUILog.Debug($"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
582
583                             Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
584
585                             currentStates.contentInfo?.Add(item);
586                         }
587                     }
588                 }
589                 contentMap.Dispose();
590                 val?.Dispose();
591             }
592             NUILog.Debug($">");
593
594             return currentStates.contentInfo;
595         }
596
597         /// <summary>
598         /// Get the list of markers' information such as the start frame and the end frame in the Lottie file.
599         /// </summary>
600         /// <returns>List of Tuple (string of marker name, integer of start frame, integer of end frame)</returns>
601         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
602         [EditorBrowsable(EditorBrowsableState.Never)]
603         public List<Tuple<string, int, int>> GetMarkerInfo()
604         {
605             if (currentStates.markerInfo != null)
606             {
607                 return currentStates.markerInfo;
608             }
609
610             NUILog.Debug($"<");
611
612             PropertyMap imageMap = base.Image;
613             if (imageMap != null)
614             {
615                 PropertyValue val = imageMap.Find(ImageVisualProperty.MarkerInfo);
616                 PropertyMap markerMap = new PropertyMap();
617                 if (val?.Get(ref markerMap) == true)
618                 {
619                     currentStates.markerInfo = new List<Tuple<string, int, int>>();
620                     for (uint i = 0; i < markerMap.Count(); i++)
621                     {
622                         using PropertyKey propertyKey = markerMap.GetKeyAt(i);
623                         string key = propertyKey.StringKey;
624
625                         using PropertyValue arrVal = markerMap.GetValue(i);
626                         using PropertyArray arr = new PropertyArray();
627                         if (arrVal.Get(arr))
628                         {
629                             int startFrame = -1;
630                             using PropertyValue start = arr.GetElementAt(0);
631                             start?.Get(out startFrame);
632
633                             int endFrame = -1;
634                             using PropertyValue end = arr.GetElementAt(1);
635                             end?.Get(out endFrame);
636
637                             NUILog.Debug($"[{i}] marker name={key}, startFrame={startFrame}, endFrame={endFrame}");
638
639                             Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
640
641                             currentStates.markerInfo?.Add(item);
642                         }
643                     }
644                 }
645                 markerMap.Dispose();
646                 val?.Dispose();
647             }
648             NUILog.Debug($">");
649
650             return currentStates.markerInfo;
651         }
652
653         /// <summary>
654         /// A marker has its start frame and end frame.
655         /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
656         /// 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.   *
657         /// </summary>
658         /// <param name="marker1">First marker</param>
659         /// <param name="marker2">Second marker</param>
660         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
661         [EditorBrowsable(EditorBrowsableState.Never)]
662         public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
663         {
664             if (currentStates.mark1 != marker1 || currentStates.mark2 != marker2)
665             {
666                 NUILog.Debug($"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
667
668                 currentStates.changed = true;
669                 currentStates.mark1 = marker1;
670                 currentStates.mark2 = marker2;
671
672                 if (string.IsNullOrEmpty(currentStates.mark2))
673                 {
674                     Interop.View.InternalUpdateVisualPropertyString(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.mark1);
675                 }
676                 else
677                 {
678                     Interop.View.InternalUpdateVisualPropertyStringPair(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.mark1, currentStates.mark2);
679                 }
680
681                 NUILog.Debug($"  [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
682             }
683         }
684
685         /// <summary>
686         /// Get MinMax Frame
687         /// </summary>
688         /// <returns>Tuple of Min and Max frames</returns>
689         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
690         [EditorBrowsable(EditorBrowsableState.Never)]
691         public Tuple<int, int> GetMinMaxFrame()
692         {
693             NUILog.Debug($"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
694
695             using PropertyMap map = Image;
696             if (map != null)
697             {
698                 using PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
699                 if (val != null)
700                 {
701                     using PropertyArray array = new PropertyArray();
702                     if (val.Get(array))
703                     {
704                         uint cnt = array.Count();
705                         int item1 = -1, item2 = -1;
706                         for (uint i = 0; i < cnt; i++)
707                         {
708                             using PropertyValue v = array.GetElementAt(i);
709                             int intRet;
710                             if (v.Get(out intRet))
711                             {
712                                 NUILog.Debug($"Got play range of string [{i}]: {intRet}");
713                                 if (i == 0)
714                                 {
715                                     item1 = intRet;
716                                 }
717                                 else if (i == 1)
718                                 {
719                                     item2 = intRet;
720                                 }
721                             }
722                             else
723                             {
724                                 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
725                             }
726                         }
727                         NUILog.Debug($"  [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
728                         return new Tuple<int, int>(item1, item2);
729                     }
730                 }
731             }
732             Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
733             return new Tuple<int, int>(-1, -1);
734         }
735
736         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
737         [EditorBrowsable(EditorBrowsableState.Never)]
738         public void DoActionExtension(LottieAnimationViewDynamicProperty info)
739         {
740             dynamicPropertyCallbackId++;
741
742             weakReferencesOfLottie?.Add(dynamicPropertyCallbackId, new WeakReference<LottieAnimationView>(this));
743             InternalSavedDynamicPropertyCallbacks?.Add(dynamicPropertyCallbackId, info.Callback);
744
745             Interop.View.DoActionExtension(SwigCPtr, ImageView.Property.IMAGE, SetDynamicProperty, dynamicPropertyCallbackId, info.KeyPath, (int)info.Property, Marshal.GetFunctionPointerForDelegate<System.Delegate>(rootCallback));
746
747             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
748         }
749
750         private void CleanCallbackDictionaries()
751         {
752             if (weakReferencesOfLottie?.Count > 0 && InternalSavedDynamicPropertyCallbacks != null)
753             {
754                 foreach (var key in InternalSavedDynamicPropertyCallbacks?.Keys)
755                 {
756                     if (weakReferencesOfLottie.ContainsKey(key))
757                     {
758                         weakReferencesOfLottie.Remove(key);
759                     }
760                 }
761             }
762             InternalSavedDynamicPropertyCallbacks?.Clear();
763             InternalSavedDynamicPropertyCallbacks = null;
764         }
765         #endregion Method
766
767
768         #region Event, Enum, Struct, ETC
769         /// <summary>
770         /// Animation finished event.
771         /// </summary>
772         /// <since_tizen> 7 </since_tizen>
773         public event EventHandler Finished
774         {
775             add
776             {
777                 if (finishedEventHandler == null)
778                 {
779                     NUILog.Debug($"<[{GetId()}] Finished eventhandler added>");
780                     visualEventSignalCallback = onVisualEventSignal;
781                     using VisualEventSignal visualEvent = VisualEventSignal();
782                     visualEvent.Connect(visualEventSignalCallback);
783                 }
784                 finishedEventHandler += value;
785             }
786             remove
787             {
788                 NUILog.Debug($"<[{GetId()}] Finished eventhandler removed>");
789                 finishedEventHandler -= value;
790                 if (finishedEventHandler == null && visualEventSignalCallback != null)
791                 {
792                     using VisualEventSignal visualEvent = VisualEventSignal();
793                     visualEvent.Disconnect(visualEventSignalCallback);
794                     if (visualEvent?.Empty() == true)
795                     {
796                         visualEventSignalCallback = null;
797                     }
798                 }
799             }
800         }
801
802         /// <summary>
803         /// Enumeration for what state the vector animation is in
804         /// </summary>
805         /// <since_tizen> 7 </since_tizen>
806         public enum PlayStateType
807         {
808             /// <summary>
809             /// Invalid
810             /// </summary>
811             /// <since_tizen> 7 </since_tizen>
812             Invalid = -1,
813             /// <summary>
814             /// Vector Animation has stopped
815             /// </summary>
816             /// <since_tizen> 7 </since_tizen>
817             Stopped = 0,
818             /// <summary>
819             /// The vector animation is playing
820             /// </summary>
821             /// <since_tizen> 7 </since_tizen>
822             Playing = 1,
823             /// <summary>
824             /// The vector animation is paused
825             /// </summary>
826             /// <since_tizen> 7 </since_tizen>
827             Paused = 2
828         }
829
830         /// <summary>
831         /// Enumeration for what to do when the animation is stopped.
832         /// </summary>
833         /// <since_tizen> 7 </since_tizen>
834         public enum StopBehaviorType
835         {
836             /// <summary>
837             /// When the animation is stopped, the current frame is shown.
838             /// </summary>
839             /// <since_tizen> 7 </since_tizen>
840             CurrentFrame,
841             /// <summary>
842             /// When the animation is stopped, the min frame (first frame) is shown.
843             /// </summary>
844             /// <since_tizen> 7 </since_tizen>
845             MinimumFrame,
846             /// <summary>
847             /// When the animation is stopped, the max frame (last frame) is shown.
848             /// </summary>
849             /// <since_tizen> 7 </since_tizen>
850             MaximumFrame
851         }
852
853         /// <summary>
854         /// Enumeration for what looping mode is in.
855         /// </summary>
856         /// <since_tizen> 7 </since_tizen>
857         public enum LoopingModeType
858         {
859             /// <summary>
860             /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
861             /// </summary>
862             /// <since_tizen> 7 </since_tizen>
863             Restart,
864             /// <summary>
865             /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
866             /// </summary>
867             /// <since_tizen> 7 </since_tizen>
868             AutoReverse
869         }
870
871         /// <summary>
872         /// Vector Property
873         /// </summary>
874         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
875         [EditorBrowsable(EditorBrowsableState.Never)]
876         public enum VectorProperty
877         {
878             /// <summary>
879             /// Fill color of the object, Type of <see cref="Vector3"/>
880             /// </summary>
881             [EditorBrowsable(EditorBrowsableState.Never)]
882             FillColor,
883
884             /// <summary>
885             /// Fill opacity of the object, Type of float
886             /// </summary>
887             [EditorBrowsable(EditorBrowsableState.Never)]
888             FillOpacity,
889
890             /// <summary>
891             /// Stroke color of the object, Type of <see cref="Vector3"/>
892             /// </summary>
893             [EditorBrowsable(EditorBrowsableState.Never)]
894             StrokeColor,
895
896             /// <summary>
897             /// Stroke opacity of the object, Type of float
898             /// </summary>
899             [EditorBrowsable(EditorBrowsableState.Never)]
900             StrokeOpacity,
901
902             /// <summary>
903             /// Stroke width of the object, Type of float
904             /// </summary>
905             [EditorBrowsable(EditorBrowsableState.Never)]
906             StrokeWidth,
907
908             /// <summary>
909             /// Transform anchor of the Layer and Group object, Type of <see cref="Vector2"/>
910             /// </summary>
911             [EditorBrowsable(EditorBrowsableState.Never)]
912             TransformAnchor,
913
914             /// <summary>
915             /// Transform position of the Layer and Group object, Type of <see cref="Vector2"/>
916             /// </summary>
917             [EditorBrowsable(EditorBrowsableState.Never)]
918             TransformPosition,
919
920             /// <summary>
921             /// Transform scale of the Layer and Group object, Type of <see cref="Vector2"/>, Value range of [0..100]
922             /// </summary>
923             [EditorBrowsable(EditorBrowsableState.Never)]
924             TransformScale,
925
926             /// <summary>
927             /// Transform rotation of the Layer and Group object, Type of float, Value range of [0..360] in degrees
928             /// </summary>
929             [EditorBrowsable(EditorBrowsableState.Never)]
930             TransformRotation,
931
932             /// <summary>
933             /// Transform opacity of the Layer and Group object, Type of float
934             /// </summary>
935             [EditorBrowsable(EditorBrowsableState.Never)]
936             TransformOpacity
937         };
938
939         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
940         [EditorBrowsable(EditorBrowsableState.Never)]
941         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
942         public delegate PropertyValue DynamicPropertyCallbackType(int returnType, uint frameNumber);
943         #endregion Event, Enum, Struct, ETC
944
945
946         #region Internal
947         internal class VisualEventSignalArgs : EventArgs
948         {
949             public int VisualIndex
950             {
951                 set;
952                 get;
953             }
954             public int SignalId
955             {
956                 set;
957                 get;
958             }
959         }
960
961         internal event EventHandler<VisualEventSignalArgs> VisualEvent
962         {
963             add
964             {
965                 if (visualEventSignalHandler == null)
966                 {
967                     visualEventSignalCallback = onVisualEventSignal;
968                     using VisualEventSignal visualEvent = VisualEventSignal();
969                     visualEvent?.Connect(visualEventSignalCallback);
970                 }
971                 visualEventSignalHandler += value;
972             }
973             remove
974             {
975                 visualEventSignalHandler -= value;
976                 if (visualEventSignalHandler == null && visualEventSignalCallback != null)
977                 {
978                     using VisualEventSignal visualEvent = VisualEventSignal();
979                     visualEvent?.Disconnect(visualEventSignalCallback);
980                     if (visualEvent?.Empty() == true)
981                     {
982                         visualEventSignalCallback = null;
983                     }
984                 }
985             }
986         }
987
988         internal void EmitVisualEventSignal(int visualIndex, int signalId)
989         {
990             using VisualEventSignal visualEvent = VisualEventSignal();
991             visualEvent?.Emit(this, visualIndex, signalId);
992         }
993
994         internal VisualEventSignal VisualEventSignal()
995         {
996             VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
997             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
998             return ret;
999         }
1000
1001         internal Dictionary<int, DynamicPropertyCallbackType> InternalSavedDynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1002
1003         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1004         internal delegate void RootCallbackType(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3);
1005
1006         internal RootCallbackType rootCallback = RootCallback;
1007
1008         static internal void RootCallback(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3)
1009         {
1010             WeakReference<LottieAnimationView> current = null;
1011             LottieAnimationView currentView = null;
1012             DynamicPropertyCallbackType currentCallback = null;
1013             PropertyValue ret = null;
1014
1015             if (weakReferencesOfLottie.TryGetValue(id, out current))
1016             {
1017                 if (current.TryGetTarget(out currentView))
1018                 {
1019                     if (currentView != null && currentView.InternalSavedDynamicPropertyCallbacks != null &&
1020                         currentView.InternalSavedDynamicPropertyCallbacks.TryGetValue(id, out currentCallback))
1021                     {
1022                         ret = currentCallback?.Invoke(returnType, frameNumber);
1023                     }
1024                     else
1025                     {
1026                         Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1027                         return;
1028                     }
1029                 }
1030                 else
1031                 {
1032                     Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1033                     return;
1034                 }
1035             }
1036             else
1037             {
1038                 Tizen.Log.Error("NUI", "can't find LottieAnimationView by id, just return here!");
1039                 return;
1040             }
1041
1042             switch (returnType)
1043             {
1044                 case (int)(VectorProperty.FillColor):
1045                 case (int)(VectorProperty.StrokeColor):
1046                     Vector3 tmpVector3 = new Vector3(-1, -1, -1);
1047                     if ((ret != null) && ret.Get(tmpVector3))
1048                     {
1049                         val1 = tmpVector3.X;
1050                         val2 = tmpVector3.Y;
1051                         val3 = tmpVector3.Z;
1052                     }
1053                     tmpVector3.Dispose();
1054                     break;
1055
1056                 case (int)(VectorProperty.TransformAnchor):
1057                 case (int)(VectorProperty.TransformPosition):
1058                 case (int)(VectorProperty.TransformScale):
1059                     Vector2 tmpVector2 = new Vector2(-1, -1);
1060                     if ((ret != null) && ret.Get(tmpVector2))
1061                     {
1062                         val1 = tmpVector2.X;
1063                         val2 = tmpVector2.Y;
1064                     }
1065                     tmpVector2.Dispose();
1066                     break;
1067
1068                 case (int)(VectorProperty.FillOpacity):
1069                 case (int)(VectorProperty.StrokeOpacity):
1070                 case (int)(VectorProperty.StrokeWidth):
1071                 case (int)(VectorProperty.TransformRotation):
1072                 case (int)(VectorProperty.TransformOpacity):
1073                     float tmpFloat = -1;
1074                     if ((ret != null) && ret.Get(out tmpFloat))
1075                     {
1076                         val1 = tmpFloat;
1077                     }
1078                     break;
1079                 default:
1080                     //do nothing
1081                     break;
1082             }
1083             ret?.Dispose();
1084         }
1085         #endregion Internal
1086
1087
1088         #region Private
1089         private struct states
1090         {
1091             internal string url;
1092             internal int loopCount;
1093             internal LoopingModeType loopMode;
1094             internal StopBehaviorType stopEndAction;
1095             internal int framePlayRangeMin;
1096             internal int framePlayRangeMax;
1097             internal bool changed;
1098             internal int totalFrame;
1099             internal float scale;
1100             internal PlayStateType playState;
1101             internal List<Tuple<string, int, int>> contentInfo;
1102             internal List<Tuple<string, int, int>> markerInfo;
1103             internal string mark1, mark2;
1104             internal bool redrawInScalingDown;
1105             internal bool enableFrameCache;
1106         };
1107         private states currentStates;
1108
1109         private struct DevelVisual
1110         {
1111             internal enum Type
1112             {
1113                 AnimatedGradient = Visual.Type.AnimatedImage + 1,
1114                 AnimatedVectorImage = Visual.Type.AnimatedImage + 2,
1115             }
1116         }
1117
1118         private const string tag = "NUITEST";
1119         private event EventHandler finishedEventHandler;
1120
1121         private void OnFinished()
1122         {
1123             NUILog.Debug($"<[{GetId()}] OnFinished()>");
1124             finishedEventHandler?.Invoke(this, null);
1125         }
1126
1127         private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
1128         {
1129             OnFinished();
1130
1131             if (targetView != IntPtr.Zero)
1132             {
1133                 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
1134                 if (v != null)
1135                 {
1136                     NUILog.Debug($"targetView is not null! name={v.Name}");
1137                 }
1138                 else
1139                 {
1140                     NUILog.Debug($"target is something created from dali");
1141                 }
1142             }
1143             VisualEventSignalArgs e = new VisualEventSignalArgs();
1144             e.VisualIndex = visualIndex;
1145             e.SignalId = signalId;
1146             visualEventSignalHandler?.Invoke(this, e);
1147
1148             NUILog.Debug($"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
1149         }
1150
1151         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
1152         private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
1153
1154         private VisualEventSignalCallbackType visualEventSignalCallback;
1155         private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
1156
1157         static private int dynamicPropertyCallbackId = 0;
1158         //static private Dictionary<int, DynamicPropertyCallbackType> dynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1159         static private Dictionary<int, WeakReference<LottieAnimationView>> weakReferencesOfLottie = new Dictionary<int, WeakReference<LottieAnimationView>>();
1160
1161         private void debugPrint()
1162         {
1163             NUILog.Debug($"===================================");
1164             NUILog.Debug($"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, \nframePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
1165             NUILog.Debug($"  get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState}");
1166             NUILog.Debug($"  RedrawInScalingDown={RedrawInScalingDown} >");
1167             NUILog.Debug($"===================================");
1168         }
1169
1170         #endregion Private
1171     }
1172
1173     /// <summary>
1174     /// A class containing frame informations for a LottieAnimationView.
1175     /// </summary>
1176     [EditorBrowsable(EditorBrowsableState.Never)]
1177     public class LottieFrameInfo : ICloneable
1178     {
1179         /// <summary>
1180         /// Creates a new instance with a playing range.
1181         /// </summary>
1182         [EditorBrowsable(EditorBrowsableState.Never)]
1183         public LottieFrameInfo(int startFrame, int endFrame)
1184         {
1185             StartFrame = startFrame;
1186             EndFrame = endFrame;
1187         }
1188
1189         /// <summary>
1190         /// Creates a new instance with a still image frame.
1191         /// </summary>
1192         [EditorBrowsable(EditorBrowsableState.Never)]
1193         public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
1194         {
1195         }
1196
1197         /// <summary>
1198         /// Create a new instance from a pair notation.
1199         /// </summary>
1200         [EditorBrowsable(EditorBrowsableState.Never)]
1201         public static implicit operator LottieFrameInfo((int, int) pair)
1202         {
1203             return new LottieFrameInfo(pair.Item1, pair.Item2);
1204         }
1205
1206         /// <summary>
1207         /// Create a new instance from an int value.
1208         /// </summary>
1209         [EditorBrowsable(EditorBrowsableState.Never)]
1210         public static implicit operator LottieFrameInfo(int stillImageFrame)
1211         {
1212             return new LottieFrameInfo(stillImageFrame);
1213         }
1214
1215         /// <summary>
1216         /// Create a new instance from string.
1217         /// Possible input : "0, 10", "10"
1218         /// </summary>
1219         [EditorBrowsable(EditorBrowsableState.Never)]
1220         public static implicit operator LottieFrameInfo(string pair)
1221         {
1222             if (pair == null)
1223             {
1224                 return null;
1225             }
1226
1227             string[] parts = pair.Split(',');
1228             if (parts.Length == 1)
1229             {
1230                 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture));
1231             }
1232             else if (parts.Length == 2)
1233             {
1234                 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), Int32.Parse(parts[1].Trim(), CultureInfo.InvariantCulture));
1235             }
1236
1237             Tizen.Log.Error("NUI", $"Can not convert string {pair} to LottieFrameInfo");
1238             return null;
1239         }
1240
1241         /// <summary>
1242         /// The start frame of the lottie animation.
1243         /// </summary>
1244         [EditorBrowsable(EditorBrowsableState.Never)]
1245         public int StartFrame { get; }
1246
1247         /// <summary>
1248         /// The end frame of the lottie animation.
1249         /// </summary>
1250         [EditorBrowsable(EditorBrowsableState.Never)]
1251         public int EndFrame { get; }
1252
1253         /// <summary>
1254         /// Create LottieFrameInfo struct with animation range information
1255         /// </summary>
1256         [EditorBrowsable(EditorBrowsableState.Never)]
1257         public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
1258         {
1259             return new LottieFrameInfo(startFrame, endFrame);
1260         }
1261
1262         /// <summary>
1263         /// Create LottieFrameInfo struct with still image information
1264         /// </summary>
1265         [EditorBrowsable(EditorBrowsableState.Never)]
1266         public static LottieFrameInfo CreateStillImage(int stillImageFrame)
1267         {
1268             return new LottieFrameInfo(stillImageFrame, stillImageFrame);
1269         }
1270
1271         /// <summary>
1272         /// Inhouse API.
1273         /// Whether this LottieFrameInfo represents one frame or more.
1274         /// </summary>
1275         [EditorBrowsable(EditorBrowsableState.Never)]
1276         public bool IsStillImage()
1277         {
1278             return StartFrame == EndFrame;
1279         }
1280
1281         /// <summary>
1282         /// Inhouse API.
1283         /// Play specified LottieAnimationView with this frame information.
1284         /// </summary>
1285         /// <param name="lottieView">The target LottieAnimationView to play.</param>
1286         /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
1287         [EditorBrowsable(EditorBrowsableState.Never)]
1288         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062: Validate arguments of public methods", Justification = "The null checking is done by BeReadyToShow()")]
1289         public void Show(LottieAnimationView lottieView, bool noPlay = false)
1290         {
1291             if (!BeReadyToShow(lottieView))
1292             {
1293                 return;
1294             }
1295
1296             lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
1297
1298             if (IsStillImage() || noPlay)
1299             {
1300                 lottieView.CurrentFrame = EndFrame;
1301             }
1302             else
1303             {
1304                 lottieView.CurrentFrame = StartFrame;
1305                 lottieView.Play();
1306             }
1307         }
1308
1309         /// <inheritdoc/>
1310         [EditorBrowsable(EditorBrowsableState.Never)]
1311         public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
1312
1313         private bool BeReadyToShow(LottieAnimationView lottieView)
1314         {
1315             // Validate input lottieView
1316             if (null == lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
1317             {
1318                 return false;
1319             }
1320
1321             // Stop if it was playing
1322             if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)
1323             {
1324                 lottieView.Stop();
1325             }
1326
1327             return true;
1328         }
1329     }
1330
1331     // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
1332     [EditorBrowsable(EditorBrowsableState.Never)]
1333     public struct LottieAnimationViewDynamicProperty : IEquatable<LottieAnimationViewDynamicProperty>
1334     {
1335         [EditorBrowsable(EditorBrowsableState.Never)]
1336         public string KeyPath { get; set; }
1337
1338         [EditorBrowsable(EditorBrowsableState.Never)]
1339         public LottieAnimationView.VectorProperty Property { get; set; }
1340
1341         [EditorBrowsable(EditorBrowsableState.Never)]
1342         public LottieAnimationView.DynamicPropertyCallbackType Callback { get; set; }
1343
1344         public override bool Equals(object obj)
1345         {
1346             if (obj is LottieAnimationViewDynamicProperty target)
1347             {
1348                 if (KeyPath == target.KeyPath && Property == target.Property && Callback == target.Callback)
1349                 {
1350                     return true;
1351                 }
1352             }
1353             return false;
1354         }
1355
1356         public override int GetHashCode()
1357         {
1358             return base.GetHashCode();
1359         }
1360
1361         public static bool operator ==(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1362         {
1363             return left.Equals(right);
1364         }
1365
1366         public static bool operator !=(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1367         {
1368             return !(left == right);
1369         }
1370
1371         public bool Equals(LottieAnimationViewDynamicProperty other)
1372         {
1373             if (other != null)
1374             {
1375                 if (KeyPath == other.KeyPath && Property == other.Property && Callback == other.Callback)
1376                 {
1377                     return true;
1378                 }
1379             }
1380             return false;
1381         }
1382     }
1383
1384 }