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