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