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