[NUI] Fix testhub errors by removing useless debugging logs
[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 class LottieAnimationView : ImageView
31     {
32         #region Constructor, Destructor, Dispose
33         /// <summary>
34         /// LottieAnimationView constructor
35         /// </summary>
36         /// <param name="scale">The factor of scaling image, default : 1.0f</param>
37         /// <param name="shown">false : not displayed (hidden), true : displayed (shown), default : true</param>
38         /// <remarks>
39         /// If the shown parameter is false, the animation is not visible even if the LottieAnimationView instance is created.
40         /// </remarks>
41         /// <example>
42         /// <code>
43         /// LottieAnimationView myLottie = new LottieAnimationView();
44         /// LottieAnimationView myLottie2 = new LottieAnimationView(2.0f);
45         /// LottieAnimationView myLottie3 = new LottieAnimationView(1.0f, false);
46         /// </code>
47         /// </example>
48         /// <since_tizen> 7 </since_tizen>
49         public LottieAnimationView(float scale = 1.0f, bool shown = true) : base()
50         {
51             NUILog.Debug($"< constructor GetId={GetId()} >");
52             currentStates.url = "";
53             currentStates.frame = -1;
54             currentStates.loopCount = 1;
55             currentStates.loopMode = LoopingModeType.Restart;
56             currentStates.stopEndAction = StopBehaviorType.CurrentFrame;
57             currentStates.framePlayRangeMin = -1;
58             currentStates.framePlayRangeMax = -1;
59             currentStates.changed = false;
60             currentStates.totalFrame = -1;
61             currentStates.scale = scale;
62             currentStates.redrawInScalingDown = true;
63             SetVisible(shown);
64         }
65
66         /// <summary>
67         /// Dispose(DisposeTypes type)
68         /// </summary>
69         /// <param name="type"></param>
70         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
71         [EditorBrowsable(EditorBrowsableState.Never)]
72         protected override void Dispose(DisposeTypes type)
73         {
74             if (disposed)
75             {
76                 return;
77             }
78
79             //Release your own unmanaged resources here.
80             //You should not access any managed member here except static instance.
81             //because the execution order of Finalizes is non-deterministic.
82
83             //disconnect event signal
84             if (finishedEventHandler != null && visualEventSignalCallback != null)
85             {
86                 VisualEventSignal().Disconnect(visualEventSignalCallback);
87                 finishedEventHandler = null;
88                 NUILog.Debug($"disconnect event signal");
89             }
90
91             base.Dispose(type);
92         }
93         #endregion Constructor, Destructor, Dispose
94
95
96         #region Property
97         /// <summary>
98         /// Set or Get resource URL of Lottie file.
99         /// </summary>
100         /// <since_tizen> 7 </since_tizen>
101         public string URL
102         {
103             set
104             {
105                 string ret = (value == null ? "" : value);
106                 currentStates.url = ret;
107                 currentStates.changed = true;
108
109                 NUILog.Debug($"<[{GetId()}]SET url={currentStates.url}");
110
111                 PropertyMap map = new PropertyMap();
112                 map.Add(Visual.Property.Type, new PropertyValue((int)DevelVisual.Type.AnimatedVectorImage))
113                     .Add(ImageVisualProperty.URL, new PropertyValue(currentStates.url))
114                     .Add(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount))
115                     .Add(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction))
116                     .Add(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
117                 Image = map;
118
119                 currentStates.contentInfo = null;
120
121                 if (currentStates.scale != 1.0f)
122                 {
123                     Scale = new Vector3(currentStates.scale, currentStates.scale, 0.0f);
124                 }
125                 NUILog.Debug($"<[{GetId()}]>");
126             }
127             get
128             {
129                 string ret = currentStates.url;
130                 NUILog.Debug($"<[{GetId()}] GET");
131
132                 PropertyMap map = Image;
133                 if (map != null)
134                 {
135                     PropertyValue val = map.Find(ImageVisualProperty.URL);
136                     if (val != null)
137                     {
138                         if (val.Get(out ret))
139                         {
140                             NUILog.Debug($"gotten url={ret} >");
141                             return ret;
142                         }
143                     }
144                 }
145                 Tizen.Log.Error(tag, $"  [ERROR][{GetId()}](LottieAnimationView) Fail to get URL from dali >");
146                 return ret;
147             }
148         }
149
150         /// <summary>
151         /// Gets the playing state
152         /// </summary>
153         /// <since_tizen> 7 </since_tizen>
154         public PlayStateType PlayState
155         {
156             get
157             {
158                 NUILog.Debug($"< Get!");
159                 PropertyMap map = base.Image;
160                 var ret = 0;
161                 if (map != null)
162                 {
163                     PropertyValue val = map.Find(ImageVisualProperty.PlayState);
164                     if (val != null)
165                     {
166                         if (val.Get(out ret))
167                         {
168                             currentStates.playState = (PlayStateType)ret;
169                             NUILog.Debug($"gotten play state={ret} >");
170                         }
171                     }
172                 }
173                 else
174                 {
175                     Tizen.Log.Error(tag, $"<[ERROR][{GetId()}]Fail to get PlayState from dali currentStates.playState={currentStates.playState}>");
176                 }
177                 return currentStates.playState;
178             }
179         }
180
181         /// <summary>
182         /// Get the number of total frames
183         /// </summary>
184         /// <since_tizen> 7 </since_tizen>
185         public int TotalFrame
186         {
187             get
188             {
189                 int ret = -1;
190                 PropertyMap map = Image;
191                 if (map != null)
192                 {
193                     PropertyValue val = map.Find(ImageVisualProperty.TotalFrameNumber);
194                     if (val != null)
195                     {
196                         if (val.Get(out ret))
197                         {
198                             //NUILog.Debug( $"TotalFrameNumber get! ret={ret}");
199                             currentStates.totalFrame = ret;
200                             return ret;
201                         }
202                     }
203                 }
204                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get TotalFrameNumber from dali>");
205                 return ret;
206             }
207         }
208
209         /// <summary>
210         /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
211         /// </summary>
212         /// <remarks>
213         /// 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.
214         /// </remarks>
215         /// <example>
216         /// We assume that the animation in myLottie.json file has 100 frames originally. If so, its frame index will be 0 - 99.
217         /// <code>
218         /// LottieAnimationView myLottie = new LottieAnimationView();
219         /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
220         /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
221         /// myLottie.CurrentFrame = 200; //display 99 frame
222         /// myLottie.SetMinMaxFrame(10, 20);
223         /// myLottie.CurrentFrame = 15; //display 15 frame
224         /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
225         /// </code>
226         /// </example>
227         /// <since_tizen> 7 </since_tizen>
228         public int CurrentFrame
229         {
230             set
231             {
232                 currentStates.frame = value;
233                 NUILog.Debug($"<[{GetId()}]SET frame={currentStates.frame}>");
234                 DoAction(ImageView.Property.IMAGE, (int)actionType.jumpTo, new PropertyValue(currentStates.frame));
235             }
236             get
237             {
238                 int ret = 0;
239                 PropertyMap map = Image;
240                 if (map != null)
241                 {
242                     PropertyValue val = map.Find(ImageVisualProperty.CurrentFrameNumber);
243                     if (val != null)
244                     {
245                         if (val.Get(out ret))
246                         {
247                             //NUILog.Debug( $"CurrentFrameNumber get! val={ret}");
248                             return ret;
249                         }
250                     }
251                 }
252                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get CurrentFrameNumber from dali!! ret={ret}>");
253                 return ret;
254             }
255         }
256
257         /// <summary>
258         /// Sets or gets the looping mode of Lottie animation.
259         /// </summary>
260         /// <since_tizen> 7 </since_tizen>
261         public LoopingModeType LoopingMode
262         {
263             set
264             {
265                 currentStates.loopMode = (LoopingModeType)value;
266                 currentStates.changed = true;
267
268                 NUILog.Debug($"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
269                 PropertyMap map = new PropertyMap();
270                 map.Add(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
271                 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
272             }
273             get
274             {
275                 //NUILog.Debug( $"LoopMode get!");
276                 PropertyMap map = base.Image;
277                 var ret = 0;
278                 if (map != null)
279                 {
280                     PropertyValue val = map.Find(ImageVisualProperty.LoopingMode);
281                     if (val != null)
282                     {
283                         if (val.Get(out ret))
284                         {
285                             //NUILog.Debug( $"gotten LoopMode={ret}");
286                             if (ret != (int)currentStates.loopMode && ret > 0)
287                             {
288                                 NUILog.Debug($" [ERROR][{GetId()}](LottieAnimationView) different LoopMode! gotten={ret}, loopMode={currentStates.loopMode}");
289                             }
290                             currentStates.loopMode = (LoopingModeType)ret;
291                             return (LoopingModeType)ret;
292                         }
293                     }
294                 }
295                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get loopMode from dali>");
296                 return currentStates.loopMode;
297             }
298         }
299
300         /// <summary>
301         /// Sets or gets the loop count.
302         /// </summary>
303         /// <remarks>
304         /// The minus value means the infinite loop count.
305         /// </remarks>
306         /// <example>
307         /// <code>
308         /// LottieAnimationView myLottie = new LottieAnimationView();
309         /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
310         /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
311         /// myLottie.LoopCount = -1; //infinite loop
312         /// myLottie.Play();
313         /// myLottie.Stop(); //it plays continuously unless Stop() is called
314         /// myLottie.LoopCount = 2;
315         /// myLottie.Play(); //it plays only 2 times and stops automatically
316         /// </code>
317         /// </example>
318         /// <since_tizen> 7 </since_tizen>
319         public int LoopCount
320         {
321             set
322             {
323                 currentStates.changed = true;
324                 currentStates.loopCount = value;
325                 NUILog.Debug($"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
326                 PropertyMap map = new PropertyMap();
327                 map.Add(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount));
328                 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
329             }
330             get
331             {
332                 //NUILog.Debug( $"LoopCount get!");
333                 PropertyMap map = base.Image;
334                 var ret = 0;
335                 if (map != null)
336                 {
337                     PropertyValue val = map.Find(ImageVisualProperty.LoopCount);
338                     if (val != null)
339                     {
340                         if (val.Get(out ret))
341                         {
342                             //NUILog.Debug( $"gotten loop count={ret}");
343                             if (ret != currentStates.loopCount && ret > 0)
344                             {
345                                 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different loop count! gotten={ret}, loopCount={currentStates.loopCount}>");
346                             }
347                             currentStates.loopCount = ret;
348                             return currentStates.loopCount;
349                         }
350                     }
351                 }
352                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get LoopCount from dali  currentStates.loopCount={currentStates.loopCount}>");
353                 return currentStates.loopCount;
354             }
355         }
356
357         /// <summary>
358         /// Sets or gets the stop behavior.
359         /// </summary>
360         /// <since_tizen> 7 </since_tizen>
361         public StopBehaviorType StopBehavior
362         {
363             set
364             {
365                 currentStates.stopEndAction = (StopBehaviorType)value;
366                 currentStates.changed = true;
367
368                 NUILog.Debug($"<[{GetId()}]SET val={currentStates.stopEndAction}>");
369                 PropertyMap map = new PropertyMap();
370                 map.Add(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction));
371                 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
372             }
373             get
374             {
375                 //NUILog.Debug( $"StopBehavior get!");
376                 PropertyMap map = base.Image;
377                 var ret = 0;
378                 if (map != null)
379                 {
380                     PropertyValue val = map.Find(ImageVisualProperty.StopBehavior);
381                     if (val != null)
382                     {
383                         if (val.Get(out ret))
384                         {
385                             //NUILog.Debug( $"gotten StopBehavior={ret}");
386                             if (ret != (int)currentStates.stopEndAction)
387                             {
388                                 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different StopBehavior! gotten={ret}, StopBehavior={currentStates.stopEndAction}>");
389                             }
390                             currentStates.stopEndAction = (StopBehaviorType)ret;
391                             return (StopBehaviorType)ret;
392                         }
393                     }
394                 }
395                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get StopBehavior from dali>");
396                 return currentStates.stopEndAction;
397             }
398         }
399
400         /// <summary>
401         /// Whether to redraw the image when the visual is scaled down.
402         /// </summary>
403         /// <remarks>
404         /// Inhouse API.
405         /// It is used in the AnimatedVectorImageVisual.The default is true.
406         /// </remarks>
407         [EditorBrowsable(EditorBrowsableState.Never)]
408         public bool RedrawInScalingDown
409         {
410             set
411             {
412                 currentStates.changed = true;
413                 currentStates.redrawInScalingDown = value;
414                 NUILog.Debug($"<[{GetId()}]SET currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
415                 PropertyMap map = new PropertyMap();
416                 map.Add(ImageVisualProperty.RedrawInScalingDown, new PropertyValue(currentStates.redrawInScalingDown));
417                 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
418             }
419             get
420             {
421                 PropertyMap map = base.Image;
422                 var ret = true;
423                 if (map != null)
424                 {
425                     PropertyValue val = map.Find(ImageVisualProperty.RedrawInScalingDown);
426                     if (val != null)
427                     {
428                         if (val.Get(out ret))
429                         {
430                             if (ret != currentStates.redrawInScalingDown)
431                             {
432                                 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different redrawInScalingDown! gotten={ret}, redrawInScalingDown={currentStates.redrawInScalingDown}>");
433                             }
434                             currentStates.redrawInScalingDown = ret;
435                             return currentStates.redrawInScalingDown;
436                         }
437                     }
438                 }
439                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get redrawInScalingDown from dali currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
440                 return currentStates.redrawInScalingDown;
441             }
442         }
443         #endregion Property
444
445
446         #region Method
447         /// <summary>
448         /// Set the minimum and the maximum frame.
449         /// </summary>
450         /// <param name="minFrame">minimum frame</param>
451         /// <param name="maxFrame">maximum frame</param>
452         /// <since_tizen> 7 </since_tizen>
453         public void SetMinMaxFrame(int minFrame, int maxFrame)
454         {
455             NUILog.Debug($"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
456
457             currentStates.changed = true;
458             currentStates.framePlayRangeMin = minFrame;
459             currentStates.framePlayRangeMax = maxFrame;
460
461             PropertyArray array = new PropertyArray();
462             array.PushBack(new PropertyValue(currentStates.framePlayRangeMin));
463             array.PushBack(new PropertyValue(currentStates.framePlayRangeMax));
464
465             PropertyMap map = new PropertyMap();
466             map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
467             DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
468             NUILog.Debug($"  [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
469         }
470
471         /// <summary>
472         /// Play Animation.
473         /// </summary>
474         /// <since_tizen> 7 </since_tizen>
475         public new void Play()
476         {
477             NUILog.Debug($"<[{GetId()}] Play()");
478             debugPrint();
479             base.Play();
480             NUILog.Debug($"[{GetId()}]>");
481         }
482
483         /// <summary>
484         /// Pause Animation.
485         /// </summary>
486         /// <since_tizen> 7 </since_tizen>
487         public new void Pause()
488         {
489             NUILog.Debug($"<[{GetId()}] Pause()>");
490             debugPrint();
491             base.Pause();
492             NUILog.Debug($"[{GetId()}]>");
493         }
494
495         /// <summary>
496         /// Stop Animation.
497         /// </summary>
498         /// <since_tizen> 7 </since_tizen>
499         public new void Stop()
500         {
501             NUILog.Debug($"<[{GetId()}] Stop()");
502             debugPrint();
503             base.Stop();
504             NUILog.Debug($"[{GetId()}]>");
505         }
506
507         /// <summary>
508         /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
509         /// </summary>
510         /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
511         /// <since_tizen> 7 </since_tizen>
512         public List<Tuple<string, int, int>> GetContentInfo()
513         {
514             NUILog.Debug($"<");
515             if (currentStates.contentInfo != null)
516             {
517                 return currentStates.contentInfo;
518             }
519
520             PropertyMap imageMap = base.Image;
521             PropertyMap contentMap = new PropertyMap();
522             if (imageMap != null)
523             {
524                 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
525                 if (val != null)
526                 {
527                     if (val.Get(contentMap))
528                     {
529                         currentStates.contentInfo = new List<Tuple<string, int, int>>();
530                         for (uint i = 0; i < contentMap.Count(); i++)
531                         {
532                             string key = contentMap.GetKeyAt(i).StringKey;
533                             PropertyArray arr = new PropertyArray();
534                             contentMap.GetValue(i).Get(arr);
535                             if (arr != null)
536                             {
537                                 int startFrame, endFrame;
538                                 arr.GetElementAt(0).Get(out startFrame);
539                                 arr.GetElementAt(1).Get(out endFrame);
540
541                                 NUILog.Debug($"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
542
543                                 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
544
545                                 currentStates.contentInfo?.Add(item);
546                             }
547                         }
548                     }
549                 }
550             }
551             NUILog.Debug($">");
552             return currentStates.contentInfo;
553         }
554
555         /// <summary>
556         /// A marker has its start frame and end frame.
557         /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
558         /// 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.   *
559         /// </summary>
560         /// <param name="marker1">First marker</param>
561         /// <param name="marker2">Second marker</param>
562         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
563         [EditorBrowsable(EditorBrowsableState.Never)]
564         public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
565         {
566             NUILog.Debug($"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
567
568             currentStates.changed = true;
569             currentStates.mark1 = marker1;
570             currentStates.mark2 = marker2;
571
572             PropertyArray array = new PropertyArray();
573             array.PushBack(new PropertyValue(currentStates.mark1));
574             if (marker2 != null)
575             {
576                 array.PushBack(new PropertyValue(currentStates.mark2));
577             }
578
579             PropertyMap map = new PropertyMap();
580             map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
581             DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
582             NUILog.Debug($"  [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
583         }
584
585         /// <summary>
586         /// Get MinMax Frame
587         /// </summary>
588         /// <returns>Tuple of Min and Max frames</returns>
589         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
590         [EditorBrowsable(EditorBrowsableState.Never)]
591         public Tuple<int, int> GetMinMaxFrame()
592         {
593             NUILog.Debug($"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
594
595             PropertyMap map = Image;
596             if (map != null)
597             {
598                 PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
599                 if (val != null)
600                 {
601                     PropertyArray array = new PropertyArray();
602                     if (val.Get(array))
603                     {
604                         uint cnt = array.Count();
605                         int item1 = -1, item2 = -1;
606                         for (uint i = 0; i < cnt; i++)
607                         {
608                             PropertyValue v = array.GetElementAt(i);
609                             int intRet;
610                             if (v.Get(out intRet))
611                             {
612                                 NUILog.Debug($"Got play range of string [{i}]: {intRet}");
613                                 if (i == 0)
614                                 {
615                                     item1 = intRet;
616                                 }
617                                 else if (i == 1)
618                                 {
619                                     item2 = intRet;
620                                 }
621                             }
622                             else
623                             {
624                                 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
625                             }
626                         }
627                         NUILog.Debug($"  [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
628                         return new Tuple<int, int>(item1, item2);
629                     }
630                 }
631             }
632             Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
633             return new Tuple<int, int>(-1, -1);
634         }
635         #endregion Method
636
637
638         #region Event, Enum, Struct, ETC
639         /// <summary>
640         /// Animation finished event.
641         /// </summary>
642         /// <since_tizen> 7 </since_tizen>
643         public event EventHandler Finished
644         {
645             add
646             {
647                 if (finishedEventHandler == null)
648                 {
649                     NUILog.Debug($"<[{GetId()}] Finished eventhandler added>");
650                     visualEventSignalCallback = onVisualEventSignal;
651                     VisualEventSignal().Connect(visualEventSignalCallback);
652                 }
653                 finishedEventHandler += value;
654             }
655             remove
656             {
657                 NUILog.Debug($"<[{GetId()}] Finished eventhandler removed>");
658                 finishedEventHandler -= value;
659                 if (finishedEventHandler == null && visualEventSignalCallback != null)
660                 {
661                     VisualEventSignal().Disconnect(visualEventSignalCallback);
662                 }
663             }
664         }
665
666         /// <summary>
667         /// Enumeration for what state the vector animation is in
668         /// </summary>
669         /// <since_tizen> 7 </since_tizen>
670         public enum PlayStateType
671         {
672             /// <summary>
673             /// Invalid
674             /// </summary>
675             /// <since_tizen> 7 </since_tizen>
676             Invalid = -1,
677             /// <summary>
678             /// Vector Animation has stopped
679             /// </summary>
680             /// <since_tizen> 7 </since_tizen>
681             Stopped = 0,
682             /// <summary>
683             /// The vector animation is playing
684             /// </summary>
685             /// <since_tizen> 7 </since_tizen>
686             Playing = 1,
687             /// <summary>
688             /// The vector animation is paused
689             /// </summary>
690             /// <since_tizen> 7 </since_tizen>
691             Paused = 2
692         }
693
694         /// <summary>
695         /// Enumeration for what to do when the animation is stopped.
696         /// </summary>
697         /// <since_tizen> 7 </since_tizen>
698         public enum StopBehaviorType
699         {
700             /// <summary>
701             /// When the animation is stopped, the current frame is shown.
702             /// </summary>
703             /// <since_tizen> 7 </since_tizen>
704             CurrentFrame,
705             /// <summary>
706             /// When the animation is stopped, the min frame (first frame) is shown.
707             /// </summary>
708             /// <since_tizen> 7 </since_tizen>
709             MinimumFrame,
710             /// <summary>
711             /// When the animation is stopped, the max frame (last frame) is shown.
712             /// </summary>
713             /// <since_tizen> 7 </since_tizen>
714             MaximumFrame
715         }
716
717         /// <summary>
718         /// Enumeration for what looping mode is in.
719         /// </summary>
720         /// <since_tizen> 7 </since_tizen>
721         public enum LoopingModeType
722         {
723             /// <summary>
724             /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
725             /// </summary>
726             /// <since_tizen> 7 </since_tizen>
727             Restart,
728             /// <summary>
729             /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
730             /// </summary>
731             /// <since_tizen> 7 </since_tizen>
732             AutoReverse
733         }
734         #endregion Event, Enum, Struct, ETC
735
736
737         #region Internal
738         internal class VisualEventSignalArgs : EventArgs
739         {
740             public int VisualIndex
741             {
742                 set;
743                 get;
744             }
745             public int SignalId
746             {
747                 set;
748                 get;
749             }
750         }
751
752         internal event EventHandler<VisualEventSignalArgs> VisualEvent
753         {
754             add
755             {
756                 if (visualEventSignalHandler == null)
757                 {
758                     visualEventSignalCallback = onVisualEventSignal;
759                     VisualEventSignal().Connect(visualEventSignalCallback);
760                 }
761                 visualEventSignalHandler += value;
762             }
763             remove
764             {
765                 visualEventSignalHandler -= value;
766                 if (visualEventSignalHandler == null && VisualEventSignal().Empty() == false)
767                 {
768                     VisualEventSignal().Disconnect(visualEventSignalCallback);
769                 }
770             }
771         }
772
773         internal void EmitVisualEventSignal(int visualIndex, int signalId)
774         {
775             VisualEventSignal().Emit(this, visualIndex, signalId);
776         }
777
778         internal VisualEventSignal VisualEventSignal()
779         {
780             VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
781             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
782             return ret;
783         }
784         #endregion Internal
785
786
787         #region Private
788         private struct states
789         {
790             internal string url;
791             internal int frame;
792             internal int loopCount;
793             internal LoopingModeType loopMode;
794             internal StopBehaviorType stopEndAction;
795             internal int framePlayRangeMin;
796             internal int framePlayRangeMax;
797             internal bool changed;
798             internal int totalFrame;
799             internal float scale;
800             internal PlayStateType playState;
801             internal List<Tuple<string, int, int>> contentInfo;
802             internal string mark1, mark2;
803             internal bool redrawInScalingDown;
804         };
805         private states currentStates;
806
807         private enum actionType
808         {
809             play,
810             pause,
811             stop,
812             jumpTo,
813             updateProperty,
814         };
815
816         private struct DevelVisual
817         {
818             internal enum Type
819             {
820                 AnimatedGradient = Visual.Type.AnimatedImage + 1,
821                 AnimatedVectorImage = Visual.Type.AnimatedImage + 2,
822             }
823         }
824
825         private const string tag = "NUITEST";
826         private event EventHandler finishedEventHandler;
827
828         private void OnFinished()
829         {
830             NUILog.Debug($"<[{GetId()}] OnFinished()>");
831             finishedEventHandler?.Invoke(this, null);
832         }
833
834         private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
835         {
836             OnFinished();
837
838             if (targetView != IntPtr.Zero)
839             {
840                 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
841                 if (v != null)
842                 {
843                     NUILog.Debug($"targetView is not null! name={v.Name}");
844                 }
845                 else
846                 {
847                     NUILog.Debug($"target is something created from dali");
848                 }
849             }
850             VisualEventSignalArgs e = new VisualEventSignalArgs();
851             e.VisualIndex = visualIndex;
852             e.SignalId = signalId;
853             visualEventSignalHandler?.Invoke(this, e);
854
855             NUILog.Debug($"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
856         }
857
858         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
859         private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
860
861         private VisualEventSignalCallbackType visualEventSignalCallback;
862         private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
863
864         private void debugPrint()
865         {
866             NUILog.Debug($"===================================");
867             NUILog.Debug($"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, \nframePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
868             NUILog.Debug($"  get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState}");
869             NUILog.Debug($"  RedrawInScalingDown={RedrawInScalingDown} >");
870             NUILog.Debug($"===================================");
871         }
872         #endregion Private
873     }
874
875     /// <summary>
876     /// A class containing frame informations for a LottieAnimationView.
877     /// </summary>
878     [EditorBrowsable(EditorBrowsableState.Never)]
879     public class LottieFrameInfo : ICloneable
880     {
881         /// <summary>
882         /// Creates a new instance with a playing range.
883         /// </summary>
884         [EditorBrowsable(EditorBrowsableState.Never)]
885         public LottieFrameInfo(int startFrame, int endFrame)
886         {
887             StartFrame = startFrame;
888             EndFrame = endFrame;
889         }
890
891         /// <summary>
892         /// Creates a new instance with a still image frame.
893         /// </summary>
894         [EditorBrowsable(EditorBrowsableState.Never)]
895         public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
896         {
897         }
898
899         /// <summary>
900         /// Create a new instance from a pair notation.
901         /// </summary>
902         [EditorBrowsable(EditorBrowsableState.Never)]
903         public static implicit operator LottieFrameInfo((int, int) pair)
904         {
905             return new LottieFrameInfo(pair.Item1, pair.Item2);
906         }
907
908         /// <summary>
909         /// Create a new instance from an int value.
910         /// </summary>
911         [EditorBrowsable(EditorBrowsableState.Never)]
912         public static implicit operator LottieFrameInfo(int stillImageFrame)
913         {
914             return new LottieFrameInfo(stillImageFrame);
915         }
916
917         /// <summary>
918         /// Create a new instance from string.
919         /// Possible input : "0, 10", "10"
920         /// </summary>
921         [EditorBrowsable(EditorBrowsableState.Never)]
922         public static implicit operator LottieFrameInfo(string pair)
923         {
924             if (pair == null)
925             {
926                 return null;
927             }
928
929             string[] parts = pair.Split(',');
930             if (parts.Length == 1)
931             {
932                 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture));
933             }
934             else if (parts.Length == 2)
935             {
936                 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), Int32.Parse(parts[1].Trim(), CultureInfo.InvariantCulture));
937             }
938
939             Tizen.Log.Error("NUI", $"Can not convert string {pair} to LottieFrameInfo");
940             return null;
941         }
942
943         /// <summary>
944         /// The start frame of the lottie animation.
945         /// </summary>
946         [EditorBrowsable(EditorBrowsableState.Never)]
947         public int StartFrame { get; }
948
949         /// <summary>
950         /// The end frame of the lottie animation.
951         /// </summary>
952         [EditorBrowsable(EditorBrowsableState.Never)]
953         public int EndFrame { get; }
954
955         /// <summary>
956         /// Create LottieFrameInfo struct with animation range information
957         /// </summary>
958         [EditorBrowsable(EditorBrowsableState.Never)]
959         public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
960         {
961             return new LottieFrameInfo(startFrame, endFrame);
962         }
963
964         /// <summary>
965         /// Create LottieFrameInfo struct with still image information
966         /// </summary>
967         [EditorBrowsable(EditorBrowsableState.Never)]
968         public static LottieFrameInfo CreateStillImage(int stillImageFrame)
969         {
970             return new LottieFrameInfo(stillImageFrame, stillImageFrame);
971         }
972
973         /// <summary>
974         /// Inhouse API.
975         /// Whether this LottieFrameInfo represents one frame or more.
976         /// </summary>
977         [EditorBrowsable(EditorBrowsableState.Never)]
978         public bool IsStillImage()
979         {
980             return StartFrame == EndFrame;
981         }
982
983         /// <summary>
984         /// Inhouse API.
985         /// Play specified LottieAnimationView with this frame information.
986         /// </summary>
987         /// <param name="lottieView">The target LottieAnimationView to play.</param>
988         /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
989         [EditorBrowsable(EditorBrowsableState.Never)]
990         public void Show(LottieAnimationView lottieView, bool noPlay = false)
991         {
992             if (!BeReadyToShow(lottieView))
993             {
994                 return;
995             }
996
997             lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
998
999             if (IsStillImage() || noPlay)
1000             {
1001                 lottieView.CurrentFrame = EndFrame;
1002             }
1003             else
1004             {
1005                 lottieView.CurrentFrame = StartFrame;
1006                 lottieView.Play();
1007             }
1008         }
1009
1010         /// <inheritdoc/>
1011         [EditorBrowsable(EditorBrowsableState.Never)]
1012         public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
1013
1014         private bool BeReadyToShow(LottieAnimationView lottieView)
1015         {
1016             // Validate input lottieView
1017             if (null == lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
1018             {
1019                 return false;
1020             }
1021
1022             // Stop if it was playing
1023             if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)
1024             {
1025                 lottieView.Stop();
1026             }
1027
1028             return true;
1029         }
1030     }
1031 }