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