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