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