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