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