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