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